diff --git a/docker-compose.yml b/docker-compose.yml index f2ef7afbe..b06e45d8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,12 +44,9 @@ services: VELA_ENABLE_SECURE_COOKIE: 'false' VELA_REPO_ALLOWLIST: '*' VELA_SCHEDULE_ALLOWLIST: '*' - VELA_OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 - VELA_OTEL_TRACING_ENDPOINT: jaeger:4318 VELA_OTEL_TRACING_ENABLE: true - VELA_OTEL_TRACING_SERVICE_NAME: vela-server - VELA_OTEL_TRACING_RESOURCE_ATTRIBUTES: "process.runtime.name=go" - VELA_OTEL_TRACING_RESOURCE_ENV_ATTRIBUTES: "deployment.environment=CLOUD_ENVIRONMENT" + OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 + VELA_OTEL_TRACING_ENDPOINT: jaeger:4318 VELA_OTEL_TRACING_SAMPLER_RATELIMIT_PER_SECOND: 0.2 env_file: - .env diff --git a/tracing/config.go b/tracing/config.go index aa48f3490..acdca2173 100644 --- a/tracing/config.go +++ b/tracing/config.go @@ -3,7 +3,7 @@ package tracing import ( - "fmt" + "maps" "os" "strings" @@ -19,70 +19,58 @@ type Client struct { // Config represents the configurations for otel tracing. type Config struct { - EnableTracing bool - ServiceName string - ExporterURL string - CertPath string - TLSMinVersion string - ResourceAttributes map[string]string + EnableTracing bool + ServiceName string + ExporterURL string + CertPath string + TLSMinVersion string + ResourceAttributes map[string]string + TraceStateAttributes map[string]string + SpanAttributes map[string]string Sampler } // Sampler represents the configurations for the otel sampler. // Used to determine if a trace should be sampled. type Sampler struct { - TraceStateAttributes map[string]string - SpanAttributes map[string]string - Ratio float64 - PerSecond float64 + PerSecond float64 } // FromCLIContext takes cli context and returns a tracing config to supply to traceable services. func FromCLIContext(c *cli.Context) (*Client, error) { cfg := Config{ - EnableTracing: c.Bool("tracing.enable"), - ServiceName: c.String("tracing.service.name"), - ExporterURL: c.String("tracing.exporter.endpoint"), - CertPath: c.String("tracing.exporter.cert_path"), - TLSMinVersion: c.String("tracing.tls-min-version"), - ResourceAttributes: map[string]string{}, + EnableTracing: c.Bool("tracing.enable"), + ServiceName: c.String("tracing.service.name"), + ExporterURL: c.String("tracing.exporter.endpoint"), + CertPath: c.String("tracing.exporter.cert_path"), + TLSMinVersion: c.String("tracing.tls-min-version"), + ResourceAttributes: map[string]string{}, + TraceStateAttributes: map[string]string{}, + SpanAttributes: map[string]string{}, Sampler: Sampler{ - TraceStateAttributes: map[string]string{ - "sampler": c.String("tracing.sampler.tracestate"), - }, - SpanAttributes: map[string]string{ - "w3c.tracestate": fmt.Sprintf("sampler=%s", c.String("tracing.sampler.tracestate")), - "sampler.parent": c.String("tracing.sampler.parent"), - "sampler.type": c.String("tracing.sampler.type"), - }, - Ratio: c.Float64("tracing.sampler.ratio"), PerSecond: c.Float64("tracing.sampler.persecond"), }, } - // add resource attributes - for _, attr := range c.StringSlice("tracing.resource.attributes") { - kv := strings.Split(attr, "=") - if len(kv) != 2 { - continue - } - - cfg.ResourceAttributes[kv[0]] = kv[1] + // identity func used to map a string back to itself + identityFn := func(s string) string { + return s } - // add resource attributes from environment - for _, attr := range c.StringSlice("tracing.resource.env_attributes") { - kv := strings.Split(attr, "=") - if len(kv) != 2 { - continue - } + // static span attributes + cfg.SpanAttributes = keyValueSliceToMap(c.StringSlice("tracing.span.attributes"), identityFn) - v, found := os.LookupEnv(kv[1]) - if found { - cfg.ResourceAttributes[kv[0]] = v - } - } + // static tracestate attributes + cfg.TraceStateAttributes = keyValueSliceToMap(c.StringSlice("tracing.tracestate.attributes"), identityFn) + + // static resource attributes + cfg.ResourceAttributes = keyValueSliceToMap(c.StringSlice("tracing.resource.attributes"), identityFn) + + // merge static resource attributes with those fetched from the environment + m := keyValueSliceToMap(c.StringSlice("tracing.resource.env_attributes"), os.Getenv) + maps.Copy(cfg.ResourceAttributes, m) + // initialize the tracer provider and assign it to the client tracer, err := initTracer(c.Context, cfg) if err != nil { return nil, err @@ -93,3 +81,25 @@ func FromCLIContext(c *cli.Context) (*Client, error) { TracerProvider: tracer, }, nil } + +// keyValueSliceToMap converts a slice of key=value strings to a map of key to value using the supplied map function. +func keyValueSliceToMap(kv []string, fn func(string) string) map[string]string { + m := map[string]string{} + for _, attr := range kv { + parts := strings.SplitN(attr, "=", 2) + + if len(parts) != 2 || len(parts[1]) == 0 { + continue + } + + v := fn(parts[1]) + + if len(v) == 0 { + continue + } + + m[parts[0]] = v + } + + return m +} diff --git a/tracing/flags.go b/tracing/flags.go index bf3fb92de..95f39643a 100644 --- a/tracing/flags.go +++ b/tracing/flags.go @@ -24,6 +24,11 @@ var Flags = []cli.Flag{ Usage: "set otel tracing service name", Value: "vela-server", }, + &cli.StringFlag{ + EnvVars: []string{"VELA_OTEL_TRACING_ENDPOINT"}, + Name: "tracing.exporter.endpoint", + Usage: "set the otel exporter endpoint", + }, &cli.StringFlag{ EnvVars: []string{"VELA_OTEL_TRACING_EXPORTER_SSL_CERT_PATH"}, Name: "tracing.exporter.cert_path", @@ -35,19 +40,13 @@ var Flags = []cli.Flag{ Usage: "optional TLS minimum version requirement to set when communicating with the otel exporter", Value: "1.2", }, - &cli.StringFlag{ - EnvVars: []string{"VELA_OTEL_TRACING_ENDPOINT"}, - Name: "tracing.exporter.endpoint", - Usage: "set the otel exporter endpoint", - Value: "127.0.0.1:4318", - }, // Resource Flags &cli.StringSliceFlag{ EnvVars: []string{"VELA_OTEL_TRACING_RESOURCE_ATTRIBUTES"}, Name: "tracing.resource.attributes", - Usage: "set otel resource attributes as a list of key=value pairs. each one will be attached to each span as an attribute", + Usage: "set otel resource attributes as a list of key=value pairs. each one will be attached to each span as a resource attribute", Value: cli.NewStringSlice("process.runtime.name=go"), }, &cli.StringSliceFlag{ @@ -55,27 +54,19 @@ var Flags = []cli.Flag{ Name: "tracing.resource.env_attributes", Usage: "set otel resource attributes as a list of key=env_variable_key pairs. each one will be attached to each span as an attribute where the value is retrieved from the environment using the pair value", }, + &cli.StringSliceFlag{ + EnvVars: []string{"VELA_OTEL_TRACING_SPAN_ATTRIBUTES"}, + Name: "tracing.span.attributes", + Usage: "set otel span attributes as a list of key=value pairs. each one will be attached to each span as a sampler attribute", + }, + &cli.StringSliceFlag{ + EnvVars: []string{"VELA_OTEL_TRACING_TRACESTATE_ATTRIBUTES"}, + Name: "tracing.tracestate.attributes", + Usage: "set otel tracestate attributes as a list of key=value pairs. each one will be inserted into the tracestate for each sampled span", + }, // Sampler Flags - &cli.StringFlag{ - EnvVars: []string{"VELA_OTEL_TRACING_SAMPLER_TRACESTATE"}, - Name: "tracing.sampler.tracestate", - Usage: "set otel sampler trace state attached to each span as an attribute.", - Value: "sampler.tracestate", - }, - &cli.StringFlag{ - EnvVars: []string{"VELA_OTEL_TRACING_SAMPLER_PARENT"}, - Name: "tracing.sampler.parent", - Usage: "set otel sampler parent attribute attached to each span.", - Value: "sampler.parent", - }, - &cli.StringFlag{ - EnvVars: []string{"VELA_OTEL_TRACING_SAMPLER_TYPE"}, - Name: "tracing.sampler.type", - Usage: "set otel sampler type attribute attached to each span.", - Value: "sampler.type", - }, &cli.Float64Flag{ EnvVars: []string{"VELA_OTEL_TRACING_SAMPLER_RATELIMIT_PER_SECOND"}, Name: "tracing.sampler.persecond",