8000 Check ordering within groups. · sxlstevengit/client_python@1ebdaf9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1ebdaf9

Browse files
committed
Check ordering within groups.
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
1 parent c7bfebc commit 1ebdaf9

File tree

3 files changed

+42
-11
lines changed

3 files changed

+42
-11
lines changed

prometheus_client/core.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class Timestamp(object):
4949
def __init__(self, sec, nsec):
5050
if nsec < 0 or nsec >= 1e9:
5151
raise ValueError("Invalid value for nanoseconds in Timestamp: {}".format(nsec))
52+
if sec < 0:
53+
nsec = -nsec
5254
self.sec = int(sec)
5355
self.nsec = int(nsec)
5456

@@ -64,6 +66,9 @@ def __float__(self):
6466
def __eq__(self, other):
6567
return type(self) == type(other) and self.sec == other.sec and self.nsec == other.nsec
6668

69+
def __gt__(self, other):
70+
return self.sec > other.sec or self.nsec > other.nsec
71+
6772

6873
Exemplar = namedtuple('Exemplar', ['labels', 'value', 'timestamp'])
6974
Exemplar.__new__.__defaults__ = (None, )

prometheus_client/openmetrics/parser.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ def text_fd_to_metric_families(fd):
264264
unit = None
265265
group = None
266266
seen_groups = set()
267+
group_timestamp = None
267268
samples = []
268269
allowed_names = []
269270
eof = False
@@ -285,10 +286,8 @@ def build_metric(name, documentation, typ, unit, samples):
285286
raise ValueError("Units not allowed for this metric type: " + name)
286287
metric = core.Metric(name, documentation, typ, unit)
287288
# TODO: check labelvalues are valid utf8
288-
# TODO: check samples are appropriately ordered
289289
# TODO: Check histogram bucket rules being followed
290-
# TODO: Check for dupliate samples
291-
# TODO: Check for decresing timestamps
290+
# TODO: Check for duplicate samples
292291
metric.samples = samples
293292
return metric
294293

@@ -317,6 +316,7 @@ def build_metric(name, documentation, typ, unit, samples):
317316
documentation = None
318317
group = None
319318
seen_groups = set()
319+
group_timestamp = None
320320
samples = []
321321
allowed_names = [parts[2]]
322322

@@ -359,6 +359,7 @@ def build_metric(name, documentation, typ, unit, samples):
359359
typ = 'unknown'
360360
samples = [sample]
361361
group = None
362+
group_timestamp = None
362363
seen_groups = set()
363364
allowed_names = [sample.name]
364365
else:
@@ -375,8 +376,14 @@ def build_metric(name, documentation, typ, unit, samples):
375376

376377
g = tuple(sorted(_group_for_sample(sample, name, typ).items()))
377378
if group is not None and g != group and g in seen_groups:
378-
raise ValueError("Invalid metric group ordering: " + line)
379+
raise ValueError("Invalid metric grouping: " + line)
380+
if group is not None and g == group:
381+
if (sample.timestamp is None) != ( E29B group_timestamp is None):
382+
raise ValueError("Mix of timestamp presence within a group: " + line)
383+
if group_timestamp is not None and group_timestamp > sample.timestamp and typ != 'info':
384+
raise ValueError("Timestamps went backwards within a group: " + line)
379385
group = g
386+
group_timestamp = sample.timestamp
380387
seen_groups.add(g)
381388

382389
if typ == 'stateset' and sample.value not in [0, 1]:

tests/openmetrics/test_parser.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,13 @@ def test_histogram_exemplars(self):
121121
families = text_string_to_metric_families("""# TYPE a histogram
122122
# HELP a help
123123
a_bucket{le="1"} 0 # {a="b"} 0.5
124-
a_bucket{le="2"} 2 123 # {a="c"} 0.5
124+
a_bucket{le="2"} 2 # {a="c"} 0.5
125125
a_bucket{le="+Inf"} 3 # {a="1234567890123456789012345678901234567890123456789012345678"} 4 123
126126
# EOF
127127
""")
128128
hfm = HistogramMetricFamily("a", "help")
129129
hfm.add_sample("a_bucket", {"le": "1"}, 0.0, None, Exemplar({"a": "b"}, 0.5))
130-
hfm.add_sample("a_bucket", {"le": "2"}, 2.0, Timestamp(123, 0), Exemplar({"a": "c"}, 0.5)),
130+
hfm.add_sample("a_bucket", {"le": "2"}, 2.0, None, Exemplar({"a": "c"}, 0.5)),
131131
hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, None, Exemplar({"a": "1234567890123456789012345678901234567890123456789012345678"}, 4, Timestamp(123, 0)))
132132
self.assertEqual([hfm], list(families))
133133

@@ -145,15 +145,15 @@ def test_simple_gaugehistogram(self):
145145
def test_gaugehistogram_exemplars(self):
146146
families = text_string_to_metric_families("""# TYPE a gaugehistogram
147147
# HELP a help
148-
a_bucket{le="1"} 0 # {a="b"} 0.5
148+
a_bucket{le="1"} 0 123 # {a="b"} 0.5
149149
a_bucket{le="2"} 2 123 # {a="c"} 0.5
150-
a_bucket{le="+Inf"} 3 # {a="d"} 4 123
150+
a_bucket{le="+Inf"} 3 123 # {a="d"} 4 123
151151
# EOF
152152
""")
153153
hfm = GaugeHistogramMetricFamily("a", "help")
154-
hfm.add_sample("a_bucket", {"le": "1"}, 0.0, None, Exemplar({"a": "b"}, 0.5))
154+
hfm.add_sample("a_bucket", {"le": "1"}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
155155
hfm.add_sample("a_bucket", {"le": "2"}, 2.0, Timestamp(123, 0), Exemplar({"a": "c"}, 0.5)),
156-
hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, None, Exemplar({"a": "d"}, 4, Timestamp(123, 0)))
156+
hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, Timestamp(123, 0), Exemplar({"a": "d"}, 4, Timestamp(123, 0)))
157157
self.assertEqual([hfm], list(families))
158158

159159
def test_simple_info(self):
@@ -164,6 +164,18 @@ def test_simple_info(self):
164164
""")
165165
self.assertEqual([InfoMetricFamily("a", "help", {'foo': 'bar'})], list(families))
166166

167+
def test_info_timestamps(self):
168+
families = text_string_to_metric_families("""# TYPE a info
169+
# HELP a help
170+
a_info{a="1",foo="bar"} 1 1
171+
a_info{a="2",foo="bar"} 1 0
172+
# EOF
173+
""")
174+
imf = InfoMetricFamily("a", "help")
175+
imf.add_sample("a_info", {"a": "1", "foo": "bar"}, 1, Timestamp(1, 0))
176+
imf.add_sample("a_info", {"a": "2", "foo": "bar"}, 1, Timestamp(0, 0))
177+
self.assertEqual([imf], list(families))
178+
167179
def test_simple_stateset(self):
168180
families = text_string_to_metric_families("""# TYPE a stateset
169181
# HELP a help
@@ -516,11 +528,18 @@ def test_invalid_input(self):
516528
('# TYPE a gaugehistogram\na_bucket{le="+Inf"} NaN\n# EOF\n'),
517529
('# TYPE a summary\na_sum NaN\n# EOF\n'),
518530
('# TYPE a summary\na_count NaN\n# EOF\n'),
519-
# Bad grouping.
531+
# Bad grouping or ordering.
520532
('# TYPE a histogram\na_sum{a="1"} 0\na_sum{a="2"} 0\na_count{a="1"} 0\n# EOF\n'),
521533
('# 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'),
522534
('# TYPE a gaugehistogram\na_gsum{a="1"} 0\na_gsum{a="2"} 0\na_gcount{a="1"} 0\n# EOF\n'),
523535
('# TYPE a summary\nquantile{quantile="0"} 0\na_sum{a="1"} 0\nquantile{quantile="1"} 0\n# EOF\n'),
536+
('# TYPE a gauge\na 0 -1\na 0 -2\n# EOF\n'),
537+
('# TYPE a gauge\na 0 -1\na 0 -1.1\n# EOF\n'),
538+
('# TYPE a gauge\na 0 1\na 0 -1\n# EOF\n'),
539+
('# TYPE a gauge\na 0 1.1\na 0 1\n# EOF\n'),
540+
('# TYPE a gauge\na 0 1\na 0 0\n# EOF\n'),
541+
('# TYPE a gauge\na 0\na 0 0\n# EOF\n'),
542+
('# TYPE a gauge\na 0 0\na 0\n# EOF\n'),
524543
]:
525544
with self.assertRaises(ValueError):
526545
list(text_string_to_metric_families(case))

0 commit comments

Comments
 (0)
0