Skip to content

Commit

Permalink
otelfiber: add WithCustomMetricAttrbutes option
Browse files Browse the repository at this point in the history
  • Loading branch information
hwo411 committed Jul 16, 2024
1 parent 906b023 commit c1d9e50
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 11 deletions.
27 changes: 18 additions & 9 deletions otelfiber/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import (

// config is used to configure the Fiber middleware.
type config struct {
Next func(*fiber.Ctx) bool
TracerProvider oteltrace.TracerProvider
MeterProvider otelmetric.MeterProvider
Port *int
Propagators propagation.TextMapPropagator
ServerName *string
SpanNameFormatter func(*fiber.Ctx) string
CustomAttributes func(*fiber.Ctx) []attribute.KeyValue
collectClientIP bool
Next func(*fiber.Ctx) bool
TracerProvider oteltrace.TracerProvider
MeterProvider otelmetric.MeterProvider
Port *int
Propagators propagation.TextMapPropagator
ServerName *string
SpanNameFormatter func(*fiber.Ctx) string
CustomAttributes func(*fiber.Ctx) []attribute.KeyValue
CustomMetricAttributes func(*fiber.Ctx) []attribute.KeyValue
collectClientIP bool
}

// Option specifies instrumentation configuration options.
Expand Down Expand Up @@ -98,6 +99,14 @@ func WithCustomAttributes(f func(ctx *fiber.Ctx) []attribute.KeyValue) Option {
})
}

// WithCustomMetricAttributes specifies a function that will be called on every
// request and the returned attributes will be added to the metrics.
func WithCustomMetricAttributes(f func(ctx *fiber.Ctx) []attribute.KeyValue) Option {
return optionFunc(func(cfg *config) {
cfg.CustomMetricAttributes = f
})
}

// WithCollectClientIP specifies whether to collect the client's IP address
// from the request. This is enabled by default.
func WithCollectClientIP(collect bool) Option {
Expand Down
4 changes: 2 additions & 2 deletions otelfiber/fiber.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ func Middleware(opts ...Option) fiber.Handler {
semconv.HTTPAttributesFromHTTPStatusCode(c.Response().StatusCode()),
semconv.HTTPRouteKey.String(c.Route().Path), // no need to copy c.Route().Path: route strings should be immutable across app lifecycle
)

var responseSize int64
requestSize := int64(len(c.Request().Body()))
if c.GetRespHeader("Content-Type") != "text/event-stream" {
responseSize = int64(len(c.Response().Body()))
}

defer func() {
responseMetricAttrs = append(
responseMetricAttrs,
Expand Down
54 changes: 54 additions & 0 deletions otelfiber/otelfiber_test/fiber_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,60 @@ func TestCustomAttributes(t *testing.T) {
assert.Contains(t, attr, attribute.String("http.query_params", "foo=bar"))
}

func TestCustomMetricAttributes(t *testing.T) {
reader := metric.NewManualReader()
provider := metric.NewMeterProvider(metric.WithReader(reader))

serverName := "foobar"
port := 8080
route := "/foo"

app := fiber.New()
app.Use(
otelfiber.Middleware(
otelfiber.WithMeterProvider(provider),
otelfiber.WithPort(port),
otelfiber.WithServerName(serverName),
otelfiber.WithCustomMetricAttributes(func(ctx *fiber.Ctx) []attribute.KeyValue {
return []attribute.KeyValue{
attribute.Key("http.query_params").String(ctx.Request().URI().QueryArgs().String()),
}
}),
),
)

app.Get(route, func(ctx *fiber.Ctx) error {
return ctx.SendStatus(http.StatusOK)
})

r := httptest.NewRequest(http.MethodGet, "/foo?foo=bar", nil)
resp, _ := app.Test(r)

// do and verify the request
require.Equal(t, http.StatusOK, resp.StatusCode)

metrics := metricdata.ResourceMetrics{}
err := reader.Collect(context.Background(), &metrics)
assert.NoError(t, err)
assert.Len(t, metrics.ScopeMetrics, 1)

requestAttrs := []attribute.KeyValue{
semconv.HTTPFlavorKey.String(fmt.Sprintf("1.%d", r.ProtoMinor)),
semconv.HTTPMethodKey.String(http.MethodGet),
semconv.HTTPSchemeHTTP,
semconv.NetHostNameKey.String(r.Host),
semconv.NetHostPortKey.Int(port),
semconv.HTTPServerNameKey.String(serverName),
attribute.String("http.query_params", "foo=bar"),
}
responseAttrs := append(
semconv.HTTPAttributesFromHTTPStatusCode(200),
semconv.HTTPRouteKey.String(route),
)

assertScopeMetrics(t, metrics.ScopeMetrics[0], route, requestAttrs, append(requestAttrs, responseAttrs...))
}

func TestOutboundTracingPropagation(t *testing.T) {
sr := new(tracetest.SpanRecorder)
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr))
Expand Down
4 changes: 4 additions & 0 deletions otelfiber/semconv.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func httpServerMetricAttributesFromRequest(c *fiber.Ctx, cfg config) []attribute
attrs = append(attrs, semconv.HTTPServerNameKey.String(*cfg.ServerName))
}

if cfg.CustomMetricAttributes != nil {
attrs = append(attrs, cfg.CustomMetricAttributes(c)...)
}

return attrs
}

Expand Down

0 comments on commit c1d9e50

Please sign in to comment.