// Package middleware defines OTel tracing infrastructure and gin middlewares. package middleware import ( "context" "fmt" "eventRT/config" "eventRT/constants" "github.com/gin-gonic/gin" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/propagation" sdkresource "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" oteltrace "go.opentelemetry.io/otel/trace" ) // InitTracerProvider creates an OTLP TracerProvider and registers it as the global provider. // It registers the W3C TraceContext propagator (traceparent header). // The caller is responsible for calling Shutdown on the returned provider during graceful shutdown. func InitTracerProvider(ctx context.Context, cfg config.EventRTConfig) (*sdktrace.TracerProvider, error) { opts := []otlptracehttp.Option{ otlptracehttp.WithEndpoint(cfg.OtelConfig.Endpoint), } if cfg.OtelConfig.Insecure { opts = append(opts, otlptracehttp.WithInsecure()) } exporter, err := otlptracehttp.New(ctx, opts...) if err != nil { return nil, fmt.Errorf("create OTLP exporter: %w", err) } res := sdkresource.NewSchemaless( attribute.String("service.name", cfg.ServiceName), attribute.String("deployment.environment", cfg.DeployEnv), ) tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(res), sdktrace.WithSampler(sdktrace.AlwaysSample()), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil } // StartTrace extracts upstream W3C trace context from request headers and starts a server span. func StartTrace() gin.HandlerFunc { tracer := otel.Tracer("eventRT/http") return func(c *gin.Context) { ctx := otel.GetTextMapPropagator().Extract( c.Request.Context(), propagation.HeaderCarrier(c.Request.Header), ) spanName := c.FullPath() if spanName == "" { spanName = c.Request.URL.Path } ctx, span := tracer.Start(ctx, spanName, oteltrace.WithSpanKind(oteltrace.SpanKindServer), ) defer span.End() spanCtx := span.SpanContext() ctx = context.WithValue(ctx, constants.CtxKeyTraceID, spanCtx.TraceID().String()) ctx = context.WithValue(ctx, constants.CtxKeySpanID, spanCtx.SpanID().String()) c.Request = c.Request.WithContext(ctx) c.Set(constants.HeaderTraceID, spanCtx.TraceID().String()) c.Set(constants.HeaderSpanID, spanCtx.SpanID().String()) c.Next() } }