Skip to content

Commit c99bac0

Browse files
committed
refactor(otelgin): Modify span name formatter function signature and default implementation
1 parent e97e6e4 commit c99bac0

File tree

4 files changed

+66
-23
lines changed

4 files changed

+66
-23
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1919

2020
- Change the span name to be `GET /path` so it complies with the OTel HTTP semantic conventions in `go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho`. (#6365)
2121
- Record errors instead of setting the `gin.errors` attribute in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6346)
22+
- Change the default span name to be `GET /path` so it complies with the OTel HTTP semantic conventions, and set `spanNameFormatter` to a non-public property in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6381)
2223

2324
### Fixed
2425

instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go

+4-6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc {
4343
if cfg.Propagators == nil {
4444
cfg.Propagators = otel.GetTextMapPropagator()
4545
}
46+
if cfg.spanNameFormatter == nil {
47+
cfg.spanNameFormatter = defaultSpanNameFormatter
48+
}
4649
return func(c *gin.Context) {
4750
for _, f := range cfg.Filters {
4851
if !f(c.Request) {
@@ -71,12 +74,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc {
7174
oteltrace.WithAttributes(semconv.HTTPRoute(c.FullPath())),
7275
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
7376
}
74-
var spanName string
75-
if cfg.SpanNameFormatter == nil {
76-
spanName = c.FullPath()
77-
} else {
78-
spanName = cfg.SpanNameFormatter(c.Request)
79-
}
77+
spanName := cfg.spanNameFormatter(c.FullPath(), c.Request)
8078
if spanName == "" {
8179
spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method)
8280
}

instrumentation/github.com/gin-gonic/gin/otelgin/option.go

+27-4
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,40 @@ package otelgin // import "go.opentelemetry.io/contrib/instrumentation/github.co
77

88
import (
99
"net/http"
10+
"slices"
11+
"strings"
1012

1113
"github.com/gin-gonic/gin"
1214

1315
"go.opentelemetry.io/otel/propagation"
1416
oteltrace "go.opentelemetry.io/otel/trace"
1517
)
1618

19+
var defaultSpanNameFormatter = func(path string, r *http.Request) string {
20+
method := strings.ToUpper(r.Method)
21+
if !slices.Contains([]string{
22+
http.MethodGet, http.MethodHead,
23+
http.MethodPost, http.MethodPut,
24+
http.MethodPatch, http.MethodDelete,
25+
http.MethodConnect, http.MethodOptions,
26+
http.MethodTrace,
27+
}, method) {
28+
method = "HTTP"
29+
}
30+
31+
if path != "" {
32+
return method + " " + path
33+
}
34+
35+
return method
36+
}
37+
1738
type config struct {
1839
TracerProvider oteltrace.TracerProvider
1940
Propagators propagation.TextMapPropagator
2041
Filters []Filter
2142
GinFilters []GinFilter
22-
SpanNameFormatter SpanNameFormatter
43+
spanNameFormatter SpanNameFormatter
2344
}
2445

2546
// Filter is a predicate used to determine whether a given http.request should
@@ -31,7 +52,7 @@ type Filter func(*http.Request) bool
3152
type GinFilter func(*gin.Context) bool
3253

3354
// SpanNameFormatter is used to set span name by http.request.
34-
type SpanNameFormatter func(r *http.Request) string
55+
type SpanNameFormatter func(path string, r *http.Request) string
3556

3657
// Option specifies instrumentation configuration options.
3758
type Option interface {
@@ -86,8 +107,10 @@ func WithGinFilter(f ...GinFilter) Option {
86107

87108
// WithSpanNameFormatter takes a function that will be called on every
88109
// request and the returned string will become the Span Name.
89-
func WithSpanNameFormatter(f func(r *http.Request) string) Option {
110+
func WithSpanNameFormatter(f func(path string, r *http.Request) string) Option {
90111
return optionFunc(func(c *config) {
91-
c.SpanNameFormatter = f
112+
if f != nil {
113+
c.spanNameFormatter = f
114+
}
92115
})
93116
}

instrumentation/github.com/gin-gonic/gin/otelgin/test/gintrace_test.go

+34-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package test
77

88
import (
99
"errors"
10+
"fmt"
1011
"html/template"
1112
"net/http"
1213
"net/http/httptest"
@@ -157,7 +158,7 @@ func TestTrace200(t *testing.T) {
157158
spans := sr.Ended()
158159
require.Len(t, spans, 1)
159160
span := spans[0]
160-
assert.Equal(t, "/user/:id", span.Name())
161+
assert.Equal(t, "GET /user/:id", span.Name())
161162
assert.Equal(t, trace.SpanKindServer, span.SpanKind())
162163
attr := span.Attributes()
163164
assert.Contains(t, attr, attribute.String("net.host.name", "foobar"))
@@ -193,7 +194,7 @@ func TestError(t *testing.T) {
193194
spans := sr.Ended()
194195
require.Len(t, spans, 1)
195196
span := spans[0]
196-
assert.Equal(t, "/server_err", span.Name())
197+
assert.Equal(t, "GET /server_err", span.Name())
197198
attr := span.Attributes()
198199
assert.Contains(t, attr, attribute.String("net.host.name", "foobar"))
199200
assert.Contains(t, attr, attribute.Int("http.status_code", http.StatusInternalServerError))
@@ -263,27 +264,47 @@ func TestSpanStatus(t *testing.T) {
263264
}
264265

265266
func TestSpanName(t *testing.T) {
267+
imsb := tracetest.NewInMemoryExporter()
268+
provider := sdktrace.NewTracerProvider(
269+
sdktrace.WithSyncer(imsb),
270+
)
271+
266272
testCases := []struct {
273+
method string
274+
route string
267275
requestPath string
268276
spanNameFormatter otelgin.SpanNameFormatter
269277
wantSpanName string
270278
}{
271-
{"/user/1", nil, "/user/:id"},
272-
{"/user/1", func(r *http.Request) string { return r.URL.Path }, "/user/1"},
279+
// Test for standard methods
280+
{http.MethodGet, "/user/:id", "/user/1", nil, "GET /user/:id"},
281+
{http.MethodPost, "/user/:id", "/user/1", nil, "POST /user/:id"},
282+
{http.MethodPut, "/user/:id", "/user/1", nil, "PUT /user/:id"},
283+
{http.MethodPatch, "/user/:id", "/user/1", nil, "PATCH /user/:id"},
284+
{http.MethodDelete, "/user/:id", "/user/1", nil, "DELETE /user/:id"},
285+
{http.MethodConnect, "/user/:id", "/user/1", nil, "CONNECT /user/:id"},
286+
{http.MethodOptions, "/user/:id", "/user/1", nil, "OPTIONS /user/:id"},
287+
{http.MethodTrace, "/user/:id", "/user/1", nil, "TRACE /user/:id"},
288+
// Test for no route
289+
{http.MethodGet, "", "/user/1", nil, "GET"},
290+
// Test for invalid method
291+
{"INVALID", "/user/:id", "/user/1", nil, "HTTP /user/:id"},
292+
// Test for custom span name formatter
293+
{http.MethodGet, "/user/:id", "/user/1", func(_ string, r *http.Request) string { return r.URL.Path }, "/user/1"},
273294
}
295+
274296
for _, tc := range testCases {
275-
t.Run(tc.requestPath, func(t *testing.T) {
276-
sr := tracetest.NewSpanRecorder()
277-
provider := sdktrace.NewTracerProvider()
278-
provider.RegisterSpanProcessor(sr)
297+
t.Run(fmt.Sprintf("method: %s, route: %s, requestPath: %s", tc.method, tc.route, tc.requestPath), func(t *testing.T) {
298+
defer imsb.Reset()
299+
279300
router := gin.New()
280301
router.Use(otelgin.Middleware("foobar", otelgin.WithTracerProvider(provider), otelgin.WithSpanNameFormatter(tc.spanNameFormatter)))
281-
router.GET("/user/:id", func(c *gin.Context) {})
302+
router.Handle(tc.method, tc.route, func(c *gin.Context) {})
282303

283-
router.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", tc.requestPath, nil))
304+
router.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(tc.method, tc.requestPath, nil))
284305

285-
require.Len(t, sr.Ended(), 1, "should emit a span")
286-
assert.Equal(t, tc.wantSpanName, sr.Ended()[0].Name(), "span name not correct")
306+
require.Len(t, imsb.GetSpans(), 1, "should emit a span")
307+
assert.Equal(t, tc.wantSpanName, imsb.GetSpans()[0].Name, "span name not correct")
287308
})
288309
}
289310
}
@@ -295,7 +316,7 @@ func TestHTTPRouteWithSpanNameFormatter(t *testing.T) {
295316
router := gin.New()
296317
router.Use(otelgin.Middleware("foobar",
297318
otelgin.WithTracerProvider(provider),
298-
otelgin.WithSpanNameFormatter(func(r *http.Request) string {
319+
otelgin.WithSpanNameFormatter(func(_ string, r *http.Request) string {
299320
return r.URL.Path
300321
}),
301322
),

0 commit comments

Comments
 (0)