Skip to content

Commit fa3e6e6

Browse files
committed
otelgin: Add a WithSpanOptions option
1 parent c1aeca6 commit fa3e6e6

File tree

4 files changed

+64
-1
lines changed

4 files changed

+64
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2121
- Allow configuring samplers in `go.opentelemetry.io/contrib/otelconf`. (#7148)
2222
- Slog log bridge now sets `SeverityText` attribute using source value in `go.opentelemetry.io/contrib/bridges/otelslog`. (#7198)
2323
- Add `http.route` metric attribute in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#7275)
24+
- Add the `WithSpanOptions` option to add extra attributes, links, etc. to the spans it generates in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#7261)
2425

2526
### Changed
2627

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

+9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
type config struct {
2222
TracerProvider oteltrace.TracerProvider
2323
Propagators propagation.TextMapPropagator
24+
SpanStartOptions []oteltrace.SpanStartOption
2425
Filters []Filter
2526
GinFilters []GinFilter
2627
SpanNameFormatter SpanNameFormatter
@@ -89,6 +90,14 @@ func WithPropagators(propagators propagation.TextMapPropagator) Option {
8990
})
9091
}
9192

93+
// WithSpanOptions configures an additional set of
94+
// trace.SpanOptions, which are applied to each new span.
95+
func WithSpanOptions(opts ...oteltrace.SpanStartOption) Option {
96+
return optionFunc(func(c *config) {
97+
c.SpanStartOptions = append(c.SpanStartOptions, opts...)
98+
})
99+
}
100+
92101
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
93102
// If none is specified, the global provider is used.
94103
func WithTracerProvider(provider oteltrace.TracerProvider) Option {

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ const (
3131
// server handling the request.
3232
func Middleware(service string, opts ...Option) gin.HandlerFunc {
3333
cfg := config{}
34+
35+
defaultOpts := []Option{
36+
WithSpanOptions(oteltrace.WithSpanKind(oteltrace.SpanKindServer)),
37+
}
38+
39+
opts = append(defaultOpts, opts...)
40+
3441
for _, opt := range opts {
3542
opt.apply(&cfg)
3643
}
@@ -92,9 +99,10 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc {
9299
opts := []oteltrace.SpanStartOption{
93100
oteltrace.WithAttributes(sc.RequestTraceAttrs(service, c.Request, requestTraceAttrOpts)...),
94101
oteltrace.WithAttributes(sc.Route(c.FullPath())),
95-
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
96102
}
97103

104+
opts = append(opts, cfg.SpanStartOptions...)
105+
98106
spanName := cfg.SpanNameFormatter(c)
99107
if spanName == "" {
100108
spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method)

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

+45
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,51 @@ func TestSpanStatus(t *testing.T) {
268268
})
269269
}
270270

271+
func TestWithSpanOptions_AddsCustomAttributes(t *testing.T) {
272+
sr := tracetest.NewSpanRecorder()
273+
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr))
274+
275+
customAttr := attribute.String("custom.key", "custom.value")
276+
277+
router := gin.New()
278+
router.Use(otelgin.Middleware("foobar",
279+
otelgin.WithTracerProvider(provider),
280+
otelgin.WithSpanOptions(trace.WithAttributes(customAttr)),
281+
))
282+
router.GET("/test", func(c *gin.Context) {})
283+
284+
r := httptest.NewRequest("GET", "/test", nil)
285+
w := httptest.NewRecorder()
286+
router.ServeHTTP(w, r)
287+
288+
spans := sr.Ended()
289+
require.Len(t, spans, 1)
290+
291+
span := spans[0]
292+
assert.Contains(t, span.Attributes(), customAttr)
293+
}
294+
295+
func TestWithSpanOptions_PreservesDefaultSpanKind(t *testing.T) {
296+
sr := tracetest.NewSpanRecorder()
297+
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr))
298+
299+
router := gin.New()
300+
router.Use(otelgin.Middleware("foobar",
301+
otelgin.WithTracerProvider(provider),
302+
otelgin.WithSpanOptions(), // no-op option, should still preserve SpanKindServer
303+
))
304+
router.GET("/test", func(c *gin.Context) {})
305+
306+
r := httptest.NewRequest("GET", "/test", nil)
307+
w := httptest.NewRecorder()
308+
router.ServeHTTP(w, r)
309+
310+
spans := sr.Ended()
311+
require.Len(t, spans, 1)
312+
313+
assert.Equal(t, trace.SpanKindServer, spans[0].SpanKind())
314+
}
315+
271316
func TestSpanName(t *testing.T) {
272317
sr := tracetest.NewSpanRecorder()
273318
provider := sdktrace.NewTracerProvider(

0 commit comments

Comments
 (0)