Description
Problem Statement
Go recently added more sophisticated pattern matching to http.ServeMux
, including matching methods and path variables. I think this patterns can make good span names: They are descriptive, but also bounded (unlike request paths).
Furthermore, in Go 1.23, http.ServeMux
populates http.Request.Pattern
, making the pattern that matched a request available for handlers and, more importantly, middlewares. It would be great if otelhttp
provided, out of the box, an option to automatically name spans from http.ServeMux
patterns.
Proposed Solution
A middleware can be used to name spans using the mux pattern that matched:
// spanNameFromPattern is a simple middleware that sets the name of the span in the request context to the pattern used
// to match this request.
func spanNameFromPattern(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Call handler first, so http.ServeMux can populate r.Pattern
next.ServeHTTP(w, r)
// Set span name after the fact. As long as this middleware is used within otelhttp.Handler, the span should
// still be open and thus renameable.
trace.SpanFromContext(r.Context()).SetName(r.Pattern)
})
}
I'm currently using this in this fashion:
handler = otelhttp.NewHandler(
// Instead of using a span name formatter, this middleware sets the span name using the mux pattern.
spanNameFromPattern(handler),
"http",
// More options...
)
I think this could be a nice addition to the otelhttp
package, so users don't need to write spanNameFromPattern
on their own. We could fashion it as an option, which transparently adds this middleware:
handler = otelhttp.NewHandler(
handler,
"http",
otelhttp.WithSpanNameFromMuxPattern(), // Tentative name.
)
Or perhaps expose it as a middleware itself:
handler = otelhttp.NewHandler(
otelhttp.SpanNamer(handler), // VERY tentative name.
"http",
)
Furthermore, I think some more sophistication could be added to the middleware, for example checking if the pattern already includes a method, and add it if it doesn't.
Alternatives
Have users write this middleware themselves, or set span names manually in application logic.
Additional Context
I'd be happy to make a PR to otelhttp
if you think this can be useful to have in there :)