Skip to content

Commit

Permalink
Merge pull request #1159 from hwo411/add-custom-metric-attributes
Browse files Browse the repository at this point in the history
otelfiber: add WithCustomMetricAttrbutes option
  • Loading branch information
ReneWerner87 authored Jul 23, 2024
2 parents 1eedcd5 + 8f3bc05 commit 32c12f3
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 22 deletions.
23 changes: 12 additions & 11 deletions otelfiber/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,18 @@ otelfiber.Middleware(opts ...otelfiber.Option) fiber.Handler
You can configure the middleware using functional parameters


| Function | Argument Type | Description | Default |
| :------------------ | :-------------------------------- | :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------- |
| `WithNext` | `func(*fiber.Ctx) bool` | Define a function to skip this middleware when returned true .| nil |
| `WithTracerProvider` | `oteltrace.TracerProvider` | Specifies a tracer provider to use for creating a tracer. | nil - the global tracer provider is used |
| `WithMeterProvider` | `otelmetric.MeterProvider` | Specifies a meter provider to use for reporting. | nil - the global meter provider is used |
| `WithPort` | `int` | Specifies the value to use when setting the `net.host.port` attribute on metrics/spans. | Defaults to (`80` for `http`, `443` for `https`) |
| `WithPropagators` | `propagation.TextMapPropagator` | Specifies propagators to use for extracting information from the HTTP requests. | If none are specified, global ones will be used |
| `WithServerName` | `string` | Specifies the value to use when setting the `http.server_name` attribute on metrics/spans. | - |
| `WithSpanNameFormatter` | `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 |
| `WithCustomAttributes` | `func(*fiber.Ctx) []attribute.KeyValue` | Define a function to add custom attributes to the span. | nil |
| `WithCollectClientIP` | `bool` | Specifies whether to collect the client's IP address from the request. | true |
| Function | Argument Type | Description | Default |
| :------------------------ | :-------------------------------- | :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------- |
| `WithNext` | `func(*fiber.Ctx) bool` | Define a function to skip this middleware when returned true .| nil |
| `WithTracerProvider` | `oteltrace.TracerProvider` | Specifies a tracer provider to use for creating a tracer. | nil - the global tracer provider is used |
| `WithMeterProvider` | `otelmetric.MeterProvider` | Specifies a meter provider to use for reporting. | nil - the global meter provider is used |
| `WithPort` | `int` | Specifies the value to use when setting the `net.host.port` attribute on metrics/spans. | Defaults to (`80` for `http`, `443` for `https`) |
| `WithPropagators` | `propagation.TextMapPropagator` | Specifies propagators to use for extracting information from the HTTP requests. | If none are specified, global ones will be used |
| `WithServerName` | `string` | Specifies the value to use when setting the `http.server_name` attribute on metrics/spans. | - |
| `WithSpanNameFormatter` | `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 |
| `WithCustomAttributes` | `func(*fiber.Ctx) []attribute.KeyValue` | Define a function to add custom attributes to the span. | nil |
| `WithCustomMetricAttributes` | `func(*fiber.Ctx) []attribute.KeyValue` | Define a function to add custom attributes to the metrics. | nil |
| `WithCollectClientIP` | `bool` | Specifies whether to collect the client's IP address from the request. | true |

## Usage

Expand Down
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 32c12f3

Please sign in to comment.