@@ -18,14 +18,19 @@ import (
1818 "log"
1919 "net/http"
2020 "net/http/httptest"
21+ "reflect"
22+ "sort"
2123 "strings"
2224 "testing"
2325 "time"
2426
2527 "github.com/prometheus/client_golang/prometheus"
28+
29+ dto "github.com/prometheus/client_model/go"
30+ "google.golang.org/protobuf/proto"
2631)
2732
28- func makeInstrumentedClient () (* http.Client , * prometheus.Registry ) {
33+ func makeInstrumentedClient (opts ... Option ) (* http.Client , * prometheus.Registry ) {
2934 client := http .DefaultClient
3035 client .Timeout = 1 * time .Second
3136
@@ -91,13 +96,91 @@ func makeInstrumentedClient() (*http.Client, *prometheus.Registry) {
9196 client .Transport = InstrumentRoundTripperInFlight (inFlightGauge ,
9297 InstrumentRoundTripperCounter (counter ,
9398 InstrumentRoundTripperTrace (trace ,
94- InstrumentRoundTripperDuration (histVec , http .DefaultTransport ),
99+ InstrumentRoundTripperDuration (histVec , http .DefaultTransport , opts ... ),
95100 ),
96- ),
101+ opts ... ),
97102 )
98103 return client , reg
99104}
100105
106+ func labelsToLabelPair (l prometheus.Labels ) []* dto.LabelPair {
107+ ret := make ([]* dto.LabelPair , 0 , len (l ))
108+ for k , v := range l {
109+ ret = append (ret , & dto.LabelPair {Name : proto .String (k ), Value : proto .String (v )})
110+ }
111+ sort .Slice (ret , func (i , j int ) bool {
112+ return * ret [i ].Name < * ret [j ].Name
113+ })
114+ return ret
115+ }
116+
117+ func assetMetricAndExemplars (
118+ t * testing.T ,
119+ reg * prometheus.Registry ,
120+ expectedNumMetrics int ,
121+ expectedExemplar []* dto.LabelPair ,
122+ ) {
123+ t .Helper ()
124+
125+ mfs , err := reg .Gather ()
126+ if err != nil {
127+ t .Fatal (err )
128+ }
129+ if want , got := expectedNumMetrics , len (mfs ); want != got {
130+ t .Fatalf ("unexpected number of metric families gathered, want %d, got %d" , want , got )
131+ }
132+
133+ for _ , mf := range mfs {
134+ if len (mf .Metric ) == 0 {
135+ t .Errorf ("metric family %s must not be empty" , mf .GetName ())
136+ }
137+ for _ , m := range mf .GetMetric () {
138+ if c := m .GetCounter (); c != nil {
139+ if len (expectedExemplar ) == 0 {
140+ if c .Exemplar != nil {
141+ t .Errorf ("expected no exemplar on the counter %v%v, got %v" , mf .GetName (), m .Label , c .Exemplar .String ())
142+ }
143+ continue
144+ }
145+
146+ if c .Exemplar == nil {
147+ t .Errorf ("expected exemplar %v on the counter %v%v, got none" , expectedExemplar , mf .GetName (), m .Label )
148+ continue
149+ }
150+ if got := c .Exemplar .Label ; ! reflect .DeepEqual (expectedExemplar , got ) {
151+ t .Errorf ("expected exemplar %v on the counter %v%v, got %v" , expectedExemplar , mf .GetName (), m .Label , got )
152+ }
153+ continue
154+ }
155+ if h := m .GetHistogram (); h != nil {
156+ found := false
157+ for _ , b := range h .GetBucket () {
158+ if len (expectedExemplar ) == 0 {
159+ if b .Exemplar != nil {
160+ t .Errorf ("expected no exemplar on histogram %v%v bkt %v, got %v" , mf .GetName (), m .Label , b .GetUpperBound (), b .Exemplar .String ())
161+ }
162+ continue
163+ }
164+
165+ if b .Exemplar == nil {
166+ continue
167+ }
168+ if got := b .Exemplar .Label ; ! reflect .DeepEqual (expectedExemplar , got ) {
169+ t .Errorf ("expected exemplar %v on the histogram %v%v on bkt %v, got %v" , expectedExemplar , mf .GetName (), m .Label , b .GetUpperBound (), got )
170+ continue
171+ }
172+ found = true
173+ break
174+ }
175+
176+ if len (expectedExemplar ) > 0 && ! found {
177+ t .Errorf ("expected exemplar %v on at least one bucket of the histogram %v%v, got none" , expectedExemplar , mf .GetName (), m .Label )
178+ }
179+ }
180+ }
181+ }
182+ }
183+
101184func TestClientMiddlewareAPI (t * testing.T ) {
102185 client , reg := makeInstrumentedClient ()
103186 backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
@@ -111,21 +194,28 @@ func TestClientMiddlewareAPI(t *testing.T) {
111194 }
112195 defer resp .Body .Close ()
113196
114- mfs , err := reg .Gather ()
197+ assetMetricAndExemplars (t , reg , 3 , nil )
198+ }
199+
200+ func TestClientMiddlewareAPI_WithExemplars (t * testing.T ) {
201+ exemplar := prometheus.Labels {"traceID" : "example situation observed by this metric" }
202+
203+ client , reg := makeInstrumentedClient (WithExemplarFromContext (func (_ context.Context ) prometheus.Labels { return exemplar }))
204+ backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
205+ w .WriteHeader (http .StatusOK )
206+ }))
207+ defer backend .Close ()
208+
209+ resp , err := client .Get (backend .URL )
115210 if err != nil {
116211 t .Fatal (err )
117212 }
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- }
213+ defer resp .Body .Close ()
214+
215+ assetMetricAndExemplars (t , reg , 3 , labelsToLabelPair (exemplar ))
126216}
127217
128- func TestClientMiddlewareAPIWithRequestContext (t * testing.T ) {
218+ func TestClientMiddlewareAPI_WithRequestContext (t * testing.T ) {
129219 client , reg := makeInstrumentedClient ()
130220 backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
131221 w .WriteHeader (http .StatusOK )
0 commit comments