Is there an existing issue for this?
Package
github.com/gin-gonic/gin
Version
v1.10.0 (current stable; needs >= v1.4.0 for FullPath())
Environment
Go HTTP servers, mostly containerized. Gin is pretty widely used this would cover a lot of ground.
Use-case
REST APIs built with gin. The net/http hook right now gives spans like "GET" with url.path=/users/42 but there's no way to get the actual route template. With gin, c.FullPath() returns /users/:id which is what you do want as http.route for grouping requests properly in dashboards. Without it every unique URL becomes its own series.
The hard part is that c.FullPath() is only populated after gin's router matches the request so a gin-specific hook is needed on top of net/http.
Telemetry
Server spans following HTTP semconv v1.37.0, same attributes as the existing nethttp server hook http.request.method, http.response.status_code, url.path, server.address, etc. The key addition would be http.route from c.FullPath().
Span name would go from just "GET" to "GET /users/:id" after the route is resolved. 5xx responses would set span status to Error. Metrics deferred to a follow-up.
Anything else?
While looking into this I checked how loongsuite and dd-trace-go handle gin:
- loongsuite (alibaba/loongsuite-go-agent/pkg/rules/gin/) hooks (*Context) Next and uses GLS to find the span. Since otelc uses context.Context instead of GLS, that approach doesn't directly apply here.
- dd-trace-go (DataDog/dd-trace-go/contrib/gin-gonic/gin/) adds a runtime middleware but that requires the user to change their code.
What I'm thinking is two hooks in one YAML file (similar to how redis/v9/v9.yaml has multiple rules):
- (*Engine).ServeHTTP start the span, wrap ResponseWriter, pass context through r.WithContext(ctx)
- (*Context).Next at this point routing is done, so read c.FullPath() and update the span name
I've been experimenting locally and have something that seems to work with basic unit tests. Happy to share it once we agree on the approach
Also not sure if this needs an ADR since it's the first case of two hooks coordinating through request context.
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Is there an existing issue for this?
Package
github.com/gin-gonic/gin
Version
v1.10.0 (current stable; needs >= v1.4.0 for FullPath())
Environment
Go HTTP servers, mostly containerized. Gin is pretty widely used this would cover a lot of ground.
Use-case
REST APIs built with gin. The net/http hook right now gives spans like "GET" with url.path=/users/42 but there's no way to get the actual route template. With gin, c.FullPath() returns /users/:id which is what you do want as http.route for grouping requests properly in dashboards. Without it every unique URL becomes its own series.
The hard part is that c.FullPath() is only populated after gin's router matches the request so a gin-specific hook is needed on top of net/http.
Telemetry
Server spans following HTTP semconv v1.37.0, same attributes as the existing nethttp server hook http.request.method, http.response.status_code, url.path, server.address, etc. The key addition would be http.route from c.FullPath().
Span name would go from just "GET" to "GET /users/:id" after the route is resolved. 5xx responses would set span status to Error. Metrics deferred to a follow-up.
Anything else?
While looking into this I checked how loongsuite and dd-trace-go handle gin:
What I'm thinking is two hooks in one YAML file (similar to how redis/v9/v9.yaml has multiple rules):
I've been experimenting locally and have something that seems to work with basic unit tests. Happy to share it once we agree on the approach
Also not sure if this needs an ADR since it's the first case of two hooks coordinating through request context.
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it. Learn more here.