8000 Check that quantile and le labels have Go %g syntax, with .0 (#346) · sxlstevengit/client_python@9c19f81 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9c19f81

Browse files
authored
Check that quantile and le labels have Go %g syntax, with .0 (prometheus#346)
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
1 parent 752c7bf commit 9c19f81

File tree

4 files changed

+56
-22
lines changed

4 files changed

+56
-22
lines changed

prometheus_client/openmetrics/parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from ..metrics_core import Metric, METRIC_LABEL_NAME_RE
88
from ..samples import Exemplar, Sample, Timestamp
9+
from ..utils import floatToGoString
910

1011
try:
1112
import StringIO
@@ -371,7 +372,7 @@ def build_metric(name, documentation, typ, unit, samples):
371372
allowed_names = {
372373
'counter': ['_total', '_created'],
373374
'summary': ['_count', '_sum', '', '_created'],
374-
'histogram': ['_count', '_sum', '_bucket', 'created'],
375+
'histogram': ['_count', '_sum', '_bucket', '_created'],
375376
'gaugehistogram': ['_gcount', '_gsum', '_bucket'],
376377
'info': ['_info'],
377378
}.get(typ, [''])
@@ -402,10 +403,12 @@ def build_metric(name, documentation, typ, unit, samples):
402403
if typ == 'stateset' and name not in sample.labels:
403404
raise ValueError("Stateset missing label: " + line)
404405
if (typ in ['histogram', 'gaugehistogram'] and name + '_bucket' == sample.name
405-
and float(sample.labels.get('le', -1)) < 0):
406+
and (float(sample.labels.get('le', -1)) < 0
407+
or sample.labels['le'] != floatToGoString(sample.labels['le']))):
406408
raise ValueError("Invalid le label: " + line)
407409
if (typ == 'summary' and name == sample.name
408-
and not (0 <= float(sample.labels.get('quantile', -1)) <= 1)):
410+
and (not (0 <= float(sample.labels.get('quantile', -1)) <= 1)
411+
or sample.labels['quantile'] != floatToGoString(sample.labels['quantile']))):
409412
raise ValueError("Invalid quantile label: " + line)
410413

411414
g = tuple(sorted(_group_for_sample(sample, name, typ).items()))

prometheus_client/utils.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@
55

66

77
def floatToGoString(d):
8+
d = float(d)
89
if d == INF:
910
return '+Inf'
1011
elif d == MINUS_INF:
1112
return '-Inf'
1213
elif math.isnan(d):
1314
return 'NaN'
1415
else:
15-
return repr(float(d))
16+
s = repr(d)
17+
dot = s.find('.')
18+
# Go switches to exponents sooner than Python.
19+
# We only need to care about positive values for le/quantile.
20+
if d > 0 and dot > 6:
21+
mantissa = '{}.{}{}'.format(s[0], s[1:dot], s[dot+1:]).rstrip('0.')
22+
return '{}e+0{}'.format(mantissa, dot-1)
23+
return s

tests/openmetrics/test_parser.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,50 +98,50 @@ def test_summary_quantiles(self):
9898
def test_simple_histogram(self):
9999
families = text_string_to_metric_families("""# TYPE a histogram
100100
# HELP a help
101-
a_bucket{le="1"} 0
101+
a_bucket{le="1.0"} 0
102102
a_bucket{le="+Inf"} 3
103103
a_count 3
104104
a_sum 2
105105
# EOF
106106
""")
107-
self.assertEqual([HistogramMetricFamily("a", "help", sum_value=2, buckets=[("1", 0.0), ("+Inf", 3.0)])], list(families))
107+
self.assertEqual([HistogramMetricFamily("a", "help", sum_value=2, buckets=[("1.0", 0.0), ("+Inf", 3.0)])], list(families))
108108

109109
def test_histogram_exemplars(self):
110110
families = text_string_to_metric_families("""# TYPE a histogram
111111
# HELP a help
112-
a_bucket{le="1"} 0 # {a="b"} 0.5
113-
a_bucket{le="2"} 2 # {a="c"} 0.5
112+
a_bucket{le="1.0"} 0 # {a="b"} 0.5
113+
a_bucket{le="2.0"} 2 # {a="c"} 0.5
114114
a_bucket{le="+Inf"} 3 # {a="1234567890123456789012345678901234567890123456789012345678"} 4 123
115115
# EOF
116116
""")
117117
hfm = HistogramMetricFamily("a", "help")
118-
hfm.add_sample("a_bucket", {"le": "1"}, 0.0, None, Exemplar({"a": "b"}, 0.5))
119-
hfm.add_sample("a_bucket", {"le": "2"}, 2.0, None, Exemplar({"a": "c"}, 0.5)),
118+
hfm.add_sample("a_bucket", {"le": "1.0"}, 0.0, None, Exemplar({"a": "b"}, 0.5))
119+
hfm.add_sample("a_bucket", {"le": "2.0"}, 2.0, None, Exemplar({"a": "c"}, 0.5)),
120120
hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, None, Exemplar({"a": "1234567890123456789012345678901234567890123456789012345678"}, 4, Timestamp(123, 0)))
121121
self.assertEqual([hfm], list(families))
122122

123123
def test_simple_gaugehistogram(self):
124124
families = text_string_to_metric_families("""# TYPE a gaugehistogram
125125
# HELP a help
126-
a_bucket{le="1"} 0
126+
a_bucket{le="1.0"} 0
127127
a_bucket{le="+Inf"} 3
128128
a_gcount 3
129129
a_gsum 2
130130
# EOF
131131
""")
132-
self.assertEqual([GaugeHistogramMetricFamily("a", "help", gsum_value=2, buckets=[("1", 0.0), ("+Inf", 3.0)])], list(families))
132+
self.assertEqual([GaugeHistogramMetricFamily("a", "help", gsum_value=2, buckets=[("1.0", 0.0), ("+Inf", 3.0)])], list(families))
133133

134134
def test_gaugehistogram_exemplars(self):
135135
families = text_string_to_metric_families("""# TYPE a gaugehistogram
136136
# HELP a help
137-
a_bucket{le="1"} 0 123 # {a="b"} 0.5
138-
a_bucket{le="2"} 2 123 # {a="c"} 0.5
137+
a_bucket{le="1.0"} 0 123 # {a="b"} 0.5
138+
a_bucket{le="2.0"} 2 123 # {a="c"} 0.5
139139
a_bucket{le="+Inf"} 3 123 # {a="d"} 4 123
140140
# EOF
141141
""")
142142
hfm = GaugeHistogramMetricFamily("a", "help")
143-
hfm.add_sample("a_bucket", {"le": "1"}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
144-
hfm.add_sample("a_bucket", {"le": "2"}, 2.0, Timestamp(123, 0), Exemplar({"a": "c"}, 0.5)),
143+
hfm.add_sample("a_bucket", {"le": "1.0"}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
144+
hfm.add_sample("a_bucket", {"le": "2.0"}, 2.0, Timestamp(123, 0), Exemplar({"a": "c"}, 0.5)),
145145
hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, Timestamp(123, 0), Exemplar({"a": "d"}, 4, Timestamp(123, 0)))
146146
self.assertEqual([hfm], list(families))
147147

@@ -383,11 +383,11 @@ def test_timestamps(self):
383383
def test_roundtrip(self):
384384
text = """# HELP go_gc_duration_seconds A summary of the GC invocation durations.
385385
# TYPE go_gc_duration_seconds summary
386-
go_gc_duration_seconds{quantile="0"} 0.013300656000000001
386+
go_gc_duration_seconds{quantile="0.0"} 0.013300656000000001
387387
go_gc_duration_seconds{quantile="0.25"} 0.013638736
388388
go_gc_duration_seconds{quantile="0.5"} 0.013759906
389389
go_gc_duration_seconds{quantile="0.75"} 0.013962066
390-
go_gc_duration_seconds{quantile="1"} 0.021383540000000003
390+
go_gc_duration_seconds{quantile="1.0"} 0.021383540000000003
391391
go_gc_duration_seconds_sum 56.12904785
392392
go_gc_duration_seconds_count 7476.0
393393
# HELP go_goroutines Number of goroutines that currently exist.
@@ -405,20 +405,36 @@ def test_roundtrip(self):
405405
process_cpu_seconds_total 29323.4
406406
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
407407
# TYPE process_virtual_memory_bytes gauge
408-
process_virtual_memory_bytes 2478268416.0
408+
process_virtual_memory_bytes 2.478268416e+09
409409
# HELP prometheus_build_info A metric with a constant '1' value labeled by version, revision, and branch from which Prometheus was built.
410410
# TYPE prometheus_build_info gauge
411411
prometheus_build_info{branch="HEAD",revision="ef176e5",version="0.16.0rc1"} 1.0
412412
# HELP prometheus_local_storage_chunk_ops The total number of chunk operations by their type.
413413
# TYPE prometheus_local_storage_chunk_ops counter
414414
prometheus_local_storage_chunk_ops_total{type="clone"} 28.0
415415
prometheus_local_storage_chunk_ops_total{type="create"} 997844.0
416-
prometheus_local_storage_chunk_ops_total{type="drop"} 1345758.0
416+
prometheus_local_storage_chunk_ops_total{type="drop"} 1.345758e+06
417417
prometheus_local_storage_chunk_ops_total{type="load"} 1641.0
418418
prometheus_local_storage_chunk_ops_total{type="persist"} 981408.0
419419
prometheus_local_storage_chunk_ops_total{type="pin"} 32662.0
420420
prometheus_local_storage_chunk_ops_total{type="transcode"} 980180.0
421421
prometheus_local_storage_chunk_ops_total{type="unpin"} 32662.0
422+
# HELP foo histogram Testing histogram buckets
423+
# TYPE foo histogram
424+
foo_bucket{le="0.0"} 0.0
425+
foo_bucket{le="1e-05"} 0.0
426+
foo_bucket{le="0.0001"} 0.0
427+
foo_bucket{le="0.1"} 8.0
428+
foo_bucket{le="1.0"} 10.0
429+
foo_bucket{le="10.0"} 17.0
430+
foo_bucket{le="100000.0"} 17.0
431+
foo_bucket{le="1e+06"} 17.0
432+
foo_bucket{le="1.55555555555552e+06"} 17.0
433+
foo_bucket{le="1e+23"} 17.0
434+
foo_bucket{le="+Inf"} 17.0
435+
foo_count 17.0
436+
foo_sum 324789.3
437+
foo_created 1.520430000123e+09
422438
# EOF
423439
"""
424440
families = list(text_string_to_metric_families(text))
@@ -522,6 +538,7 @@ def test_invalid_input(self):
522538
('# TYPE a summary\na{quantile="foo"} 0\n# EOF\n'),
523539
('# TYPE a summary\na{quantile="1.01"} 0\n# EOF\n'),
524540
('# TYPE a summary\na{quantile="NaN"} 0\n# EOF\n'),
541+
('# TYPE a summary\na{quantile="1"} 0\n# EOF\n'),
525542
('# TYPE a histogram\na_bucket 0\n# EOF\n'),
526543
('# TYPE a gaugehistogram\na_bucket 0\n# EOF\n'),
527544
('# TYPE a stateset\na 0\n# EOF\n'),
@@ -549,6 +566,12 @@ def test_invalid_input(self):
549566
('# TYPE a gaugehistogram\na_gsum 1\n# EOF\n'),
550567
('# TYPE a histogram\na_count 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
551568
('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_count 1\n# EOF\n'),
569+
('# TYPE a histogram\na_bucket{le="1"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
570+
('# TYPE a histogram\na_bucket{le="9.999999999999999e+22"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
571+
('# TYPE a histogram\na_bucket{le="1.5555555555555201e+06"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
572+
('# TYPE a histogram\na_bucket{le="1e-04"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
573+
('# TYPE a histogram\na_bucket{le="1e+05"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
574+
('# TYPE a histogram\na_bucket{le="+INF"} 0\n# EOF\n'),
552575
('# TYPE a histogram\na_bucket{le="2"} 0\na_bucket{le="1"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
553576
('# TYPE a histogram\na_bucket{le="1"} 1\na_bucket{le="2"} 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
554577
# Bad grouping or ordering.

tests/test_parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,15 @@ def test_roundtrip(self):
305305
process_cpu_seconds_total 29323.4
306306
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
307307
# TYPE process_virtual_memory_bytes gauge
308-
process_virtual_memory_bytes 2478268416.0
308+
process_virtual_memory_bytes 2.478268416e+09
309309
# HELP prometheus_build_info A metric with a constant '1' value labeled by version, revision, and branch from which Prometheus was built.
310310
# TYPE prometheus_build_info gauge
311311
prometheus_build_info{branch="HEAD",revision="ef176e5",version="0.16.0rc1"} 1.0
312312
# HELP prometheus_local_storage_chunk_ops_total The total number of chunk operations by their type.
313313
# TYPE prometheus_local_storage_chunk_ops_total counter
314314
prometheus_local_storage_chunk_ops_total{type="clone"} 28.0
315315
prometheus_local_storage_chunk_ops_total{type="create"} 997844.0
316-
prometheus_local_storage_chunk_ops_total{type="drop"} 1345758.0
316+
prometheus_local_storage_chunk_ops_total{type="drop"} 1.345758e+06
317317
prometheus_local_storage_chunk_ops_total{type="load"} 1641.0
318318
prometheus_local_storage_chunk_ops_total{type="persist"} 981408.0
319319
prometheus_local_storage_chunk_ops_total{type="pin"} 32662.0

0 commit comments

Comments
 (0)
0