package observability import ( "context" "log" "net/url" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "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.26.0" ) // InitTracer 初始化 OpenTelemetry tracer. // // endpoint 为 "http://host:port" 格式(如 "http://localhost:4318"); // 为空时跳过初始化(tracing disabled)。 // // 返回 shutdown 函数,应在服务退出时调用以 flush 待发送 span. func InitTracer(serviceName, endpoint string) func() { if endpoint == "" { log.Println("OTEL endpoint not set, tracing disabled") return func() {} } u, err := url.Parse(endpoint) if err != nil || u.Host == "" { log.Printf("invalid OTEL endpoint %q, tracing disabled", endpoint) return func() {} } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() exporter, err := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint(u.Host), otlptracehttp.WithInsecure(), ) if err != nil { log.Printf("failed to create OTLP exporter: %v, tracing disabled", err) return func() {} } res, err := resource.New(ctx, resource.WithAttributes(semconv.ServiceName(serviceName)), ) if err != nil { log.Printf("failed to create resource: %v", err) return func() {} } tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(res), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, )) log.Printf("OpenTelemetry tracer initialized for %s (endpoint=%s)", serviceName, u.Host) return func() { shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := tp.Shutdown(shutdownCtx); err != nil { log.Printf("failed to shutdown tracer: %v", err) } } }