| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- /*
- Copyright 2020 Docker Compose CLI authors
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package tracing
- import (
- "context"
- "errors"
- "fmt"
- "os"
- "strconv"
- "strings"
- "github.com/docker/compose/v2/internal"
- "go.opentelemetry.io/otel/attribute"
- "github.com/docker/cli/cli/command"
- "github.com/moby/buildkit/util/tracing/detect"
- _ "github.com/moby/buildkit/util/tracing/detect/delegated" //nolint:blank-imports
- _ "github.com/moby/buildkit/util/tracing/env" //nolint:blank-imports
- "go.opentelemetry.io/otel"
- "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.18.0"
- )
- func init() {
- detect.ServiceName = "compose"
- // do not log tracing errors to stdio
- otel.SetErrorHandler(skipErrors{})
- }
- var Tracer = otel.Tracer("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{})
- if v, _ := strconv.ParseBool(os.Getenv("COMPOSE_EXPERIMENTAL_OTEL")); !v {
- return nil, nil
- }
- 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: %v", err)
- }
- muxExporter := MuxExporter{exporters: exporters}
- sp := sdktrace.NewSimpleSpanProcessor(muxExporter)
- tracerProvider := sdktrace.NewTracerProvider(
- sdktrace.WithSampler(sdktrace.AlwaysSample()),
- sdktrace.WithResource(res),
- sdktrace.WithSpanProcessor(sp),
- )
- 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
- }
|