Skip to content

Commit

Permalink
Add route tag to metrics as well as traces when using WithRouteTag.
Browse files Browse the repository at this point in the history
Resolves #611.
  • Loading branch information
charleskorn committed May 28, 2023
1 parent 0a4fc1a commit 4e27aed
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
8 changes: 7 additions & 1 deletion instrumentation/net/http/otelhttp/handler.go
Expand Up @@ -257,8 +257,14 @@ func setAfterServeAttributes(span trace.Span, read, wrote int64, statusCode int,
// RouteKey Tag.
func WithRouteTag(route string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
attr := semconv.HTTPRouteKey.String(route)

span := trace.SpanFromContext(r.Context())
span.SetAttributes(semconv.HTTPRoute(route))
span.SetAttributes(attr)

labeler, _ := LabelerFromContext(r.Context())
labeler.Add(attr)

h.ServeHTTP(w, r)
})
}
51 changes: 51 additions & 0 deletions instrumentation/net/http/otelhttp/test/handler_test.go
Expand Up @@ -468,3 +468,54 @@ func TestSpanStatus(t *testing.T) {
})
}
}

func TestWithRouteTag(t *testing.T) {
route := "/some/route"

spanRecorder := tracetest.NewSpanRecorder()
tracerProvider := sdktrace.NewTracerProvider()
tracerProvider.RegisterSpanProcessor(spanRecorder)

metricReader := metric.NewManualReader()
meterProvider := metric.NewMeterProvider(metric.WithReader(metricReader))

h := otelhttp.NewHandler(
otelhttp.WithRouteTag(
route,
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTeapot)
}),
),
"test_handler",
otelhttp.WithTracerProvider(tracerProvider),
otelhttp.WithMeterProvider(meterProvider),
)

h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil))
expectedAttribute := semconv.HTTPRouteKey.String(route)

require.Len(t, spanRecorder.Ended(), 1, "should emit a span")
span := spanRecorder.Ended()[0]
require.Contains(t, span.Attributes(), expectedAttribute, "should add route to span attributes")

rm := metricdata.ResourceMetrics{}
err := metricReader.Collect(context.Background(), &rm)
require.NoError(t, err)
require.Len(t, rm.ScopeMetrics, 1, "should emit metrics for one scope")
scopeMetrics := rm.ScopeMetrics[0]

for _, m := range scopeMetrics.Metrics {
switch d := m.Data.(type) {
case metricdata.Sum[int64]:
require.Len(t, d.DataPoints, 1, "metric '%v' should have exactly one data point", m.Name)
require.Contains(t, d.DataPoints[0].Attributes.ToSlice(), expectedAttribute, "should add route to attributes for metric '%v'", m.Name)

case metricdata.Histogram[float64]:
require.Len(t, d.DataPoints, 1, "metric '%v' should have exactly one data point", m.Name)
require.Contains(t, d.DataPoints[0].Attributes.ToSlice(), expectedAttribute, "should add route to attributes for metric '%v'", m.Name)

default:
require.Fail(t, "metric has unexpected data type", "metric '%v' has unexpected data type %T", m.Name, m.Data)
}
}
}

0 comments on commit 4e27aed

Please sign in to comment.