diff --git a/CHANGELOG.md b/CHANGELOG.md index acdbb292766..90aca708dc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Added test for Fields in `go.opentelemetry.io/contrib/propagators/jaeger`. (#7119) - Allow configuring samplers in `go.opentelemetry.io/contrib/otelconf`. (#7148) - Slog log bridge now sets `SeverityText` attribute using source value in `go.opentelemetry.io/contrib/bridges/otelslog`. (#7198) +- Add `http.route` metric attribute in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#7275) ### Changed diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go index 47e576b9679..3243f7df1f0 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go @@ -122,6 +122,9 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { // Record the server-side attributes. var additionalAttributes []attribute.KeyValue + if c.FullPath() != "" { + additionalAttributes = append(additionalAttributes, sc.Route(c.FullPath())) + } if cfg.MetricAttributeFn != nil { additionalAttributes = append(additionalAttributes, cfg.MetricAttributeFn(c.Request)...) } diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/test/gin_test.go b/instrumentation/github.com/gin-gonic/gin/otelgin/test/gin_test.go index 6eb79245364..58e9c9b97b6 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/test/gin_test.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/test/gin_test.go @@ -461,11 +461,24 @@ func TestMetrics(t *testing.T) { name string metricAttributeExtractor func(*http.Request) []attribute.KeyValue ginMetricAttributeExtractor func(*gin.Context) []attribute.KeyValue + requestTarget string + wantRouteAttr string + wantStatus int64 }{ { name: "default", metricAttributeExtractor: nil, ginMetricAttributeExtractor: nil, + requestTarget: "/user/123", + wantRouteAttr: "/user/:id", + wantStatus: 200, + }, + { + name: "request target not exist", + metricAttributeExtractor: nil, + ginMetricAttributeExtractor: nil, + requestTarget: "/abc/123", + wantStatus: 404, }, { name: "with metric attributes callback", @@ -481,6 +494,9 @@ func TestMetrics(t *testing.T) { attribute.String("key3", "value3"), } }, + requestTarget: "/user/123", + wantRouteAttr: "/user/:id", + wantStatus: 200, }, } @@ -501,7 +517,7 @@ func TestMetrics(t *testing.T) { _, _ = c.Writer.Write([]byte(id)) }) - r := httptest.NewRequest("GET", "/user/123", nil) + r := httptest.NewRequest("GET", tt.requestTarget, nil) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = r @@ -518,12 +534,15 @@ func TestMetrics(t *testing.T) { attrs := []attribute.KeyValue{ attribute.String("http.request.method", "GET"), - attribute.Int64("http.response.status_code", 200), + attribute.Int64("http.response.status_code", tt.wantStatus), attribute.String("network.protocol.name", "http"), attribute.String("network.protocol.version", fmt.Sprintf("1.%d", r.ProtoMinor)), attribute.String("server.address", "foobar"), attribute.String("url.scheme", "http"), } + if tt.wantRouteAttr != "" { + attrs = append(attrs, attribute.String("http.route", tt.wantRouteAttr)) + } if tt.metricAttributeExtractor != nil { attrs = append(attrs, tt.metricAttributeExtractor(r)...)