Skip to content

Commit

Permalink
Add tests for active requests
Browse files Browse the repository at this point in the history
  • Loading branch information
hadrienk committed Nov 14, 2023
1 parent 9899fdb commit a54734a
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 12 deletions.
3 changes: 3 additions & 0 deletions instrumentation/net/http/otelhttp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/stretchr/testify v1.8.4
go.opentelemetry.io/otel v1.19.0
go.opentelemetry.io/otel/metric v1.19.0
go.opentelemetry.io/otel/sdk/metric v1.19.0
go.opentelemetry.io/otel/trace v1.19.0
)

Expand All @@ -15,5 +16,7 @@ require (
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/sdk v1.20.0 // indirect
golang.org/x/sys v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
21 changes: 14 additions & 7 deletions instrumentation/net/http/otelhttp/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM=
go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0=
go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY=
go.opentelemetry.io/otel/sdk/metric v1.20.0 h1:5eD40l/H2CqdKmbSV7iht2KMK0faAIL2pVYzJOWobGk=
go.opentelemetry.io/otel/sdk/metric v1.20.0/go.mod h1:AGvpC+YF/jblITiafMTYgvRBUiwi9hZf0EYE2E5XlS8=
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
17 changes: 12 additions & 5 deletions instrumentation/net/http/otelhttp/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ func (h *middleware) createMeasures() {
h.valueRecorders[ServerLatency] = serverLatencyMeasure
}

func httpSchemeFromRequest(r *http.Request) attribute.KeyValue {
if r.TLS != nil {
return semconv.HTTPSchemeHTTPS
} else {
return semconv.HTTPSchemeHTTP
}
}

// serveHTTP sets up tracing and calls the given next http.Handler with the span
// context injected into the request context.
func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http.Handler) {
Expand Down Expand Up @@ -236,17 +244,16 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
labeler := &Labeler{}
ctx = injectLabeler(ctx, labeler)

reqAttrs := semconvutil.HTTPServerRequestMetrics(h.server, r)
ao := metric.WithAttributes(reqAttrs...)
h.upDownCounters[ActiveRequests].Add(ctx, 1, ao)
defer h.upDownCounters[ActiveRequests].Add(ctx, -1, ao)
attrs := metric.WithAttributes(httpSchemeFromRequest(r), semconv.HTTPMethod(r.Method))
h.upDownCounters[ActiveRequests].Add(ctx, 1, attrs)
defer h.upDownCounters[ActiveRequests].Add(ctx, -1, attrs)

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

setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err)

// Add metrics
attributes := append(labeler.Get(), reqAttrs...)
attributes := append(labeler.Get(), semconvutil.HTTPServerRequestMetrics(h.server, r)...)
if rww.statusCode > 0 {
attributes = append(attributes, semconv.HTTPStatusCode(rww.statusCode))
}
Expand Down
92 changes: 92 additions & 0 deletions instrumentation/net/http/otelhttp/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@
package otelhttp_test

import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
"io"
"net/http"
"net/http/httptest"
"slices"
"strings"
"sync"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -27,6 +35,90 @@ import (
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func assertActiveMetric(t *testing.T, ctx context.Context, reader *metric.ManualReader, actual metricdata.Aggregation, opts ...metricdatatest.Option) bool {
var rm metricdata.ResourceMetrics
err := reader.Collect(ctx, &rm)

if !assert.NoError(t, err, "failed to read the metric reader") {
return false
}

if !assert.Len(t, rm.ScopeMetrics, 1, "too many metrics") {
return false
}

metrics := rm.ScopeMetrics[0].Metrics
m := metrics[slices.IndexFunc(metrics, func(m metricdata.Metrics) bool {
return m.Name == otelhttp.ActiveRequests
})]
return metricdatatest.AssertAggregationsEqual(t, actual, m.Data, opts...)
}

func activeMetric(val int64) metricdata.Sum[int64] {
return metricdata.Sum[int64]{
Temporality: metricdata.CumulativeTemporality,
IsMonotonic: false,
DataPoints: []metricdata.DataPoint[int64]{
{
Value: val,
Attributes: attribute.NewSet(
semconv.HTTPMethod("GET"),
semconv.HTTPScheme("http"),
),
},
},
}
}

func TestActiveMetrics(t *testing.T) {
ctx, done := context.WithCancel(context.Background())
defer done()

reader := metric.NewManualReader()
var g sync.WaitGroup
var ag sync.WaitGroup
var l sync.Mutex

handler := otelhttp.NewHandler(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
g.Done()
l.Lock()
defer l.Unlock()
ag.Done()
}), "test", otelhttp.WithMeterProvider(metric.NewMeterProvider(metric.WithReader(reader))))

g.Add(5)
ag.Add(5)
l.Lock()

for i := 0; i < 5; i++ {
go handler.ServeHTTP(
httptest.NewRecorder(),
httptest.NewRequest("GET", "/foo/bar", nil),
)
}

g.Wait()

assertActiveMetric(t, ctx, reader, activeMetric(5), metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars())

g.Add(5)
ag.Add(5)
for i := 0; i < 5; i++ {
go handler.ServeHTTP(
httptest.NewRecorder(),
httptest.NewRequest("GET", "/foo/bar", nil),
)
}
g.Wait()

assertActiveMetric(t, ctx, reader, activeMetric(10), metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars())

l.Unlock()
ag.Wait()

assertActiveMetric(t, ctx, reader, activeMetric(0), metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars())
}

func TestHandler(t *testing.T) {
testCases := []struct {
name string
Expand Down

0 comments on commit a54734a

Please sign in to comment.