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 69f66d2
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108)
- Support for stdoutlog exporter in `go.opentelemetry.io/contrib/config`. (#5850)
- Add macOS ARM64 platform to the compatibility testing suite. (#5868)
- Added option for adding default attributes to otel http transport in `go.opentelemetry.io/contrib/instrumentation/net/http/config`. (#5876)

### Removed

Expand Down
11 changes: 11 additions & 0 deletions instrumentation/net/http/otelhttp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"net/http"
"net/http/httptrace"

"go.opentelemetry.io/otel/attribute"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/propagation"
Expand All @@ -32,6 +34,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 +197,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
})
}
89 changes: 73 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,15 @@ 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 +532,83 @@ 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 69f66d2

Please sign in to comment.