Skip to content

Commit

Permalink
added option on http transport for having default attributes to be i…
Browse files Browse the repository at this point in the history
…ncluded in request metric
  • Loading branch information
luca-filipponi committed Jul 9, 2024
1 parent c80e464 commit 977c738
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 17 deletions.
10 changes: 10 additions & 0 deletions instrumentation/net/http/otelhttp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http

import (
"context"
"go.opentelemetry.io/otel/attribute"
"net/http"
"net/http/httptrace"

Expand Down Expand Up @@ -32,6 +33,7 @@ type config struct {
Filters []Filter
SpanNameFormatter func(string, *http.Request) string
ClientTrace func(context.Context) *httptrace.ClientTrace
DefaultAttributes []attribute.KeyValue

TracerProvider trace.TracerProvider
MeterProvider metric.MeterProvider
Expand Down Expand Up @@ -194,3 +196,11 @@ func WithServerName(server string) Option {
c.ServerName = server
})
}

// WithDefaultAttributes returns an option that sets the default attributes to be
// included in metrics.
func WithDefaultAttributes(attributes []attribute.KeyValue) Option {
return optionFunc(func(c *config) {
c.DefaultAttributes = attributes
})
}
87 changes: 71 additions & 16 deletions instrumentation/net/http/otelhttp/test/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,14 @@ func TestCustomAttributesHandling(t *testing.T) {
}))
defer ts.Close()

expectedAttributes := []attribute.KeyValue{
attribute.String("foo", "fooValue"),
attribute.String("bar", "barValue")}

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"))
labeler.Add(expectedAttributes...)
ctx = otelhttp.ContextWithLabeler(ctx, labeler)
r = r.WithContext(ctx)

Expand All @@ -528,30 +531,82 @@ func TestCustomAttributesHandling(t *testing.T) {
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")))
containsAttributes(t, d.DataPoints[0].Attributes, expectedAttributes)
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"))
containsAttributes(t, d.DataPoints[0].Attributes, expectedAttributes)
}
}
}

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

defaultAttributes := []attribute.KeyValue{
attribute.String("defaultFoo", "fooValue"),
attribute.String("defaultBar", "barValue")}

transport := otelhttp.NewTransport(
http.DefaultTransport, otelhttp.WithMeterProvider(provider),
otelhttp.WithDefaultAttributes(defaultAttributes))
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)

_, 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.Equal(t, "fooValue", fooAtrr.AsString())
barAtrr, ok := attrSet.Value(attribute.Key("bar"))
assert.Len(t, d.DataPoints, 1)
containsAttributes(t, d.DataPoints[0].Attributes, defaultAttributes)
case clientDuration:
d, ok := m.Data.(metricdata.Histogram[float64])
assert.True(t, ok)
assert.Equal(t, "barValue", barAtrr.AsString())
assert.False(t, attrSet.HasValue(attribute.Key("baz")))
assert.Len(t, d.DataPoints, 1)
containsAttributes(t, d.DataPoints[0].Attributes, defaultAttributes)
}
}
}

func containsAttributes(t *testing.T, attrSet attribute.Set, expected []attribute.KeyValue) {
for _, att := range expected {
actualValue, ok := attrSet.Value(att.Key)
assert.True(t, ok)
assert.Equal(t, att.Value.AsString(), actualValue.AsString())
}
}

func BenchmarkTransportRoundTrip(b *testing.B) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
Expand Down
4 changes: 3 additions & 1 deletion instrumentation/net/http/otelhttp/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Transport struct {
filters []Filter
spanNameFormatter func(string, *http.Request) string
clientTrace func(context.Context) *httptrace.ClientTrace
defaultAttributes []attribute.KeyValue

requestBytesCounter metric.Int64Counter
responseBytesCounter metric.Int64Counter
Expand Down Expand Up @@ -76,6 +77,7 @@ func (t *Transport) applyConfig(c *config) {
t.filters = c.Filters
t.spanNameFormatter = c.SpanNameFormatter
t.clientTrace = c.ClientTrace
t.defaultAttributes = c.DefaultAttributes
}

func (t *Transport) createMeasures() {
Expand Down Expand Up @@ -167,7 +169,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
}

// metrics
metricAttrs := append(labeler.Get(), semconvutil.HTTPClientRequestMetrics(r)...)
metricAttrs := append(append(labeler.Get(), semconvutil.HTTPClientRequestMetrics(r)...), t.defaultAttributes...)
if res.StatusCode > 0 {
metricAttrs = append(metricAttrs, semconv.HTTPStatusCode(res.StatusCode))
}
Expand Down

0 comments on commit 977c738

Please sign in to comment.