8000 Check bucket rules are being followed · sxlstevengit/client_python@a61233d · GitHub
[go: up one dir, main page]

Skip to content

Commit a61233d

Browse files
committed
Check bucket rules are being followed
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
1 parent 1ebdaf9 commit a61233d

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

prometheus_client/core.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ def __float__(self):
6666
def __eq__(self, other):
6767
return type(self) == type(other) and self.sec == other.sec and self.nsec == other.nsec
6868

69+
def __ne__(self, other):
70+
return not self == other
71+
6972
def __gt__(self, other):
7073
return self.sec > other.sec or self.nsec > other.nsec
7174

prometheus_client/openmetrics/parser.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,42 @@ def _group_for_sample(sample, name, typ):
249249
return sample.labels
250250

251251

252+
def _check_histogram(samples, name):
253+
group = None
254+
timestamp = None
255+
256+
def do_checks():
257+
if bucket != float('+Inf'):
258+
raise ValueError("+Inf bucket missing: " + name)
259+
if count is not None and value != count:
260+
raise ValueError("Count does not match +Inf value: " + name)
261+
262+
for s in samples:
263+
suffix = s.name[len(name):]
264+
g = _group_for_sample(s, name, 'histogram')
265+
if g != group or s.timestamp != timestamp:
266+
if group is not None:
267+
do_checks()
268+
count = None
269+
bucket = -1
270+
value = 0
271+
group = g
272+
timestamp = s.timestamp
273+
274+
if suffix == '_bucket':
275+
b = float(s.labels['le'])
276+
if b <= bucket:
277+
raise ValueError("Buckets out of order: " + name)
278+
if s.value < value:
279+
raise ValueError("Bucket values out of order: " + name)
280+
bucket = b
281+
value = s.value
282+
elif suffix in ['_count', '_gcount']:
283+
count = s.value
284+
if group is not None:
285+
do_checks()
286+
287+
252288
def text_fd_to_metric_families(fd):
253289
"""Parse Prometheus text format from a file descriptor.
254290
@@ -284,9 +320,10 @@ def build_metric(name, documentation, typ, unit, samples):
284320
raise ValueError("Unit does not match metric name: " + name)
285321
if unit and typ in ['info', 'stateset']:
286322
raise ValueError("Units not allowed for this metric type: " + name)
323+
if typ in ['histogram', 'gaugehistogram']:
324+
_check_histogram(samples, name)
287325
metric = core.Metric(name, documentation, typ, unit)
288326
# TODO: check labelvalues are valid utf8
289-
# TODO: Check histogram bucket rules being followed
290327
# TODO: Check for duplicate samples
291328
metric.samples = samples
292329
return metric

tests/openmetrics/test_parser.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,14 @@ def test_invalid_input(self):
528528
('# TYPE a gaugehistogram\na_bucket{le="+Inf"} NaN\n# EOF\n'),
529529
('# TYPE a summary\na_sum NaN\n# EOF\n'),
530530
('# TYPE a summary\na_count NaN\n# EOF\n'),
531+
# Bad histograms.
532+
('# TYPE a histogram\na_sum 1\n# EOF\n'),
533+
('# TYPE a gaugehistogram\na_gsum 1\n# EOF\n'),
534+
('# TYPE a histogram\na_count 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
535+
('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_count 1\n# EOF\n'),
536+
('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
537+
('# TYPE a histogram\na_bucket{le="2"} 0\na_bucket{le="1"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
538+
('# TYPE a histogram\na_bucket{le="1"} 1\na_bucket{le="2"} 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
531539
# Bad grouping or ordering.
532540
('# TYPE a histogram\na_sum{a="1"} 0\na_sum{a="2"} 0\na_count{a="1"} 0\n# EOF\n'),
533541
('# TYPE a histogram\na_bucket{a="1",le="1"} 0\na_bucket{a="2",le="+Inf""} 0\na_bucket{a="1",le="+Inf"} 0\n# EOF\n'),

0 commit comments

Comments
 (0)
0