diff --git a/instrumentation/github.com/gorilla/mux/otelmux/config.go b/instrumentation/github.com/gorilla/mux/otelmux/config.go index 0f9b24242a0..53814f3eb98 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/config.go +++ b/instrumentation/github.com/gorilla/mux/otelmux/config.go @@ -6,6 +6,8 @@ package otelmux // import "go.opentelemetry.io/contrib/instrumentation/github.co import ( "net/http" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/propagation" oteltrace "go.opentelemetry.io/otel/trace" ) @@ -13,6 +15,7 @@ import ( // config is used to configure the mux middleware. type config struct { TracerProvider oteltrace.TracerProvider + meterProvider metric.MeterProvider Propagators propagation.TextMapPropagator spanNameFormatter func(string, *http.Request) string PublicEndpoint bool @@ -97,3 +100,13 @@ func WithFilter(f Filter) Option { c.Filters = append(c.Filters, f) }) } + +// WithMeterProvider specifies a meter provider to use for creating a meter. +// If none is specified, the global provider is used. +func WithMeterProvider(provider metric.MeterProvider) Option { + return optionFunc(func(cfg *config) { + if provider != nil { + cfg.meterProvider = provider + } + }) +} diff --git a/instrumentation/github.com/gorilla/mux/otelmux/example/go.mod b/instrumentation/github.com/gorilla/mux/otelmux/example/go.mod index adc81dfb759..63fbde6a961 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/example/go.mod +++ b/instrumentation/github.com/gorilla/mux/otelmux/example/go.mod @@ -19,6 +19,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/google/uuid v1.6.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect golang.org/x/sys v0.29.0 // indirect ) diff --git a/instrumentation/github.com/gorilla/mux/otelmux/example/go.sum b/instrumentation/github.com/gorilla/mux/otelmux/example/go.sum index e160ea2ce32..d26a3605a8f 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/example/go.sum +++ b/instrumentation/github.com/gorilla/mux/otelmux/example/go.sum @@ -19,6 +19,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 h1:jBpDk4HAUsrnVO1FsfCfCOTEc/MkInJmvfCHYLFiT80= diff --git a/instrumentation/github.com/gorilla/mux/otelmux/go.mod b/instrumentation/github.com/gorilla/mux/otelmux/go.mod index bd30f1a7de0..c1ecd75fc0e 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/go.mod +++ b/instrumentation/github.com/gorilla/mux/otelmux/go.mod @@ -3,19 +3,20 @@ module go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmu go 1.22.0 require ( - github.com/felixge/httpsnoop v1.0.4 github.com/gorilla/mux v1.8.1 github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 go.opentelemetry.io/otel v1.34.0 + go.opentelemetry.io/otel/metric v1.34.0 go.opentelemetry.io/otel/trace v1.34.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/instrumentation/github.com/gorilla/mux/otelmux/go.sum b/instrumentation/github.com/gorilla/mux/otelmux/go.sum index 15010c081b9..5f32cdd19a3 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/go.sum +++ b/instrumentation/github.com/gorilla/mux/otelmux/go.sum @@ -23,6 +23,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= diff --git a/instrumentation/github.com/gorilla/mux/otelmux/mux.go b/instrumentation/github.com/gorilla/mux/otelmux/mux.go index c7b2355eca8..1e786f5cab2 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/mux.go +++ b/instrumentation/github.com/gorilla/mux/otelmux/mux.go @@ -4,16 +4,18 @@ package otelmux // import "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" import ( - "fmt" "net/http" - "sync" - "github.com/felixge/httpsnoop" - "github.com/gorilla/mux" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux/internal/semconvutil" + + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + + "github.com/gorilla/mux" + "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/propagation" semconv "go.opentelemetry.io/otel/semconv/v1.20.0" "go.opentelemetry.io/otel/trace" ) @@ -34,7 +36,8 @@ func Middleware(service string, opts ...Option) mux.MiddlewareFunc { if cfg.TracerProvider == nil { cfg.TracerProvider = otel.GetTracerProvider() } - tracer := cfg.TracerProvider.Tracer( + // TODO: check if this is necessary + _ = cfg.TracerProvider.Tracer( ScopeName, trace.WithInstrumentationVersion(Version()), ) @@ -44,92 +47,40 @@ func Middleware(service string, opts ...Option) mux.MiddlewareFunc { if cfg.spanNameFormatter == nil { cfg.spanNameFormatter = defaultSpanNameFunc } + if cfg.meterProvider == nil { + cfg.meterProvider = otel.GetMeterProvider() + } return func(handler http.Handler) http.Handler { - return traceware{ + return middleware{ service: service, - tracer: tracer, - propagators: cfg.Propagators, handler: handler, spanNameFormatter: cfg.spanNameFormatter, - publicEndpoint: cfg.PublicEndpoint, + tracerProvider: cfg.TracerProvider, + meterProvider: cfg.meterProvider, + propagators: cfg.Propagators, publicEndpointFn: cfg.PublicEndpointFn, + publicEndpoint: cfg.PublicEndpoint, filters: cfg.Filters, } } } -type traceware struct { - service string - tracer trace.Tracer - propagators propagation.TextMapPropagator +type middleware struct { handler http.Handler + service string spanNameFormatter func(string, *http.Request) string - publicEndpoint bool + tracerProvider trace.TracerProvider + propagators propagation.TextMapPropagator publicEndpointFn func(*http.Request) bool + publicEndpoint bool filters []Filter + meterProvider metric.MeterProvider } -type recordingResponseWriter struct { - writer http.ResponseWriter - written bool - status int -} - -var rrwPool = &sync.Pool{ - New: func() interface{} { - return &recordingResponseWriter{} - }, -} - -func getRRW(writer http.ResponseWriter) *recordingResponseWriter { - rrw := rrwPool.Get().(*recordingResponseWriter) - rrw.written = false - rrw.status = http.StatusOK - rrw.writer = httpsnoop.Wrap(writer, httpsnoop.Hooks{ - Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc { - return func(b []byte) (int, error) { - if !rrw.written { - rrw.written = true - } - return next(b) - } - }, - WriteHeader: func(next httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { - return func(statusCode int) { - if !rrw.written { - rrw.written = true - rrw.status = statusCode - } - next(statusCode) - } - }, - }) - return rrw -} - -func putRRW(rrw *recordingResponseWriter) { - rrw.writer = nil - rrwPool.Put(rrw) -} - -// defaultSpanNameFunc just reuses the route name as the span name. -func defaultSpanNameFunc(routeName string, _ *http.Request) string { return routeName } - -// ServeHTTP implements the http.Handler interface. It does the actual -// tracing of the request. -func (tw traceware) ServeHTTP(w http.ResponseWriter, r *http.Request) { - for _, f := range tw.filters { - if !f(r) { - // Simply pass through to the handler if a filter rejects the request - tw.handler.ServeHTTP(w, r) - return - } - } - - ctx := tw.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) +func (m middleware) ServeHTTP(writer http.ResponseWriter, request *http.Request) { routeStr := "" - route := mux.CurrentRoute(r) + route := mux.CurrentRoute(request) if route != nil { var err error routeStr, err = route.GetPathTemplate() @@ -141,34 +92,45 @@ func (tw traceware) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - opts := []trace.SpanStartOption{ - trace.WithAttributes(semconvutil.HTTPServerRequest(tw.service, r)...), - trace.WithSpanKind(trace.SpanKindServer), + otelOpts := []otelhttp.Option{ + otelhttp.WithTracerProvider(m.tracerProvider), + otelhttp.WithMeterProvider(m.meterProvider), + otelhttp.WithPropagators(m.propagators), + otelhttp.WithPublicEndpointFn(m.publicEndpointFn), + otelhttp.WithSpanNameFormatter(func(op string, r *http.Request) string { + return m.spanNameFormatter(routeStr, r) + }), } - if tw.publicEndpoint || (tw.publicEndpointFn != nil && tw.publicEndpointFn(r.WithContext(ctx))) { - opts = append(opts, trace.WithNewRoot()) - // Linking incoming span context if any for public endpoint. - if s := trace.SpanContextFromContext(ctx); s.IsValid() && s.IsRemote() { - opts = append(opts, trace.WithLinks(trace.Link{SpanContext: s})) - } + if m.publicEndpoint { + otelOpts = append(otelOpts, otelhttp.WithPublicEndpoint()) } - if routeStr == "" { - routeStr = fmt.Sprintf("HTTP %s route not found", r.Method) - } else { - rAttr := semconv.HTTPRoute(routeStr) - opts = append(opts, trace.WithAttributes(rAttr)) + for _, f := range m.filters { + otelOpts = append(otelOpts, otelhttp.WithFilter(otelhttp.Filter(f))) + } + traceOpts := []trace.SpanStartOption{ + trace.WithAttributes(semconvutil.HTTPServerRequest(m.service, request)...), + trace.WithSpanKind(trace.SpanKindServer), } - spanName := tw.spanNameFormatter(routeStr, r) - ctx, span := tw.tracer.Start(ctx, spanName, opts...) - defer span.End() - r2 := r.WithContext(ctx) - rrw := getRRW(w) - defer putRRW(rrw) - tw.handler.ServeHTTP(rrw.writer, r2) - if rrw.status > 0 { - span.SetAttributes(semconv.HTTPStatusCode(rrw.status)) + + if routeStr != "" { + rAttr := semconv.HTTPRoute(routeStr) + traceOpts = append(traceOpts, trace.WithAttributes(rAttr)) } - span.SetStatus(semconvutil.HTTPServerStatus(rrw.status)) + otelOpts = append(otelOpts, otelhttp.WithSpanOptions(traceOpts...)) + + h := otelhttp.NewHandler( + m.handler, + m.service, + otelOpts..., + ) + h.ServeHTTP(writer, request) +} + +var _ http.Handler = middleware{} + +// defaultSpanNameFunc just reuses the route name as the span name. +func defaultSpanNameFunc(routeName string, r *http.Request) string { + return routeName } diff --git a/instrumentation/github.com/gorilla/mux/otelmux/test/go.mod b/instrumentation/github.com/gorilla/mux/otelmux/test/go.mod index b7ba984be47..141a2cd4a9b 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/test/go.mod +++ b/instrumentation/github.com/gorilla/mux/otelmux/test/go.mod @@ -19,6 +19,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect golang.org/x/sys v0.29.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/instrumentation/github.com/gorilla/mux/otelmux/test/go.sum b/instrumentation/github.com/gorilla/mux/otelmux/test/go.sum index 8d9ce78bc23..baea1350d55 100644 --- a/instrumentation/github.com/gorilla/mux/otelmux/test/go.sum +++ b/instrumentation/github.com/gorilla/mux/otelmux/test/go.sum @@ -25,6 +25,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=