diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go index 3a676586212..138c02740f1 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go @@ -6,10 +6,11 @@ package otelgin // import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" import ( - "fmt" + "net/http" + "slices" + "strings" "github.com/gin-gonic/gin" - "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin/internal/semconvutil" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -71,16 +72,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { oteltrace.WithAttributes(semconv.HTTPRoute(c.FullPath())), oteltrace.WithSpanKind(oteltrace.SpanKindServer), } - var spanName string - if cfg.SpanNameFormatter == nil { - spanName = c.FullPath() - } else { - spanName = cfg.SpanNameFormatter(c.Request) - } - if spanName == "" { - spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method) - } - ctx, span := tracer.Start(ctx, spanName, opts...) + ctx, span := tracer.Start(ctx, spanNameFormatter(c), opts...) defer span.End() // pass the span through the request context @@ -103,6 +95,26 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { } } +// spanNameFormatter is used to set span name by gin.Context. +func spanNameFormatter(c *gin.Context) string { + method, path := strings.ToUpper(c.Request.Method), c.FullPath() + if !slices.Contains([]string{ + http.MethodGet, http.MethodHead, + http.MethodPost, http.MethodPut, + http.MethodPatch, http.MethodDelete, + http.MethodConnect, http.MethodOptions, + http.MethodTrace, + }, method) { + method = "HTTP" + } + + if path != "" { + return method + " " + path + } + + return method +} + // HTML will trace the rendering of the template as a child of the // span in the given context. This is a replacement for // gin.Context.HTML function - it invokes the original function after diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/option.go b/instrumentation/github.com/gin-gonic/gin/otelgin/option.go index 143ca8e849e..67a49dad485 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/option.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/option.go @@ -19,7 +19,7 @@ type config struct { Propagators propagation.TextMapPropagator Filters []Filter GinFilters []GinFilter - SpanNameFormatter SpanNameFormatter + SpanNameFormatter SpanNameFormatter // Deprecated: since 0.58.0, remove in 0.59.0. } // Filter is a predicate used to determine whether a given http.request should @@ -31,6 +31,7 @@ type Filter func(*http.Request) bool type GinFilter func(*gin.Context) bool // SpanNameFormatter is used to set span name by http.request. +// Deprecated: since 0.58.0, remove in 0.59.0. type SpanNameFormatter func(r *http.Request) string // Option specifies instrumentation configuration options. @@ -86,8 +87,7 @@ func WithGinFilter(f ...GinFilter) Option { // WithSpanNameFormatter takes a function that will be called on every // request and the returned string will become the Span Name. -func WithSpanNameFormatter(f func(r *http.Request) string) Option { - return optionFunc(func(c *config) { - c.SpanNameFormatter = f - }) +// Deprecated: since 0.58.0, remove in 0.59.0. +func WithSpanNameFormatter(func(r *http.Request) string) Option { + return optionFunc(func(*config) {}) } diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go b/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go index f63d1955f0e..62e96ce4dee 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go @@ -157,7 +157,7 @@ func TestTrace200(t *testing.T) { spans := sr.Ended() require.Len(t, spans, 1) span := spans[0] - assert.Equal(t, "/user/:id", span.Name()) + assert.Equal(t, "GET /user/:id", span.Name()) assert.Equal(t, trace.SpanKindServer, span.SpanKind()) attr := span.Attributes() assert.Contains(t, attr, attribute.String("net.host.name", "foobar")) @@ -193,7 +193,7 @@ func TestError(t *testing.T) { spans := sr.Ended() require.Len(t, spans, 1) span := spans[0] - assert.Equal(t, "/server_err", span.Name()) + assert.Equal(t, "GET /server_err", span.Name()) attr := span.Attributes() assert.Contains(t, attr, attribute.String("net.host.name", "foobar")) assert.Contains(t, attr, attribute.Int("http.status_code", http.StatusInternalServerError)) @@ -268,8 +268,8 @@ func TestSpanName(t *testing.T) { spanNameFormatter otelgin.SpanNameFormatter wantSpanName string }{ - {"/user/1", nil, "/user/:id"}, - {"/user/1", func(r *http.Request) string { return r.URL.Path }, "/user/1"}, + {"/user/1", nil, "GET /user/:id"}, + {"/user/1", func(r *http.Request) string { return r.URL.Path }, "GET /user/:id"}, } for _, tc := range testCases { t.Run(tc.requestPath, func(t *testing.T) { @@ -317,7 +317,7 @@ func TestHTTPRouteWithSpanNameFormatter(t *testing.T) { spans := sr.Ended() require.Len(t, spans, 1) span := spans[0] - assert.Equal(t, "/user/123", span.Name()) + assert.Equal(t, "GET /user/:id", span.Name()) assert.Equal(t, trace.SpanKindServer, span.SpanKind()) attr := span.Attributes() assert.Contains(t, attr, attribute.String("http.method", "GET"))