@@ -18,14 +18,18 @@ import (
1818 "log"
1919 "net/http"
2020 "net/http/httptest"
21+ "reflect"
22+ "sort"
2123 "strings"
2224 "testing"
2325 "time"
2426
27+ "github.com/golang/protobuf/proto"
2528 "github.com/prometheus/client_golang/prometheus"
29+ dto "github.com/prometheus/client_model/go"
2630)
2731
28- func makeInstrumentedClient () (* http.Client , * prometheus.Registry ) {
32+ func makeInstrumentedClient (opts ... Option ) (* http.Client , * prometheus.Registry ) {
2933 client := http .DefaultClient
3034 client .Timeout = 1 * time .Second
3135
@@ -91,13 +95,91 @@ func makeInstrumentedClient() (*http.Client, *prometheus.Registry) {
9195 client .Transport = InstrumentRoundTripperInFlight (inFlightGauge ,
9296 InstrumentRoundTripperCounter (counter ,
9397 InstrumentRoundTripperTrace (trace ,
94- InstrumentRoundTripperDuration (histVec , http .DefaultTransport ),
98+ InstrumentRoundTripperDuration (histVec , http .DefaultTransport , opts ... ),
9599 ),
96- ),
100+ opts ... ),
97101 )
98102 return client , reg
99103}
100104
105+ func labelsToLabelPair (l prometheus.Labels ) []* dto.LabelPair {
106+ ret := make ([]* dto.LabelPair , 0 , len (l ))
107+ for k , v := range l {
108+ ret = append (ret , & dto.LabelPair {Name : proto .String (k ), Value : proto .String (v )})
109+ }
110+ sort .Slice (ret , func (i , j int ) bool {
111+ return * ret [i ].Name < * ret [j ].Name
112+ })
113+ return ret
114+ }
115+
116+ func assetMetricAndExemplars (
117+ t * testing.T ,
118+ reg * prometheus.Registry ,
119+ expectedNumMetrics int ,
120+ expectedExemplar []* dto.LabelPair ,
121+ ) {
122+ t .Helper ()
123+
124+ mfs , err := reg .Gather ()
125+ if err != nil {
126+ t .Fatal (err )
127+ }
128+ if want , got := expectedNumMetrics , len (mfs ); want != got {
129+ t .Fatalf ("unexpected number of metric families gathered, want %d, got %d" , want , got )
130+ }
131+
132+ for _ , mf := range mfs {
133+ if len (mf .Metric ) == 0 {
134+ t .Errorf ("metric family %s must not be empty" , mf .GetName ())
135+ }
136+ for _ , m := range mf .GetMetric () {
137+ if c := m .GetCounter (); c != nil {
138+ if len (expectedExemplar ) == 0 {
139+ if c .Exemplar != nil {
140+ t .Errorf ("expected no exemplar on the counter %v%v, got %v" , mf .GetName (), m .Label , c .Exemplar .String ())
141+ }
142+ continue
143+ }
144+
145+ if c .Exemplar == nil {
146+ t .Errorf ("expected exemplar %v on the counter %v%v, got none" , expectedExemplar , mf .GetName (), m .Label )
147+ continue
148+ }
149+ if got := c .Exemplar .Label ; ! reflect .DeepEqual (expectedExemplar , got ) {
150+ t .Errorf ("expected exemplar %v on the counter %v%v, got %v" , expectedExemplar , mf .GetName (), m .Label , got )
151+ }
152+ continue
153+ }
154+ if h := m .GetHistogram (); h != nil {
155+ found := false
156+ for _ , b := range h .GetBucket () {
157+ if len (expectedExemplar ) == 0 {
158+ if b .Exemplar != nil {
159+ t .Errorf ("expected no exemplar on histogram %v%v bkt %v, got %v" , mf .GetName (), m .Label , b .GetUpperBound (), b .Exemplar .String ())
160+ }
161+ continue
162+ }
163+
164+ if b .Exemplar == nil {
165+ continue
166+ }
167+ if got := b .Exemplar .Label ; ! reflect .DeepEqual (expectedExemplar , got ) {
168+ t .Errorf ("expected exemplar %v on the histogram %v%v on bkt %v, got %v" , expectedExemplar , mf .GetName (), m .Label , b .GetUpperBound (), got )
169+ continue
170+ }
171+ found = true
172+ break
173+ }
174+
175+ if len (expectedExemplar ) > 0 && ! found {
176+ t .Errorf ("expected exemplar %v on at least one bucket of the histogram %v%v, got none" , expectedExemplar , mf .GetName (), m .Label )
177+ }
178+ }
179+ }
180+ }
181+ }
182+
101183func TestClientMiddlewareAPI (t * testing.T ) {
102184 client , reg := makeInstrumentedClient ()
103185 backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
@@ -111,21 +193,28 @@ func TestClientMiddlewareAPI(t *testing.T) {
111193 }
112194 defer resp .Body .Close ()
113195
114- mfs , err := reg .Gather ()
196+ assetMetricAndExemplars (t , reg , 3 , nil )
197+ }
198+
199+ func TestClientMiddlewareAPI_WithExemplars (t * testing.T ) {
200+ exemplar := prometheus.Labels {"traceID" : "example situation observed by this metric" }
201+
202+ client , reg := makeInstrumentedClient (WithExemplarFromContext (func (_ context.Context ) prometheus.Labels { return exemplar }))
203+ backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
204+ w .WriteHeader (http .StatusOK )
205+ }))
206+ defer backend .Close ()
207+
208+ resp , err := client .Get (backend .URL )
115209 if err != nil {
116210 t .Fatal (err )
117211 }
118- if want , got := 3 , len (mfs ); want != got {
119- t .Fatalf ("unexpected number of metric families gathered, want %d, got %d" , want , got )
120- }
121- for _ , mf := range mfs {
122- if len (mf .Metric ) == 0 {
123- t .Errorf ("metric family %s must not be empty" , mf .GetName ())
124- }
125- }
212+ defer resp .Body .Close ()
213+
214+ assetMetricAndExemplars (t , reg , 3 , labelsToLabelPair (exemplar ))
126215}
127216
128- func TestClientMiddlewareAPIWithRequestContext (t * testing.T ) {
217+ func TestClientMiddlewareAPI_WithRequestContext (t * testing.T ) {
129218 client , reg := makeInstrumentedClient ()
130219 backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
131220 w .WriteHeader (http .StatusOK )
0 commit comments