package observability import ( "context" "log/slog" "net/url" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "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(W6 裁决:资源属性补全)。 // // 资源属性包含:service.name / service.version / deployment.environment / host.name。 // endpoint 为 "http://host:port" 格式(如 "http://localhost:4318"); // 为空时跳过初始化(tracing disabled)。 // // 返回 shutdown 函数,应在服务退出时调用以 flush 待发送 span。 func InitTracer(serviceName, endpoint, env, version, hostName string) func() { if endpoint == "" { slog.Info("OTEL endpoint not set, tracing disabled") return func() {} } u, err := url.Parse(endpoint) if err != nil || u.Host == "" { slog.Warn("invalid OTEL endpoint, tracing disabled", "endpoint", 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 { slog.Error("failed to create OTLP exporter, tracing disabled", "error", err) return func() {} } // W6 裁决:资源属性补全(service.name + version + env + host) res, err := resource.New(ctx, resource.WithAttributes( semconv.ServiceName(serviceName), attribute.String("service.version", version), attribute.String("deployment.environment", env), attribute.String("host.name", hostName), ), ) if err != nil { slog.Error("failed to create resource", "error", err) return func() {} } tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(res), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, )) slog.Info("OpenTelemetry tracer initialized", "service", serviceName, "endpoint", u.Host, "env", env, "version", version, "host", hostName, ) return func() { shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := tp.Shutdown(shutdownCtx); err != nil { slog.Error("failed to shutdown tracer", "error", err) } } }