Files
grafana/pkg/infra/tracing/tracing_config.go
Marcus Efraimsson 6c1de260a2 API Server: Standalone observability (#84789)
Adds support for logs (specify level), metrics (enable metrics and Prometheus /metrics endpoint 
and traces (jaeger or otlp) for standalone API server. This will allow any grafana core service 
part of standalone apiserver to use logging, metrics and traces as normal.
2024-03-21 17:06:32 +01:00

145 lines
3.8 KiB
Go

package tracing
import (
"fmt"
"os"
"strings"
"github.com/grafana/grafana/pkg/setting"
"go.opentelemetry.io/otel/attribute"
)
type TracingConfig struct {
enabled string
Address string
Propagation string
CustomAttribs []attribute.KeyValue
Sampler string
SamplerParam float64
SamplerRemoteURL string
ServiceName string
ServiceVersion string
}
func ProvideTracingConfig(cfg *setting.Cfg) (*TracingConfig, error) {
return ParseTracingConfig(cfg)
}
func NewEmptyTracingConfig() *TracingConfig {
return &TracingConfig{
CustomAttribs: []attribute.KeyValue{},
}
}
func NewJaegerTracingConfig(address string, propagation string) (*TracingConfig, error) {
if address == "" {
return nil, fmt.Errorf("address cannot be empty")
}
cfg := NewEmptyTracingConfig()
cfg.enabled = jaegerExporter
cfg.Address = address
cfg.Propagation = propagation
return cfg, nil
}
func NewOTLPTracingConfig(address string, propagation string) (*TracingConfig, error) {
if address == "" {
return nil, fmt.Errorf("address cannot be empty")
}
cfg := NewEmptyTracingConfig()
cfg.enabled = otlpExporter
cfg.Address = address
cfg.Propagation = propagation
return cfg, nil
}
func ParseTracingConfig(cfg *setting.Cfg) (*TracingConfig, error) {
if cfg == nil {
return nil, fmt.Errorf("cfg cannot be nil")
}
tc := NewEmptyTracingConfig()
tc.ServiceName = "grafana"
tc.ServiceVersion = cfg.BuildVersion
legacyAddress, legacyTags := "", ""
if section, err := cfg.Raw.GetSection("tracing.jaeger"); err == nil {
legacyAddress = section.Key("address").MustString("")
if legacyAddress == "" {
host, port := os.Getenv(envJaegerAgentHost), os.Getenv(envJaegerAgentPort)
if host != "" || port != "" {
legacyAddress = fmt.Sprintf("%s:%s", host, port)
}
}
legacyTags = section.Key("always_included_tag").MustString("")
tc.Sampler = section.Key("sampler_type").MustString("")
tc.SamplerParam = section.Key("sampler_param").MustFloat64(1)
tc.SamplerRemoteURL = section.Key("sampling_server_url").MustString("")
}
section := cfg.Raw.Section("tracing.opentelemetry")
var err error
// we default to legacy tag set (attributes) if the new config format is absent
tc.CustomAttribs, err = splitCustomAttribs(section.Key("custom_attributes").MustString(legacyTags))
if err != nil {
return nil, err
}
// if sampler_type is set in tracing.opentelemetry, we ignore the config in tracing.jaeger
sampler := section.Key("sampler_type").MustString("")
if sampler != "" {
tc.Sampler = sampler
}
samplerParam := section.Key("sampler_param").MustFloat64(0)
if samplerParam != 0 {
tc.SamplerParam = samplerParam
}
samplerRemoteURL := section.Key("sampling_server_url").MustString("")
if samplerRemoteURL != "" {
tc.SamplerRemoteURL = samplerRemoteURL
}
section = cfg.Raw.Section("tracing.opentelemetry.jaeger")
tc.enabled = noopExporter
// we default to legacy Jaeger agent address if the new config value is empty
tc.Address = section.Key("address").MustString(legacyAddress)
tc.Propagation = section.Key("propagation").MustString("")
if tc.Address != "" {
tc.enabled = jaegerExporter
return tc, nil
}
section = cfg.Raw.Section("tracing.opentelemetry.otlp")
tc.Address = section.Key("address").MustString("")
if tc.Address != "" {
tc.enabled = otlpExporter
}
tc.Propagation = section.Key("propagation").MustString("")
return tc, nil
}
func (tc TracingConfig) OTelExporterEnabled() bool {
return tc.enabled == otlpExporter
}
func splitCustomAttribs(s string) ([]attribute.KeyValue, error) {
res := []attribute.KeyValue{}
attribs := strings.Split(s, ",")
for _, v := range attribs {
parts := strings.SplitN(v, ":", 2)
if len(parts) > 1 {
res = append(res, attribute.String(parts[0], parts[1]))
} else if v != "" {
return nil, fmt.Errorf("custom attribute malformed - must be in 'key:value' form: %q", v)
}
}
return res, nil
}