From 00cb0d0421f26f33b90c9431eeeb8e642e82074b Mon Sep 17 00:00:00 2001 From: Ryan D McGuire Date: Wed, 27 Aug 2025 10:04:05 -0400 Subject: [PATCH 1/2] refactors OpenTelemetry HTTP instrumentation by centralizing handler wrapping, adding span name formatting, and filtering health and metrics paths. --- pkg/srv/http/http.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pkg/srv/http/http.go b/pkg/srv/http/http.go index 78bbea6..b87defc 100644 --- a/pkg/srv/http/http.go +++ b/pkg/srv/http/http.go @@ -39,19 +39,12 @@ func prepHTTPServer(opts *opts.AppHTTP) *http.Server { mux = &http.ServeMux{} ) - // NOTE: Wraps handle func with otelhttp handler and - // inserts route tag - otelHandleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) { - handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc)) - mux.Handle(pattern, handler) // Associate pattern with handler - } - healthChecks := handleHealthCheckFunc(opts.Ctx, opts.HealthChecks...) - otelHandleFunc("/health", healthChecks) - otelHandleFunc("/", healthChecks) + mux.HandleFunc("/health", healthChecks) + mux.HandleFunc("/", healthChecks) for _, f := range opts.Funcs { - otelHandleFunc(f.Path, f.HandlerFunc) + mux.HandleFunc(f.Path, f.HandlerFunc) } // Prometheus metrics endpoint @@ -74,12 +67,12 @@ func prepHTTPServer(opts *opts.AppHTTP) *http.Server { if h.StripPrefix { h.Handler = http.StripPrefix(h.Prefix[:len(h.Prefix)-1], h.Handler) } - mux.Handle(h.Prefix, h.Handler) + mux.Handle(h.Prefix, otelhttp.WithRouteTag(h.Prefix, h.Handler)) } - // Add OTEL, skip health-check spans - // NOTE: Add any other span exclusions here + // Add OTEL instrumentation, filter noise, set span names handler := otelhttp.NewHandler(mux, "/", + // TODO: Make configurable similar to config.http.LogExcludePathRegexps otelhttp.WithFilter(func(r *http.Request) bool { switch r.URL.Path { case "/health": @@ -89,6 +82,14 @@ func prepHTTPServer(opts *opts.AppHTTP) *http.Server { default: return true } + }), + otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string { + _, pattern := mux.Handler(r) + if pattern != "" { + return pattern // Use the route pattern as the span name, e.g., "/users/{id}" + } + // Fallback to the default naming convention if no route is found. + return operation + " " + r.URL.Path })) // Set timeouts from defaults, override -- 2.49.1 From f0c3bc6b9eda471010b6359c0836d37935fb4056 Mon Sep 17 00:00:00 2001 From: Ryan D McGuire Date: Wed, 27 Aug 2025 10:17:24 -0400 Subject: [PATCH 2/2] feat: standardize OpenTelemetry HTTP span names to include method and route pattern --- pkg/srv/http/http.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/srv/http/http.go b/pkg/srv/http/http.go index b87defc..4c5f6a2 100644 --- a/pkg/srv/http/http.go +++ b/pkg/srv/http/http.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "os" + "regexp" "strings" "time" @@ -25,11 +26,12 @@ import ( ) var ( - httpMeter metric.Meter - httpTracer trace.Tracer - defReadTimeout = 10 * time.Second - defWriteTimeout = 10 * time.Second - defIdleTimeout = 15 * time.Second + httpMeter metric.Meter + httpTracer trace.Tracer + httpPatternWithMethodRegexp = regexp.MustCompile(`(\w+) .*`) + defReadTimeout = 10 * time.Second + defWriteTimeout = 10 * time.Second + defIdleTimeout = 15 * time.Second ) func prepHTTPServer(opts *opts.AppHTTP) *http.Server { @@ -84,12 +86,16 @@ func prepHTTPServer(opts *opts.AppHTTP) *http.Server { } }), otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string { - _, pattern := mux.Handler(r) - if pattern != "" { - return pattern // Use the route pattern as the span name, e.g., "/users/{id}" + endpoint := r.URL.Path + if _, pattern := mux.Handler(r); pattern != "" { + endpoint = pattern } - // Fallback to the default naming convention if no route is found. - return operation + " " + r.URL.Path + + if httpPatternWithMethodRegexp.MatchString(endpoint) { + return endpoint + } + + return fmt.Sprintf("%s %s", r.Method, endpoint) })) // Set timeouts from defaults, override -- 2.49.1