From 32aca4e33a7ed395155625579889cfcd64498751 Mon Sep 17 00:00:00 2001 From: kaptinlin Date: Fri, 28 Jul 2023 15:35:06 +0800 Subject: [PATCH 1/2] add custom attributes support to otelfiber --- otelfiber/README.md | 1 + otelfiber/config.go | 9 ++++++++ otelfiber/fiber_test.go | 46 +++++++++++++++++++++++++++++++++++++++++ otelfiber/semconv.go | 4 ++++ 4 files changed, 60 insertions(+) diff --git a/otelfiber/README.md b/otelfiber/README.md index 7632358c..90bbdf59 100644 --- a/otelfiber/README.md +++ b/otelfiber/README.md @@ -39,6 +39,7 @@ otelfiber.Middleware(opts ...otelfiber.Option) fiber.Handler | Propagators | `propagation.TextMapPropagator` | Specifies propagators to use for extracting information from the HTTP requests | If none are specified, global ones will be used | | ServerName | `*string` | specifies the value to use when setting the `http.server_name` attribute on metrics/spans | - | | SpanNameFormatter | `func(*fiber.Ctx) string` | Takes a function that will be called on every request and the returned string will become the Span Name | default formatter returns the route pathRaw | +| CustomAttributes | `func(*fiber.Ctx) []attribute.KeyValue` | Define a function to add custom attributes to the span | nil | ## Usage diff --git a/otelfiber/config.go b/otelfiber/config.go index ef84ee2a..70373128 100644 --- a/otelfiber/config.go +++ b/otelfiber/config.go @@ -2,6 +2,7 @@ package otelfiber import ( "github.com/gofiber/fiber/v2" + "go.opentelemetry.io/otel/attribute" otelmetric "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" oteltrace "go.opentelemetry.io/otel/trace" @@ -16,6 +17,7 @@ type config struct { Propagators propagation.TextMapPropagator ServerName *string SpanNameFormatter func(*fiber.Ctx) string + CustomAttributes func(*fiber.Ctx) []attribute.KeyValue } // Option specifies instrumentation configuration options. @@ -87,3 +89,10 @@ func WithPort(port int) Option { }) } +// WithCustomAttributes specifies a function that will be called on every +// request and the returned attributes will be added to the span. +func WithCustomAttributes(f func(ctx *fiber.Ctx) []attribute.KeyValue) Option { + return optionFunc(func(cfg *config) { + cfg.CustomAttributes = f + }) +} diff --git a/otelfiber/fiber_test.go b/otelfiber/fiber_test.go index e17f5438..72b764c8 100644 --- a/otelfiber/fiber_test.go +++ b/otelfiber/fiber_test.go @@ -401,3 +401,49 @@ func getHistogram(value float64, attrs []attribute.KeyValue) metricdata.Histogra Temporality: metricdata.CumulativeTemporality, } } + +func TestCustomAttributes(t *testing.T) { + sr := new(oteltest.SpanRecorder) + provider := oteltest.NewTracerProvider(oteltest.WithSpanRecorder(sr)) + + var gotSpan oteltrace.Span + + app := fiber.New() + app.Use( + Middleware( + WithTracerProvider(provider), + WithCustomAttributes(func(ctx *fiber.Ctx) []attribute.KeyValue { + return []attribute.KeyValue{ + attribute.Key("http.query_params").String(ctx.Request().URI().QueryArgs().String()), + } + }), + ), + ) + + app.Get("/user/:id", func(ctx *fiber.Ctx) error { + gotSpan = oteltrace.SpanFromContext(ctx.UserContext()) + id := ctx.Params("id") + return ctx.SendString(id) + }) + + resp, _ := app.Test(httptest.NewRequest("GET", "/user/123?foo=bar", nil), 3000) + + // do and verify the request + require.Equal(t, http.StatusOK, resp.StatusCode) + + mspan, ok := gotSpan.(*oteltest.Span) + require.True(t, ok) + assert.Equal(t, attribute.StringValue("foo=bar"), mspan.Attributes()["http.query_params"]) + + // verify traces look good + spans := sr.Completed() + require.Len(t, spans, 1) + span := spans[0] + assert.Equal(t, "/user/:id", span.Name()) + assert.Equal(t, oteltrace.SpanKindServer, span.SpanKind()) + assert.Equal(t, attribute.IntValue(http.StatusOK), span.Attributes()["http.status_code"]) + assert.Equal(t, attribute.StringValue("GET"), span.Attributes()["http.method"]) + assert.Equal(t, attribute.StringValue("/user/123?foo=bar"), span.Attributes()["http.target"]) + assert.Equal(t, attribute.StringValue("/user/:id"), span.Attributes()["http.route"]) + assert.Equal(t, attribute.StringValue("foo=bar"), span.Attributes()["http.query_params"]) +} diff --git a/otelfiber/semconv.go b/otelfiber/semconv.go index c0bc8402..e442e47b 100644 --- a/otelfiber/semconv.go +++ b/otelfiber/semconv.go @@ -61,6 +61,10 @@ func httpServerTraceAttributesFromRequest(c *fiber.Ctx, cfg config) []attribute. attrs = append(attrs, semconv.HTTPClientIPKey.String(utils.CopyString(clientIP))) } + if cfg.CustomAttributes != nil { + attrs = append(attrs, cfg.CustomAttributes(c)...) + } + return attrs } From 5294f762f7183b29e471518808ab98de9f6119fa Mon Sep 17 00:00:00 2001 From: kaptinlin Date: Sat, 5 Aug 2023 14:39:44 +0800 Subject: [PATCH 2/2] Update test for otelfiber --- otelfiber/otelfiber_test/fiber_test.go | 32 +++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/otelfiber/otelfiber_test/fiber_test.go b/otelfiber/otelfiber_test/fiber_test.go index 3e5f229f..a42fabc0 100644 --- a/otelfiber/otelfiber_test/fiber_test.go +++ b/otelfiber/otelfiber_test/fiber_test.go @@ -406,16 +406,14 @@ func getHistogram(value float64, attrs []attribute.KeyValue) metricdata.Histogra } func TestCustomAttributes(t *testing.T) { - sr := new(oteltest.SpanRecorder) - provider := oteltest.NewTracerProvider(oteltest.WithSpanRecorder(sr)) - - var gotSpan oteltrace.Span + sr := new(tracetest.SpanRecorder) + provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) app := fiber.New() app.Use( - Middleware( - WithTracerProvider(provider), - WithCustomAttributes(func(ctx *fiber.Ctx) []attribute.KeyValue { + otelfiber.Middleware( + otelfiber.WithTracerProvider(provider), + otelfiber.WithCustomAttributes(func(ctx *fiber.Ctx) []attribute.KeyValue { return []attribute.KeyValue{ attribute.Key("http.query_params").String(ctx.Request().URI().QueryArgs().String()), } @@ -424,7 +422,6 @@ func TestCustomAttributes(t *testing.T) { ) app.Get("/user/:id", func(ctx *fiber.Ctx) error { - gotSpan = oteltrace.SpanFromContext(ctx.UserContext()) id := ctx.Params("id") return ctx.SendString(id) }) @@ -434,19 +431,18 @@ func TestCustomAttributes(t *testing.T) { // do and verify the request require.Equal(t, http.StatusOK, resp.StatusCode) - mspan, ok := gotSpan.(*oteltest.Span) - require.True(t, ok) - assert.Equal(t, attribute.StringValue("foo=bar"), mspan.Attributes()["http.query_params"]) + spans := sr.Ended() + require.Len(t, spans, 1) // verify traces look good - spans := sr.Completed() - require.Len(t, spans, 1) span := spans[0] + attr := span.Attributes() + assert.Equal(t, "/user/:id", span.Name()) assert.Equal(t, oteltrace.SpanKindServer, span.SpanKind()) - assert.Equal(t, attribute.IntValue(http.StatusOK), span.Attributes()["http.status_code"]) - assert.Equal(t, attribute.StringValue("GET"), span.Attributes()["http.method"]) - assert.Equal(t, attribute.StringValue("/user/123?foo=bar"), span.Attributes()["http.target"]) - assert.Equal(t, attribute.StringValue("/user/:id"), span.Attributes()["http.route"]) - assert.Equal(t, attribute.StringValue("foo=bar"), span.Attributes()["http.query_params"]) + assert.Contains(t, attr, attribute.Int("http.status_code", http.StatusOK)) + assert.Contains(t, attr, attribute.String("http.method", "GET")) + assert.Contains(t, attr, attribute.String("http.target", "/user/123?foo=bar")) + assert.Contains(t, attr, attribute.String("http.route", "/user/:id")) + assert.Contains(t, attr, attribute.String("http.query_params", "foo=bar")) }