Skip to content

Automatic route/pattern naming for http.ServeMux #6193

Open
@nadiamoe

Description

@nadiamoe

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 :)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions