@@ -18,14 +18,18 @@ import (
18
18
"log"
19
19
"net/http"
20
20
"net/http/httptest"
21
+ "reflect"
22
+ "sort"
21
23
"strings"
22
24
"testing"
23
25
"time"
24
26
25
27
"github.com/prometheus/client_golang/prometheus"
28
+ dto "github.com/prometheus/client_model/go"
29
+ "google.golang.org/protobuf/proto"
26
30
)
27
31
28
- func makeInstrumentedClient () (* http.Client , * prometheus.Registry ) {
32
+ func makeInstrumentedClient (opts ... Option ) (* http.Client , * prometheus.Registry ) {
29
33
client := http .DefaultClient
30
34
client .Timeout = 1 * time .Second
31
35
@@ -91,13 +95,91 @@ func makeInstrumentedClient() (*http.Client, *prometheus.Registry) {
91
95
client .Transport = InstrumentRoundTripperInFlight (inFlightGauge ,
92
96
InstrumentRoundTripperCounter (counter ,
93
97
InstrumentRoundTripperTrace (trace ,
94
- InstrumentRoundTripperDuration (histVec , http .DefaultTransport ),
98
+ InstrumentRoundTripperDuration (histVec , http .DefaultTransport , opts ... ),
95
99
),
96
- ),
100
+ opts ... ),
97
101
)
98
102
return client , reg
99
103
}
100
104
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
+
101
183
func TestClientMiddlewareAPI (t * testing.T ) {
102
184
client , reg := makeInstrumentedClient ()
103
185
backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
@@ -111,21 +193,28 @@ func TestClientMiddlewareAPI(t *testing.T) {
111
193
}
112
194
defer resp .Body .Close ()
113
195
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 )
115
209
if err != nil {
116
210
t .Fatal (err )
117
211
}
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 ))
126
215
}
127
216
128
- func TestClientMiddlewareAPIWithRequestContext (t * testing.T ) {
217
+ func TestClientMiddlewareAPI_WithRequestContext (t * testing.T ) {
129
218
client , reg := makeInstrumentedClient ()
130
219
backend := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
131
220
w .WriteHeader (http .StatusOK )
0 commit comments