package tracing import ( "net/http" "github.com/go-logr/logr" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel/propagation" ) // HTTPMiddleware returns middleware that opens a server span per request // (named from the mux route pattern once routing has happened) and hands // handlers a request context whose logf.FromContext logger is base enriched // with the trace context. Incoming W3C traceparent headers are honored, so // clients see their own trace continue into the operator. /healthz is never // traced. With tracing disabled the middleware degrades to injecting base // unenriched — handlers can rely on logf.FromContext either way. func HTTPMiddleware(operation string, base logr.Logger, opts ...Option) func(http.Handler) http.Handler { o := newOptions(opts) return func(next http.Handler) http.Handler { inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r2 := r.WithContext(ContextWithLogger(r.Context(), base)) next.ServeHTTP(w, r2) // WithContext copies the request, so the mux recorded the matched // route on r2; surface it to otelhttp, which renames the span // from r.Pattern after the handler returns. r.Pattern = r2.Pattern }) otelOpts := []otelhttp.Option{ otelhttp.WithFilter(func(r *http.Request) bool { return r.URL.Path != "/healthz" }), // Explicit propagators: deterministic regardless of whether the // global propagator has been installed (tests, disabled tracing). otelhttp.WithPropagators(propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{})), } if o.tp != nil { otelOpts = append(otelOpts, otelhttp.WithTracerProvider(o.tp)) } return otelhttp.NewHandler(inner, operation, otelOpts...) } }