Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/component/otelcol/receiver/prometheus/internal/metricfamily_test.go
5460 views
1
// Copyright The OpenTelemetry Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
package internal
16
17
import (
18
"math"
19
"testing"
20
"time"
21
22
"github.com/prometheus/prometheus/model/labels"
23
"github.com/prometheus/prometheus/model/textparse"
24
"github.com/prometheus/prometheus/model/value"
25
"github.com/prometheus/prometheus/scrape"
26
"github.com/stretchr/testify/require"
27
"go.opentelemetry.io/collector/pdata/pcommon"
28
"go.opentelemetry.io/collector/pdata/pmetric"
29
"go.uber.org/zap"
30
)
31
32
type testMetadataStore map[string]scrape.MetricMetadata
33
34
func (tmc testMetadataStore) GetMetadata(familyName string) (scrape.MetricMetadata, bool) {
35
lookup, ok := tmc[familyName]
36
return lookup, ok
37
}
38
39
func (tmc testMetadataStore) ListMetadata() []scrape.MetricMetadata { return nil }
40
41
func (tmc testMetadataStore) SizeMetadata() int { return 0 }
42
43
func (tmc testMetadataStore) LengthMetadata() int {
44
return len(tmc)
45
}
46
47
var mc = testMetadataStore{
48
"counter": scrape.MetricMetadata{
49
Metric: "cr",
50
Type: textparse.MetricTypeCounter,
51
Help: "This is some help for a counter",
52
Unit: "By",
53
},
54
"gauge": scrape.MetricMetadata{
55
Metric: "ge",
56
Type: textparse.MetricTypeGauge,
57
Help: "This is some help for a gauge",
58
Unit: "1",
59
},
60
"gaugehistogram": scrape.MetricMetadata{
61
Metric: "gh",
62
Type: textparse.MetricTypeGaugeHistogram,
63
Help: "This is some help for a gauge histogram",
64
Unit: "?",
65
},
66
"histogram": scrape.MetricMetadata{
67
Metric: "hg",
68
Type: textparse.MetricTypeHistogram,
69
Help: "This is some help for a histogram",
70
Unit: "ms",
71
},
72
"histogram_stale": scrape.MetricMetadata{
73
Metric: "hg_stale",
74
Type: textparse.MetricTypeHistogram,
75
Help: "This is some help for a histogram",
76
Unit: "ms",
77
},
78
"summary": scrape.MetricMetadata{
79
Metric: "s",
80
Type: textparse.MetricTypeSummary,
81
Help: "This is some help for a summary",
82
Unit: "ms",
83
},
84
"summary_stale": scrape.MetricMetadata{
85
Metric: "s_stale",
86
Type: textparse.MetricTypeSummary,
87
Help: "This is some help for a summary",
88
Unit: "ms",
89
},
90
"unknown": scrape.MetricMetadata{
91
Metric: "u",
92
Type: textparse.MetricTypeUnknown,
93
Help: "This is some help for an unknown metric",
94
Unit: "?",
95
},
96
}
97
98
func TestMetricGroupData_toDistributionUnitTest(t *testing.T) {
99
type scrape struct {
100
at int64
101
value float64
102
metric string
103
extraLabel labels.Label
104
}
105
tests := []struct {
106
name string
107
metricName string
108
labels labels.Labels
109
scrapes []*scrape
110
want func() pmetric.HistogramDataPoint
111
wantErr bool
112
intervalStartTimeMs int64
113
}{
114
{
115
name: "histogram with startTimestamp",
116
metricName: "histogram",
117
intervalStartTimeMs: 11,
118
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
119
scrapes: []*scrape{
120
{at: 11, value: 66, metric: "histogram_count"},
121
{at: 11, value: 1004.78, metric: "histogram_sum"},
122
{at: 11, value: 33, metric: "histogram_bucket", extraLabel: labels.Label{Name: "le", Value: "0.75"}},
123
{at: 11, value: 55, metric: "histogram_bucket", extraLabel: labels.Label{Name: "le", Value: "2.75"}},
124
{at: 11, value: 66, metric: "histogram_bucket", extraLabel: labels.Label{Name: "le", Value: "+Inf"}},
125
},
126
want: func() pmetric.HistogramDataPoint {
127
point := pmetric.NewHistogramDataPoint()
128
point.SetCount(66)
129
point.SetSum(1004.78)
130
point.SetTimestamp(pcommon.Timestamp(11 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
131
point.ExplicitBounds().FromRaw([]float64{0.75, 2.75})
132
point.BucketCounts().FromRaw([]uint64{33, 22, 11})
133
point.SetStartTimestamp(pcommon.Timestamp(11 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
134
attributes := point.Attributes()
135
attributes.PutStr("a", "A")
136
attributes.PutStr("b", "B")
137
return point
138
},
139
},
140
{
141
name: "histogram that is stale",
142
metricName: "histogram_stale",
143
intervalStartTimeMs: 11,
144
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
145
scrapes: []*scrape{
146
{at: 11, value: math.Float64frombits(value.StaleNaN), metric: "histogram_stale_count"},
147
{at: 11, value: math.Float64frombits(value.StaleNaN), metric: "histogram_stale_sum"},
148
{at: 11, value: math.Float64frombits(value.StaleNaN), metric: "histogram_bucket", extraLabel: labels.Label{Name: "le", Value: "0.75"}},
149
{at: 11, value: math.Float64frombits(value.StaleNaN), metric: "histogram_bucket", extraLabel: labels.Label{Name: "le", Value: "2.75"}},
150
{at: 11, value: math.Float64frombits(value.StaleNaN), metric: "histogram_bucket", extraLabel: labels.Label{Name: "le", Value: "+Inf"}},
151
},
152
want: func() pmetric.HistogramDataPoint {
153
point := pmetric.NewHistogramDataPoint()
154
point.SetTimestamp(pcommon.Timestamp(11 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
155
point.SetFlags(pmetric.DefaultDataPointFlags.WithNoRecordedValue(true))
156
point.ExplicitBounds().FromRaw([]float64{0.75, 2.75})
157
point.BucketCounts().FromRaw([]uint64{0, 0, 0})
158
point.SetStartTimestamp(pcommon.Timestamp(11 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
159
attributes := point.Attributes()
160
attributes.PutStr("a", "A")
161
attributes.PutStr("b", "B")
162
return point
163
},
164
},
165
{
166
name: "histogram with inconsistent timestamps",
167
metricName: "histogram_inconsistent_ts",
168
intervalStartTimeMs: 11,
169
labels: labels.FromMap(map[string]string{"a": "A", "le": "0.75", "b": "B"}),
170
scrapes: []*scrape{
171
{at: 11, value: math.Float64frombits(value.StaleNaN), metric: "histogram_stale_count"},
172
{at: 12, value: math.Float64frombits(value.StaleNaN), metric: "histogram_stale_sum"},
173
{at: 13, value: math.Float64frombits(value.StaleNaN), metric: "value"},
174
},
175
wantErr: true,
176
},
177
}
178
179
for _, tt := range tests {
180
tt := tt
181
t.Run(tt.name, func(t *testing.T) {
182
mp := newMetricFamily(tt.metricName, mc, zap.NewNop())
183
for i, tv := range tt.scrapes {
184
var lbls labels.Labels
185
if tv.extraLabel.Name != "" {
186
lbls = labels.NewBuilder(tt.labels).Set(tv.extraLabel.Name, tv.extraLabel.Value).Labels(lbls)
187
} else {
188
lbls = tt.labels.Copy()
189
}
190
err := mp.Add(tv.metric, lbls, tv.at, tv.value)
191
if tt.wantErr {
192
if i != 0 {
193
require.Error(t, err)
194
}
195
} else {
196
require.NoError(t, err)
197
}
198
}
199
if tt.wantErr {
200
// Don't check the result if we got an error
201
return
202
}
203
204
require.Len(t, mp.groups, 1)
205
groupKey := mp.getGroupKey(tt.labels.Copy())
206
require.NotNil(t, mp.groups[groupKey])
207
208
sl := pmetric.NewMetricSlice()
209
mp.appendMetric(sl)
210
211
require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
212
metric := sl.At(0)
213
require.Equal(t, mc[tt.metricName].Help, metric.Description(), "Expected help metadata in metric description")
214
require.Equal(t, mc[tt.metricName].Unit, metric.Unit(), "Expected unit metadata in metric")
215
216
hdpL := metric.Histogram().DataPoints()
217
require.Equal(t, 1, hdpL.Len(), "Exactly one point expected")
218
got := hdpL.At(0)
219
want := tt.want()
220
require.Equal(t, want, got, "Expected the points to be equal")
221
})
222
}
223
}
224
225
func TestMetricGroupData_toSummaryUnitTest(t *testing.T) {
226
type scrape struct {
227
at int64
228
value float64
229
metric string
230
}
231
232
type labelsScrapes struct {
233
labels labels.Labels
234
scrapes []*scrape
235
}
236
tests := []struct {
237
name string
238
labelsScrapes []*labelsScrapes
239
want func() pmetric.SummaryDataPoint
240
wantErr bool
241
}{
242
{
243
name: "summary",
244
labelsScrapes: []*labelsScrapes{
245
{
246
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
247
scrapes: []*scrape{
248
{at: 14, value: 10, metric: "summary_count"},
249
{at: 14, value: 15, metric: "summary_sum"},
250
},
251
},
252
{
253
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.0", "b": "B"}),
254
scrapes: []*scrape{
255
{at: 14, value: 8, metric: "value"},
256
},
257
},
258
{
259
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.75", "b": "B"}),
260
scrapes: []*scrape{
261
{at: 14, value: 33.7, metric: "value"},
262
},
263
},
264
{
265
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.50", "b": "B"}),
266
scrapes: []*scrape{
267
{at: 14, value: 27, metric: "value"},
268
},
269
},
270
{
271
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.90", "b": "B"}),
272
scrapes: []*scrape{
273
{at: 14, value: 56, metric: "value"},
274
},
275
},
276
{
277
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.99", "b": "B"}),
278
scrapes: []*scrape{
279
{at: 14, value: 82, metric: "value"},
280
},
281
},
282
},
283
want: func() pmetric.SummaryDataPoint {
284
point := pmetric.NewSummaryDataPoint()
285
point.SetCount(10)
286
point.SetSum(15)
287
qtL := point.QuantileValues()
288
qn0 := qtL.AppendEmpty()
289
qn0.SetQuantile(0)
290
qn0.SetValue(8)
291
qn50 := qtL.AppendEmpty()
292
qn50.SetQuantile(.5)
293
qn50.SetValue(27)
294
qn75 := qtL.AppendEmpty()
295
qn75.SetQuantile(.75)
296
qn75.SetValue(33.7)
297
qn90 := qtL.AppendEmpty()
298
qn90.SetQuantile(.9)
299
qn90.SetValue(56)
300
qn99 := qtL.AppendEmpty()
301
qn99.SetQuantile(.99)
302
qn99.SetValue(82)
303
point.SetTimestamp(pcommon.Timestamp(14 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
304
point.SetStartTimestamp(pcommon.Timestamp(14 * time.Millisecond)) // the time in milliseconds -> nanoseconds
305
attributes := point.Attributes()
306
attributes.PutStr("a", "A")
307
attributes.PutStr("b", "B")
308
return point
309
},
310
},
311
{
312
name: "summary_stale",
313
labelsScrapes: []*labelsScrapes{
314
{
315
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.0", "b": "B"}),
316
scrapes: []*scrape{
317
{at: 14, value: 10, metric: "summary_stale_count"},
318
{at: 14, value: 12, metric: "summary_stale_sum"},
319
{at: 14, value: 8, metric: "value"},
320
},
321
},
322
{
323
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.75", "b": "B"}),
324
scrapes: []*scrape{
325
{at: 14, value: 10, metric: "summary_stale_count"},
326
{at: 14, value: 1004.78, metric: "summary_stale_sum"},
327
{at: 14, value: 33.7, metric: "value"},
328
},
329
},
330
{
331
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.50", "b": "B"}),
332
scrapes: []*scrape{
333
{at: 14, value: 10, metric: "summary_stale_count"},
334
{at: 14, value: 13, metric: "summary_stale_sum"},
335
{at: 14, value: 27, metric: "value"},
336
},
337
},
338
{
339
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.90", "b": "B"}),
340
scrapes: []*scrape{
341
{at: 14, value: 10, metric: "summary_stale_count"},
342
{at: 14, value: 14, metric: "summary_stale_sum"},
343
{at: 14, value: 56, metric: "value"},
344
},
345
},
346
{
347
labels: labels.FromMap(map[string]string{"a": "A", "quantile": "0.99", "b": "B"}),
348
scrapes: []*scrape{
349
{at: 14, value: math.Float64frombits(value.StaleNaN), metric: "summary_stale_count"},
350
{at: 14, value: math.Float64frombits(value.StaleNaN), metric: "summary_stale_sum"},
351
{at: 14, value: math.Float64frombits(value.StaleNaN), metric: "value"},
352
},
353
},
354
},
355
want: func() pmetric.SummaryDataPoint {
356
point := pmetric.NewSummaryDataPoint()
357
qtL := point.QuantileValues()
358
qn0 := qtL.AppendEmpty()
359
point.SetFlags(pmetric.DefaultDataPointFlags.WithNoRecordedValue(true))
360
qn0.SetQuantile(0)
361
qn0.SetValue(0)
362
qn50 := qtL.AppendEmpty()
363
qn50.SetQuantile(.5)
364
qn50.SetValue(0)
365
qn75 := qtL.AppendEmpty()
366
qn75.SetQuantile(.75)
367
qn75.SetValue(0)
368
qn90 := qtL.AppendEmpty()
369
qn90.SetQuantile(.9)
370
qn90.SetValue(0)
371
qn99 := qtL.AppendEmpty()
372
qn99.SetQuantile(.99)
373
qn99.SetValue(0)
374
point.SetTimestamp(pcommon.Timestamp(14 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
375
point.SetStartTimestamp(pcommon.Timestamp(14 * time.Millisecond)) // the time in milliseconds -> nanoseconds
376
attributes := point.Attributes()
377
attributes.PutStr("a", "A")
378
attributes.PutStr("b", "B")
379
return point
380
},
381
},
382
{
383
name: "summary with inconsistent timestamps",
384
labelsScrapes: []*labelsScrapes{
385
{
386
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
387
scrapes: []*scrape{
388
{at: 11, value: 10, metric: "summary_count"},
389
{at: 14, value: 15, metric: "summary_sum"},
390
},
391
},
392
},
393
wantErr: true,
394
},
395
}
396
397
for _, tt := range tests {
398
tt := tt
399
t.Run(tt.name, func(t *testing.T) {
400
mp := newMetricFamily(tt.name, mc, zap.NewNop())
401
for _, lbs := range tt.labelsScrapes {
402
for i, scrape := range lbs.scrapes {
403
err := mp.Add(scrape.metric, lbs.labels.Copy(), scrape.at, scrape.value)
404
if tt.wantErr {
405
// The first scrape won't have an error
406
if i != 0 {
407
require.Error(t, err)
408
}
409
} else {
410
require.NoError(t, err)
411
}
412
}
413
}
414
if tt.wantErr {
415
// Don't check the result if we got an error
416
return
417
}
418
419
require.Len(t, mp.groups, 1)
420
groupKey := mp.getGroupKey(tt.labelsScrapes[0].labels.Copy())
421
require.NotNil(t, mp.groups[groupKey])
422
423
sl := pmetric.NewMetricSlice()
424
mp.appendMetric(sl)
425
426
require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
427
metric := sl.At(0)
428
require.Equal(t, mc[tt.name].Help, metric.Description(), "Expected help metadata in metric description")
429
require.Equal(t, mc[tt.name].Unit, metric.Unit(), "Expected unit metadata in metric")
430
431
sdpL := metric.Summary().DataPoints()
432
require.Equal(t, 1, sdpL.Len(), "Exactly one point expected")
433
got := sdpL.At(0)
434
want := tt.want()
435
require.Equal(t, want, got, "Expected the points to be equal")
436
})
437
}
438
}
439
440
func TestMetricGroupData_toNumberDataUnitTest(t *testing.T) {
441
type scrape struct {
442
at int64
443
value float64
444
metric string
445
}
446
tests := []struct {
447
name string
448
metricKind string
449
labels labels.Labels
450
scrapes []*scrape
451
intervalStartTimestampMs int64
452
want func() pmetric.NumberDataPoint
453
}{
454
{
455
metricKind: "counter",
456
name: "counter:: startTimestampMs of 11",
457
intervalStartTimestampMs: 11,
458
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
459
scrapes: []*scrape{
460
{at: 13, value: 33.7, metric: "value"},
461
},
462
want: func() pmetric.NumberDataPoint {
463
point := pmetric.NewNumberDataPoint()
464
point.SetDoubleValue(33.7)
465
point.SetTimestamp(pcommon.Timestamp(13 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
466
point.SetStartTimestamp(pcommon.Timestamp(13 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
467
attributes := point.Attributes()
468
attributes.PutStr("a", "A")
469
attributes.PutStr("b", "B")
470
return point
471
},
472
},
473
{
474
name: "counter:: startTimestampMs of 0",
475
metricKind: "counter",
476
intervalStartTimestampMs: 0,
477
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
478
scrapes: []*scrape{
479
{at: 28, value: 99.9, metric: "value"},
480
},
481
want: func() pmetric.NumberDataPoint {
482
point := pmetric.NewNumberDataPoint()
483
point.SetDoubleValue(99.9)
484
point.SetTimestamp(pcommon.Timestamp(28 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
485
point.SetStartTimestamp(pcommon.Timestamp(28 * time.Millisecond)) // the time in milliseconds -> nanoseconds.
486
attributes := point.Attributes()
487
attributes.PutStr("a", "A")
488
attributes.PutStr("b", "B")
489
return point
490
},
491
},
492
}
493
494
for _, tt := range tests {
495
tt := tt
496
t.Run(tt.name, func(t *testing.T) {
497
mp := newMetricFamily(tt.metricKind, mc, zap.NewNop())
498
for _, tv := range tt.scrapes {
499
require.NoError(t, mp.Add(tv.metric, tt.labels.Copy(), tv.at, tv.value))
500
}
501
502
require.Len(t, mp.groups, 1)
503
groupKey := mp.getGroupKey(tt.labels.Copy())
504
require.NotNil(t, mp.groups[groupKey])
505
506
sl := pmetric.NewMetricSlice()
507
mp.appendMetric(sl)
508
509
require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
510
metric := sl.At(0)
511
require.Equal(t, mc[tt.metricKind].Help, metric.Description(), "Expected help metadata in metric description")
512
require.Equal(t, mc[tt.metricKind].Unit, metric.Unit(), "Expected unit metadata in metric")
513
514
ndpL := metric.Sum().DataPoints()
515
require.Equal(t, 1, ndpL.Len(), "Exactly one point expected")
516
got := ndpL.At(0)
517
want := tt.want()
518
require.Equal(t, want, got, "Expected the points to be equal")
519
})
520
}
521
}
522
523