Skip to content

Commit

Permalink
fix: Custom attributes targeting metrics instrumented by the are igno…
Browse files Browse the repository at this point in the history
  • Loading branch information
zailic committed May 21, 2024
1 parent a91e60b commit 45aaa6b
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- The `InterceptorFilter` type in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` is deprecated. (#5196)

### Fixed

- Custom attributes targeting metrics recorded by the `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` are not ignored anymore. (#5129)

## [1.26.0/0.51.0/0.20.0/0.6.0/0.1.0] - 2024-04-24

### Added
Expand Down
6 changes: 4 additions & 2 deletions instrumentation/net/http/otelhttp/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
},
})

labeler := &Labeler{}
ctx = injectLabeler(ctx, labeler)
labeler, found := LabelerFromContext(ctx)
if !found {
ctx = ContextWithLabeler(ctx, labeler)
}

next.ServeHTTP(w, r.WithContext(ctx))

Expand Down
8 changes: 6 additions & 2 deletions instrumentation/net/http/otelhttp/labeler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ type labelerContextKeyType int

const lablelerContextKey labelerContextKeyType = 0

func injectLabeler(ctx context.Context, l *Labeler) context.Context {
return context.WithValue(ctx, lablelerContextKey, l)
// ContextWithLabeler returns a new context with the provided Labeler instance.
// Attributes added to the specified labeler will be injected into metrics
// emitted by the instrumentation. Only one labeller can be injected into the
// context. Injecting it multiple times will override the previous calls.
func ContextWithLabeler(parent context.Context, l *Labeler) context.Context {
return context.WithValue(parent, lablelerContextKey, l)
}

// LabelerFromContext retrieves a Labeler instance from the provided context if
Expand Down
74 changes: 74 additions & 0 deletions instrumentation/net/http/otelhttp/test/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,77 @@ func assertClientScopeMetrics(t *testing.T, sm metricdata.ScopeMetrics, attrs at
}
metricdatatest.AssertEqual(t, want, sm.Metrics[2], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue())
}

func TestCustomAttributesHandling(t *testing.T) {
var rm metricdata.ResourceMetrics
const (
clientRequestSize = "http.client.request.size"
clientDuration = "http.client.duration"
)
ctx := context.TODO()
reader := metric.NewManualReader()
provider := metric.NewMeterProvider(metric.WithReader(reader))
defer func() {
err := provider.Shutdown(ctx)
if err != nil {
t.Errorf("Error shutting down provider: %v", err)
}
}()

transport := otelhttp.NewTransport(http.DefaultTransport, otelhttp.WithMeterProvider(provider))
client := http.Client{Transport: transport}

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()

r, err := http.NewRequest(http.MethodGet, ts.URL, nil)
require.NoError(t, err)
labeler := &otelhttp.Labeler{}
labeler.Add(attribute.String("foo", "fooValue"))
labeler.Add(attribute.String("bar", "barValue"))
ctx = otelhttp.ContextWithLabeler(ctx, labeler)
r = r.WithContext(ctx)

// test bonus: intententionally ignoring response to confirm that
// http.client.response.size metric is not recorded
// by the Transport.RoundTrip logic
_, err = client.Do(r)
require.NoError(t, err)

err = reader.Collect(ctx, &rm)
assert.NoError(t, err)

// http.client.response.size is not recorded so the assert.Len
// above should be 2 instead of 3(test bonus)
assert.Len(t, rm.ScopeMetrics[0].Metrics, 2)
for _, m := range rm.ScopeMetrics[0].Metrics {
switch m.Name {
case clientRequestSize:
d, ok := m.Data.(metricdata.Sum[int64])
assert.True(t, ok)
assert.Len(t, d.DataPoints, 1)
attrSet := d.DataPoints[0].Attributes
fooAtrr, ok := attrSet.Value(attribute.Key("foo"))
assert.True(t, ok)
assert.Equal(t, "fooValue", fooAtrr.AsString())
barAtrr, ok := attrSet.Value(attribute.Key("bar"))
assert.True(t, ok)
assert.Equal(t, "barValue", barAtrr.AsString())
assert.False(t, attrSet.HasValue(attribute.Key("baz")))
case clientDuration:
d, ok := m.Data.(metricdata.Histogram[float64])
assert.True(t, ok)
assert.Len(t, d.DataPoints, 1)
attrSet := d.DataPoints[0].Attributes
fooAtrr, ok := attrSet.Value(attribute.Key("foo"))
assert.True(t, ok)
assert.Equal(t, "fooValue", fooAtrr.AsString())
barAtrr, ok := attrSet.Value(attribute.Key("bar"))
assert.True(t, ok)
assert.Equal(t, "barValue", barAtrr.AsString())
assert.False(t, attrSet.HasValue(attribute.Key("baz")))
}
}
}
6 changes: 4 additions & 2 deletions instrumentation/net/http/otelhttp/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,10 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx))
}

labeler := &Labeler{}
ctx = injectLabeler(ctx, labeler)
labeler, found := LabelerFromContext(ctx)
if !found {
ctx = ContextWithLabeler(ctx, labeler)
}

r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request.

Expand Down

0 comments on commit 45aaa6b

Please sign in to comment.