1
2
3
4
5
6
7
8
9
10
11
12
13
14 package expfmt
15
16 import (
17 "bytes"
18 "math"
19 "strings"
20 "testing"
21
22 "google.golang.org/protobuf/proto"
23
24 dto "github.com/prometheus/client_model/go"
25
26 "github.com/prometheus/common/model"
27 )
28
29 func TestCreate(t *testing.T) {
30 oldDefaultScheme := model.NameEscapingScheme
31 model.NameEscapingScheme = model.NoEscaping
32 defer func() {
33 model.NameEscapingScheme = oldDefaultScheme
34 }()
35
36 scenarios := []struct {
37 in *dto.MetricFamily
38 out string
39 }{
40
41 {
42 in: &dto.MetricFamily{
43 Name: proto.String("name"),
44 Help: proto.String("two-line\n doc str\\ing"),
45 Type: dto.MetricType_COUNTER.Enum(),
46 Metric: []*dto.Metric{
47 {
48 Label: []*dto.LabelPair{
49 {
50 Name: proto.String("labelname"),
51 Value: proto.String("val1"),
52 },
53 {
54 Name: proto.String("basename"),
55 Value: proto.String("basevalue"),
56 },
57 },
58 Counter: &dto.Counter{
59 Value: proto.Float64(math.NaN()),
60 },
61 },
62 {
63 Label: []*dto.LabelPair{
64 {
65 Name: proto.String("labelname"),
66 Value: proto.String("val2"),
67 },
68 {
69 Name: proto.String("basename"),
70 Value: proto.String("basevalue"),
71 },
72 },
73 Counter: &dto.Counter{
74 Value: proto.Float64(.23),
75 },
76 TimestampMs: proto.Int64(1234567890),
77 },
78 },
79 },
80 out: `# HELP name two-line\n doc str\\ing
81 # TYPE name counter
82 name{labelname="val1",basename="basevalue"} NaN
83 name{labelname="val2",basename="basevalue"} 0.23 1234567890
84 `,
85 },
86
87 {
88 in: &dto.MetricFamily{
89 Name: proto.String("gauge_name"),
90 Help: proto.String("gauge\ndoc\nstr\"ing"),
91 Type: dto.MetricType_GAUGE.Enum(),
92 Metric: []*dto.Metric{
93 {
94 Label: []*dto.LabelPair{
95 {
96 Name: proto.String("name_1"),
97 Value: proto.String("val with\nnew line"),
98 },
99 {
100 Name: proto.String("name_2"),
101 Value: proto.String("val with \\backslash and \"quotes\""),
102 },
103 },
104 Gauge: &dto.Gauge{
105 Value: proto.Float64(math.Inf(+1)),
106 },
107 },
108 {
109 Label: []*dto.LabelPair{
110 {
111 Name: proto.String("name_1"),
112 Value: proto.String("Björn"),
113 },
114 {
115 Name: proto.String("name_2"),
116 Value: proto.String("佖佥"),
117 },
118 },
119 Gauge: &dto.Gauge{
120 Value: proto.Float64(3.14e42),
121 },
122 },
123 },
124 },
125 out: `# HELP gauge_name gauge\ndoc\nstr"ing
126 # TYPE gauge_name gauge
127 gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf
128 gauge_name{name_1="Björn",name_2="佖佥"} 3.14e+42
129 `,
130 },
131
132 {
133 in: &dto.MetricFamily{
134 Name: proto.String("gauge.name"),
135 Help: proto.String("gauge\ndoc\nstr\"ing"),
136 Type: dto.MetricType_GAUGE.Enum(),
137 Metric: []*dto.Metric{
138 {
139 Label: []*dto.LabelPair{
140 {
141 Name: proto.String("name.1"),
142 Value: proto.String("val with\nnew line"),
143 },
144 {
145 Name: proto.String("name*2"),
146 Value: proto.String("val with \\backslash and \"quotes\""),
147 },
148 },
149 Gauge: &dto.Gauge{
150 Value: proto.Float64(math.Inf(+1)),
151 },
152 },
153 {
154 Label: []*dto.LabelPair{
155 {
156 Name: proto.String("name.1"),
157 Value: proto.String("Björn"),
158 },
159 {
160 Name: proto.String("name*2"),
161 Value: proto.String("佖佥"),
162 },
163 },
164 Gauge: &dto.Gauge{
165 Value: proto.Float64(3.14e42),
166 },
167 },
168 },
169 },
170 out: `# HELP "gauge.name" gauge\ndoc\nstr"ing
171 # TYPE "gauge.name" gauge
172 {"gauge.name","name.1"="val with\nnew line","name*2"="val with \\backslash and \"quotes\""} +Inf
173 {"gauge.name","name.1"="Björn","name*2"="佖佥"} 3.14e+42
174 `,
175 },
176
177 {
178 in: &dto.MetricFamily{
179 Name: proto.String("untyped_name"),
180 Type: dto.MetricType_UNTYPED.Enum(),
181 Metric: []*dto.Metric{
182 {
183 Untyped: &dto.Untyped{
184 Value: proto.Float64(math.Inf(-1)),
185 },
186 },
187 {
188 Label: []*dto.LabelPair{
189 {
190 Name: proto.String("name_1"),
191 Value: proto.String("value 1"),
192 },
193 },
194 Untyped: &dto.Untyped{
195 Value: proto.Float64(-1.23e-45),
196 },
197 },
198 },
199 },
200 out: `# TYPE untyped_name untyped
201 untyped_name -Inf
202 untyped_name{name_1="value 1"} -1.23e-45
203 `,
204 },
205
206 {
207 in: &dto.MetricFamily{
208 Name: proto.String("summary_name"),
209 Help: proto.String("summary docstring"),
210 Type: dto.MetricType_SUMMARY.Enum(),
211 Metric: []*dto.Metric{
212 {
213 Summary: &dto.Summary{
214 SampleCount: proto.Uint64(42),
215 SampleSum: proto.Float64(-3.4567),
216 Quantile: []*dto.Quantile{
217 {
218 Quantile: proto.Float64(0.5),
219 Value: proto.Float64(-1.23),
220 },
221 {
222 Quantile: proto.Float64(0.9),
223 Value: proto.Float64(.2342354),
224 },
225 {
226 Quantile: proto.Float64(0.99),
227 Value: proto.Float64(0),
228 },
229 },
230 },
231 },
232 {
233 Label: []*dto.LabelPair{
234 {
235 Name: proto.String("name_1"),
236 Value: proto.String("value 1"),
237 },
238 {
239 Name: proto.String("name_2"),
240 Value: proto.String("value 2"),
241 },
242 },
243 Summary: &dto.Summary{
244 SampleCount: proto.Uint64(4711),
245 SampleSum: proto.Float64(2010.1971),
246 Quantile: []*dto.Quantile{
247 {
248 Quantile: proto.Float64(0.5),
249 Value: proto.Float64(1),
250 },
251 {
252 Quantile: proto.Float64(0.9),
253 Value: proto.Float64(2),
254 },
255 {
256 Quantile: proto.Float64(0.99),
257 Value: proto.Float64(3),
258 },
259 },
260 },
261 },
262 },
263 },
264 out: `# HELP summary_name summary docstring
265 # TYPE summary_name summary
266 summary_name{quantile="0.5"} -1.23
267 summary_name{quantile="0.9"} 0.2342354
268 summary_name{quantile="0.99"} 0
269 summary_name_sum -3.4567
270 summary_name_count 42
271 summary_name{name_1="value 1",name_2="value 2",quantile="0.5"} 1
272 summary_name{name_1="value 1",name_2="value 2",quantile="0.9"} 2
273 summary_name{name_1="value 1",name_2="value 2",quantile="0.99"} 3
274 summary_name_sum{name_1="value 1",name_2="value 2"} 2010.1971
275 summary_name_count{name_1="value 1",name_2="value 2"} 4711
276 `,
277 },
278
279 {
280 in: &dto.MetricFamily{
281 Name: proto.String("request_duration_microseconds"),
282 Help: proto.String("The response latency."),
283 Type: dto.MetricType_HISTOGRAM.Enum(),
284 Metric: []*dto.Metric{
285 {
286 Histogram: &dto.Histogram{
287 SampleCount: proto.Uint64(2693),
288 SampleSum: proto.Float64(1756047.3),
289 Bucket: []*dto.Bucket{
290 {
291 UpperBound: proto.Float64(100),
292 CumulativeCount: proto.Uint64(123),
293 },
294 {
295 UpperBound: proto.Float64(120),
296 CumulativeCount: proto.Uint64(412),
297 },
298 {
299 UpperBound: proto.Float64(144),
300 CumulativeCount: proto.Uint64(592),
301 },
302 {
303 UpperBound: proto.Float64(172.8),
304 CumulativeCount: proto.Uint64(1524),
305 },
306 {
307 UpperBound: proto.Float64(math.Inf(+1)),
308 CumulativeCount: proto.Uint64(2693),
309 },
310 },
311 },
312 },
313 },
314 },
315 out: `# HELP request_duration_microseconds The response latency.
316 # TYPE request_duration_microseconds histogram
317 request_duration_microseconds_bucket{le="100"} 123
318 request_duration_microseconds_bucket{le="120"} 412
319 request_duration_microseconds_bucket{le="144"} 592
320 request_duration_microseconds_bucket{le="172.8"} 1524
321 request_duration_microseconds_bucket{le="+Inf"} 2693
322 request_duration_microseconds_sum 1.7560473e+06
323 request_duration_microseconds_count 2693
324 `,
325 },
326
327 {
328 in: &dto.MetricFamily{
329 Name: proto.String("request_duration_microseconds"),
330 Help: proto.String("The response latency."),
331 Type: dto.MetricType_HISTOGRAM.Enum(),
332 Metric: []*dto.Metric{
333 {
334 Histogram: &dto.Histogram{
335 SampleCount: proto.Uint64(2693),
336 SampleSum: proto.Float64(1756047.3),
337 Bucket: []*dto.Bucket{
338 {
339 UpperBound: proto.Float64(100),
340 CumulativeCount: proto.Uint64(123),
341 },
342 {
343 UpperBound: proto.Float64(120),
344 CumulativeCount: proto.Uint64(412),
345 },
346 {
347 UpperBound: proto.Float64(144),
348 CumulativeCount: proto.Uint64(592),
349 },
350 {
351 UpperBound: proto.Float64(172.8),
352 CumulativeCount: proto.Uint64(1524),
353 },
354 },
355 },
356 },
357 },
358 },
359 out: `# HELP request_duration_microseconds The response latency.
360 # TYPE request_duration_microseconds histogram
361 request_duration_microseconds_bucket{le="100"} 123
362 request_duration_microseconds_bucket{le="120"} 412
363 request_duration_microseconds_bucket{le="144"} 592
364 request_duration_microseconds_bucket{le="172.8"} 1524
365 request_duration_microseconds_bucket{le="+Inf"} 2693
366 request_duration_microseconds_sum 1.7560473e+06
367 request_duration_microseconds_count 2693
368 `,
369 },
370
371 {
372 in: &dto.MetricFamily{
373 Name: proto.String("name"),
374 Help: proto.String("doc string"),
375 Metric: []*dto.Metric{
376 {
377 Counter: &dto.Counter{
378 Value: proto.Float64(math.Inf(-1)),
379 },
380 },
381 },
382 },
383 out: `# HELP name doc string
384 # TYPE name counter
385 name -Inf
386 `,
387 },
388 }
389
390 for i, scenario := range scenarios {
391 out := bytes.NewBuffer(make([]byte, 0, len(scenario.out)))
392 n, err := MetricFamilyToText(out, scenario.in)
393 if err != nil {
394 t.Errorf("%d. error: %s", i, err)
395 continue
396 }
397 if expected, got := len(scenario.out), n; expected != got {
398 t.Errorf(
399 "%d. expected %d bytes written, got %d",
400 i, expected, got,
401 )
402 }
403 if expected, got := scenario.out, out.String(); expected != got {
404 t.Errorf(
405 "%d. expected out=%q, got %q",
406 i, expected, got,
407 )
408 }
409 }
410 }
411
412 func BenchmarkCreate(b *testing.B) {
413 mf := &dto.MetricFamily{
414 Name: proto.String("request_duration_microseconds"),
415 Help: proto.String("The response latency."),
416 Type: dto.MetricType_HISTOGRAM.Enum(),
417 Metric: []*dto.Metric{
418 {
419 Label: []*dto.LabelPair{
420 {
421 Name: proto.String("name_1"),
422 Value: proto.String("val with\nnew line"),
423 },
424 {
425 Name: proto.String("name_2"),
426 Value: proto.String("val with \\backslash and \"quotes\""),
427 },
428 {
429 Name: proto.String("name_3"),
430 Value: proto.String("Just a quite long label value to test performance."),
431 },
432 },
433 Histogram: &dto.Histogram{
434 SampleCount: proto.Uint64(2693),
435 SampleSum: proto.Float64(1756047.3),
436 Bucket: []*dto.Bucket{
437 {
438 UpperBound: proto.Float64(100),
439 CumulativeCount: proto.Uint64(123),
440 },
441 {
442 UpperBound: proto.Float64(120),
443 CumulativeCount: proto.Uint64(412),
444 },
445 {
446 UpperBound: proto.Float64(144),
447 CumulativeCount: proto.Uint64(592),
448 },
449 {
450 UpperBound: proto.Float64(172.8),
451 CumulativeCount: proto.Uint64(1524),
452 },
453 {
454 UpperBound: proto.Float64(math.Inf(+1)),
455 CumulativeCount: proto.Uint64(2693),
456 },
457 },
458 },
459 },
460 {
461 Label: []*dto.LabelPair{
462 {
463 Name: proto.String("name_1"),
464 Value: proto.String("Björn"),
465 },
466 {
467 Name: proto.String("name_2"),
468 Value: proto.String("佖佥"),
469 },
470 {
471 Name: proto.String("name_3"),
472 Value: proto.String("Just a quite long label value to test performance."),
473 },
474 },
475 Histogram: &dto.Histogram{
476 SampleCount: proto.Uint64(5699),
477 SampleSum: proto.Float64(49484343543.4343),
478 Bucket: []*dto.Bucket{
479 {
480 UpperBound: proto.Float64(100),
481 CumulativeCount: proto.Uint64(120),
482 },
483 {
484 UpperBound: proto.Float64(120),
485 CumulativeCount: proto.Uint64(412),
486 },
487 {
488 UpperBound: proto.Float64(144),
489 CumulativeCount: proto.Uint64(596),
490 },
491 {
492 UpperBound: proto.Float64(172.8),
493 CumulativeCount: proto.Uint64(1535),
494 },
495 },
496 },
497 TimestampMs: proto.Int64(1234567890),
498 },
499 },
500 }
501 out := bytes.NewBuffer(make([]byte, 0, 1024))
502
503 for i := 0; i < b.N; i++ {
504 _, err := MetricFamilyToText(out, mf)
505 if err != nil {
506 b.Fatal(err)
507 }
508 out.Reset()
509 }
510 }
511
512 func BenchmarkCreateBuildInfo(b *testing.B) {
513 mf := &dto.MetricFamily{
514 Name: proto.String("benchmark_build_info"),
515 Help: proto.String("Test the creation of constant 1-value build_info metric."),
516 Type: dto.MetricType_GAUGE.Enum(),
517 Metric: []*dto.Metric{
518 {
519 Label: []*dto.LabelPair{
520 {
521 Name: proto.String("version"),
522 Value: proto.String("1.2.3"),
523 },
524 {
525 Name: proto.String("revision"),
526 Value: proto.String("2e84f5e4eacdffb574035810305191ff390360fe"),
527 },
528 {
529 Name: proto.String("go_version"),
530 Value: proto.String("1.11.1"),
531 },
532 },
533 Gauge: &dto.Gauge{
534 Value: proto.Float64(1),
535 },
536 },
537 },
538 }
539 out := bytes.NewBuffer(make([]byte, 0, 1024))
540
541 for i := 0; i < b.N; i++ {
542 _, err := MetricFamilyToText(out, mf)
543 if err != nil {
544 b.Fatal(err)
545 }
546 out.Reset()
547 }
548 }
549
550 func TestCreateError(t *testing.T) {
551 scenarios := []struct {
552 in *dto.MetricFamily
553 err string
554 }{
555
556 {
557 in: &dto.MetricFamily{
558 Name: proto.String("name"),
559 Help: proto.String("doc string"),
560 Type: dto.MetricType_COUNTER.Enum(),
561 Metric: []*dto.Metric{},
562 },
563 err: "MetricFamily has no metrics",
564 },
565
566 {
567 in: &dto.MetricFamily{
568 Help: proto.String("doc string"),
569 Type: dto.MetricType_UNTYPED.Enum(),
570 Metric: []*dto.Metric{
571 {
572 Untyped: &dto.Untyped{
573 Value: proto.Float64(math.Inf(-1)),
574 },
575 },
576 },
577 },
578 err: "MetricFamily has no name",
579 },
580
581 {
582 in: &dto.MetricFamily{
583 Name: proto.String("name"),
584 Help: proto.String("doc string"),
585 Type: dto.MetricType_COUNTER.Enum(),
586 Metric: []*dto.Metric{
587 {
588 Untyped: &dto.Untyped{
589 Value: proto.Float64(math.Inf(-1)),
590 },
591 },
592 },
593 },
594 err: "expected counter in metric",
595 },
596 }
597
598 for i, scenario := range scenarios {
599 var out bytes.Buffer
600 _, err := MetricFamilyToText(&out, scenario.in)
601 if err == nil {
602 t.Errorf("%d. expected error, got nil", i)
603 continue
604 }
605 if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 {
606 t.Errorf(
607 "%d. expected error starting with %q, got %q",
608 i, expected, got,
609 )
610 }
611 }
612 }
613
View as plain text