From 832c0e6c2d623ee51603a2353fdce999ee31cdef Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 6 Mar 2024 14:34:44 +0100 Subject: [PATCH] add span start end end options to gin configuration --- .../gin-gonic/gin/otelgin/gintrace.go | 6 ++-- .../gin-gonic/gin/otelgin/option.go | 18 ++++++++++ .../gin/otelgin/test/gintrace_test.go | 34 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go index df0fbf9c52b..34ad848e034 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go @@ -58,10 +58,10 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { c.Request = c.Request.WithContext(savedCtx) }() ctx := cfg.Propagators.Extract(savedCtx, propagation.HeaderCarrier(c.Request.Header)) - opts := []oteltrace.SpanStartOption{ + opts := append(cfg.SpanStartOptions, []oteltrace.SpanStartOption{ oteltrace.WithAttributes(semconvutil.HTTPServerRequest(service, c.Request)...), oteltrace.WithSpanKind(oteltrace.SpanKindServer), - } + }...) var spanName string if cfg.SpanNameFormatter == nil { spanName = c.FullPath() @@ -75,7 +75,7 @@ func Middleware(service string, opts ...Option) gin.HandlerFunc { opts = append(opts, oteltrace.WithAttributes(rAttr)) } ctx, span := tracer.Start(ctx, spanName, opts...) - defer span.End() + defer span.End(cfg.SpanEndOptions...) // pass the span through the request context c.Request = c.Request.WithContext(ctx) diff --git a/instrumentation/github.com/gin-gonic/gin/otelgin/option.go b/instrumentation/github.com/gin-gonic/gin/otelgin/option.go index 113576ca00a..0772c41fab7 100644 --- a/instrumentation/github.com/gin-gonic/gin/otelgin/option.go +++ b/instrumentation/github.com/gin-gonic/gin/otelgin/option.go @@ -17,6 +17,8 @@ type config struct { Propagators propagation.TextMapPropagator Filters []Filter SpanNameFormatter SpanNameFormatter + SpanStartOptions []oteltrace.SpanStartOption + SpanEndOptions []oteltrace.SpanEndOption } // Filter is a predicate used to determine whether a given http.request should @@ -77,3 +79,19 @@ func WithSpanNameFormatter(f func(r *http.Request) string) Option { c.SpanNameFormatter = f }) } + +// WithSpanStartOption applies options to all the HTTP span created by the +// instrumentation. +func WithSpanStartOption(o ...oteltrace.SpanStartOption) Option { + return optionFunc(func(c *config) { + c.SpanStartOptions = append(c.SpanStartOptions, o...) + }) +} + +// WithSpanEndOption applies options when ending the HTTP span created by the +// instrumentation. +func WithSpanEndOption(o ...oteltrace.SpanEndOption) Option { + return optionFunc(func(c *config) { + c.SpanEndOptions = append(c.SpanEndOptions, o...) + }) +} 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 afd6460983a..c2c9355b578 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 @@ -22,6 +22,7 @@ import ( "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" + "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/attribute" oteltrace "go.opentelemetry.io/otel/trace" @@ -179,6 +180,39 @@ func TestSpanName(t *testing.T) { } } +func TestWithSpanStartOptions(t *testing.T) { + sr := tracetest.NewSpanRecorder() + provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + + // setup + router := gin.New() + router.Use(otelgin.Middleware( + "foobar", + otelgin.WithTracerProvider(provider), + otelgin.WithSpanStartOption( + trace.WithAttributes(attribute.String("spanStart", "true")), + ), + )) + + // configure a handler that returns an error and 5xx status + // code + router.GET("/", func(c *gin.Context) { + }) + r := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + response := w.Result() + assert.Equal(t, http.StatusOK, response.StatusCode) + + // verify the errors and status are correct + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + assert.Equal(t, "/", span.Name()) + attr := span.Attributes() + assert.Contains(t, attr, attribute.String("spanStart", "true")) +} + func TestHTML(t *testing.T) { sr := tracetest.NewSpanRecorder() provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr))