Repository navigation
refactor(tracing): use cli provided tracer #14382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,126 +18,24 @@ package tracing | |||||
|
|
||||||
| import ( | ||||||
| "context" | ||||||
| "errors" | ||||||
| "fmt" | ||||||
| "os" | ||||||
| "strings" | ||||||
|
|
||||||
| "github.com/docker/cli/cli/command" | ||||||
| _ "github.com/moby/buildkit/util/tracing/childprocess" //nolint:blank-imports // initializes BuildKit tracing child process propagation. | ||||||
| "github.com/moby/buildkit/util/tracing/detect" | ||||||
| "go.opentelemetry.io/otel" | ||||||
| "go.opentelemetry.io/otel/attribute" | ||||||
| "go.opentelemetry.io/otel/exporters/otlp/otlptrace" | ||||||
| "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" | ||||||
| "go.opentelemetry.io/otel/propagation" | ||||||
| "go.opentelemetry.io/otel/sdk/resource" | ||||||
| sdktrace "go.opentelemetry.io/otel/sdk/trace" | ||||||
| semconv "go.opentelemetry.io/otel/semconv/v1.21.0" | ||||||
|
|
||||||
| "github.com/docker/compose/v5/internal" | ||||||
| ) | ||||||
|
|
||||||
| func init() { | ||||||
| detect.ServiceName = "compose" | ||||||
| } | ||||||
|
|
||||||
| // OTLPConfig contains the necessary values to initialize an OTLP client | ||||||
| // manually. | ||||||
| // | ||||||
| // This supports a minimal set of options based on what is necessary for | ||||||
| // automatic OTEL configuration from Docker context metadata. | ||||||
| type OTLPConfig struct { | ||||||
| Endpoint string | ||||||
| } | ||||||
|
|
||||||
| // ShutdownFunc flushes and stops an OTEL exporter. | ||||||
| type ShutdownFunc func(ctx context.Context) error | ||||||
|
|
||||||
| // envMap is a convenience type for OS environment variables. | ||||||
| type envMap map[string]string | ||||||
|
|
||||||
| func InitTracing(dockerCli command.Cli) (ShutdownFunc, error) { | ||||||
| // set global propagator to tracecontext (the default is no-op). | ||||||
| otel.SetTextMapPropagator(propagation.TraceContext{}) | ||||||
| return InitProvider(dockerCli) | ||||||
| } | ||||||
|
|
||||||
| func InitProvider(dockerCli command.Cli) (ShutdownFunc, error) { | ||||||
| ctx := context.Background() | ||||||
|
|
||||||
| var errs []error | ||||||
| var exporters []sdktrace.SpanExporter | ||||||
|
|
||||||
| envClient, otelEnv := traceClientFromEnv() | ||||||
| if envClient != nil { | ||||||
| if envExporter, err := otlptrace.New(ctx, envClient); err != nil { | ||||||
| errs = append(errs, err) | ||||||
| } else if envExporter != nil { | ||||||
| exporters = append(exporters, envExporter) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if dcClient, err := traceClientFromDockerContext(dockerCli, otelEnv); err != nil { | ||||||
| errs = append(errs, err) | ||||||
| } else if dcClient != nil { | ||||||
| if dcExporter, err := otlptrace.New(ctx, dcClient); err != nil { | ||||||
| errs = append(errs, err) | ||||||
| } else if dcExporter != nil { | ||||||
| exporters = append(exporters, dcExporter) | ||||||
| } | ||||||
| } | ||||||
| if len(errs) != 0 { | ||||||
| return nil, errors.Join(errs...) | ||||||
| } | ||||||
|
|
||||||
| res, err := resource.New( | ||||||
| ctx, | ||||||
| resource.WithAttributes( | ||||||
| semconv.ServiceName("compose"), | ||||||
| semconv.ServiceVersion(internal.Version), | ||||||
| attribute.String("docker.context", dockerCli.CurrentContext()), | ||||||
| ), | ||||||
| ) | ||||||
| if err != nil { | ||||||
| return nil, fmt.Errorf("failed to create resource: %w", err) | ||||||
| } | ||||||
|
|
||||||
| muxExporter := MuxExporter{exporters: exporters} | ||||||
| tracerProvider := sdktrace.NewTracerProvider( | ||||||
| sdktrace.WithResource(res), | ||||||
| sdktrace.WithBatcher(muxExporter), | ||||||
| ) | ||||||
| otel.SetTracerProvider(tracerProvider) | ||||||
|
|
||||||
| // Shutdown will flush any remaining spans and shut down the exporter. | ||||||
| return tracerProvider.Shutdown, nil | ||||||
| } | ||||||
|
|
||||||
| // traceClientFromEnv creates a GRPC OTLP client based on OS environment | ||||||
| // variables. | ||||||
| // | ||||||
| // https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/ | ||||||
| func traceClientFromEnv() (otlptrace.Client, envMap) { | ||||||
| hasOtelEndpointInEnv := false | ||||||
| otelEnv := make(map[string]string) | ||||||
| for _, kv := range os.Environ() { | ||||||
| k, v, ok := strings.Cut(kv, "=") | ||||||
| if !ok { | ||||||
| continue | ||||||
| } | ||||||
| if strings.HasPrefix(k, "OTEL_") { | ||||||
| otelEnv[k] = v | ||||||
| if strings.HasSuffix(k, "ENDPOINT") { | ||||||
| hasOtelEndpointInEnv = true | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if !hasOtelEndpointInEnv { | ||||||
| return nil, nil | ||||||
| } | ||||||
|
|
||||||
| client := otlptracegrpc.NewClient() | ||||||
| return client, otelEnv | ||||||
| return func(ctx context.Context) error { | ||||||
| return nil | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] The old implementation returned func InitProvider(dockerCli command.Cli) (ShutdownFunc, error) {
return func(ctx context.Context) error {
return nil
}, nil
}
The net effect is a regression in observability: any spans buffered in the Consider either: (a) keeping a local
|
||||||
| }, nil | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[high/security]
replacedirective pinsdocker/clito an unmerged personal forkThe added line substitutes the official
github.com/docker/climodule with the PR author's personal fork (github.com/jsternberg/cli) on a pseudo-version commit that is outside Docker's control:While the full 12-hex commit suffix pins exact bytes (integrity), the fork lives on a personal GitHub account — it can be deleted, the account can be compromised, or the pseudo-version can be yanked — any of which breaks builds or introduces malicious code for every downstream consumer of
docker/compose.The PR description acknowledges this as a temporary workaround pending docker/cli#7433. This
replacedirective must not be merged intomain; it should be removed once the upstream fix is published as a proper release from the officialdocker/clirepository.