// Package trace setup otlp trace and unified the traceID, requestID header, rid log package trace import ( "context" "fmt" "io" "log/slog" "github.com/samber/lo" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "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.37.0" "git.ifooth.com/common/pkg/version" ) // Option trace setup opt type Option struct { Enabled bool `json:"enabled"` Endpoint string `json:"endpoint"` Token string `json:"token"` } // SetupTrace setup trace by opt func SetupTrace(ctx context.Context, opt *Option) error { var ( exporter sdktrace.SpanExporter err error ) if opt.Enabled && opt.Endpoint != "" { slog.InfoContext(ctx, "trace enabled", "exporter", "otlptracehttp", "endpoint", opt.Endpoint) // 配置http上报地址 option := []otlptracehttp.Option{ otlptracehttp.WithEndpoint(opt.Endpoint), otlptracehttp.WithInsecure(), } exporter, err = otlptracehttp.New(ctx, option...) } else { // 日志打印rid/span等 exporter, err = stdouttrace.New(stdouttrace.WithWriter(io.Discard)) } if err != nil { return err } tp, err := newTraceProvider(exporter) if err != nil { return err } // Set as global trace provider otel.SetTracerProvider(tp) // 支持传递traceparent/X-Request-Id // W3C Trace Context traceparent = --- propagator := propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, // 支持从外部设置 traceparent requestIDContext{}, // 支持外部设置 X-Request-Id propagation.Baggage{}, ) otel.SetTextMapPropagator(propagator) return nil } // Shutdown graceful shutdown trace exporter func Shutdown(ctx context.Context) error { tp, ok := otel.GetTracerProvider().(*sdktrace.TracerProvider) if !ok || tp == nil { return nil } return tp.Shutdown(ctx) } // OTEL tracer provider setup func newTraceProvider(exp sdktrace.SpanExporter) (*sdktrace.TracerProvider, error) { r, err := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceName("trace"), semconv.ServiceVersion(version.Version), ), ) if err != nil { return nil, fmt.Errorf("merge resource: %w", err) } provider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exp), sdktrace.WithResource(r), ) return provider, nil } func init() { // 默认只做context传递, 不导出到其他输出 lo.Must0(SetupTrace(context.Background(), new(Option))) }