diff --git a/CHANGELOG.md b/CHANGELOG.md index edbb73f1415..91d2fa5a17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - 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) +- Add the `WithSpanStartOptions` option to add custom options to new spans `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#7261) - Add instrumentation support for `go.mongodb.org/mongo-driver/v2` in `go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/v2/mongo/otelmongo`. (#6539) ### Changed diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/config.go b/instrumentation/github.com/gin-gonic/gin/otelgin/config.go index c69fc3b3d41..10cf06228b5 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/config.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/config.go @@ -21,6 +21,7 @@ import ( type config struct { TracerProvider oteltrace.TracerProvider Propagators propagation.TextMapPropagator + SpanStartOptions []oteltrace.SpanStartOption Filters []Filter GinFilters []GinFilter SpanNameFormatter SpanNameFormatter @@ -89,6 +90,14 @@ func WithPropagators(propagators propagation.TextMapPropagator) Option { }) } +// WithSpanStartOptions configures an additional set of +// trace.SpanStartOptions, which are applied to each new span. +func WithSpanStartOptions(opts ...oteltrace.SpanStartOption) Option { + return optionFunc(func(c *config) { + c.SpanStartOptions = append(c.SpanStartOptions, opts...) + }) +} + // WithTracerProvider specifies a tracer provider to use for creating a tracer. // If none is specified, the global provider is used. func WithTracerProvider(provider oteltrace.TracerProvider) Option { diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go index 3243f7df1f0..a157d5d6c54 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gin.go @@ -31,6 +31,7 @@ const ( // server handling the request. func Middleware(service string, opts ...Option) gin.HandlerFunc { cfg := config{} + for _, opt := range opts { opt.apply(&cfg) } @@ -95,6 +96,8 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { oteltrace.WithSpanKind(oteltrace.SpanKindServer), } + opts = append(opts, cfg.SpanStartOptions...) + spanName := cfg.SpanNameFormatter(c) if spanName == "" { spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method) 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 58e9c9b97b6..2b55912bfb3 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 @@ -268,6 +268,31 @@ func TestSpanStatus(t *testing.T) { }) } +func TestWithSpanOptions_CustomAttributesAndSpanKind(t *testing.T) { + sr := tracetest.NewSpanRecorder() + provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + + customAttr := attribute.String("custom.key", "custom.value") + + router := gin.New() + router.Use(otelgin.Middleware("foobar", + otelgin.WithTracerProvider(provider), + otelgin.WithSpanStartOptions(trace.WithAttributes(customAttr)), + )) + router.GET("/test", func(c *gin.Context) {}) + + r := httptest.NewRequest("GET", "/test", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + spans := sr.Ended() + require.Len(t, spans, 1) + + span := spans[0] + assert.Contains(t, span.Attributes(), customAttr) + assert.Equal(t, trace.SpanKindServer, span.SpanKind()) +} + func TestSpanName(t *testing.T) { sr := tracetest.NewSpanRecorder() provider := sdktrace.NewTracerProvider(