Skip to content

Commit

Permalink
Add support for exemplars on counters. (#434)
Browse files Browse the repository at this point in the history
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
  • Loading branch information
brian-brazil committed Jul 3, 2019
1 parent 6b091ab commit 4712878
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 9 deletions.
16 changes: 8 additions & 8 deletions prometheus_client/openmetrics/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,11 @@ def _parse_labels(text):


def _parse_sample(text):
seperator = " # "
# Detect the labels in the text
label_start = text.find("{")
if label_start == -1:
# We don't have labels
if label_start == -1 or seperator in text[:label_start]:
# We don't have labels, but there could be an exemplar.
name_end = text.index(" ")
name = text[:name_end]
# Parse the remaining text after the name
Expand All @@ -250,8 +251,7 @@ def _parse_sample(text):
return Sample(name, {}, value, timestamp, exemplar)
# The name is before the labels
name = text[:label_start]
seperator = " # "
if text.count(seperator) == 0:
if seperator not in text:
# Line doesn't contain an exemplar
# We can use `rindex` to find `label_end`
label_end = text.rindex("}")
Expand All @@ -261,7 +261,7 @@ def _parse_sample(text):
# Line potentially contains an exemplar
# Fallback to parsing labels with a state machine
labels, labels_len = _parse_labels_with_state_machine(text[label_start + 1:])
label_end = labels_len + len(name)
label_end = labels_len + len(name)
# Parsing labels succeeded, continue parsing the remaining text
remaining_text = text[label_end + 2:]
value, timestamp, exemplar = _parse_remaining_text(remaining_text)
Expand Down Expand Up @@ -564,9 +564,9 @@ def build_metric(name, documentation, typ, unit, samples):
'_gsum'] and sample.value < 0:
raise ValueError("Counter-like samples cannot be negative: " + line)
if sample.exemplar and not (
typ in ['histogram', 'gaugehistogram']
and sample.name.endswith('_bucket')):
raise ValueError("Invalid line only histogram/gaugehistogram buckets can have exemplars: " + line)
(typ in ['histogram', 'gaugehistogram'] and sample.name.endswith('_bucket'))
or (typ in ['counter'] and sample.name.endswith('_total'))):
raise ValueError("Invalid line only histogram/gaugehistogram buckets and counters can have exemplars: " + line)

if name is not None:
yield build_metric(name, documentation, typ, unit, samples)
Expand Down
14 changes: 13 additions & 1 deletion tests/openmetrics/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ def test_gaugehistogram_exemplars(self):
hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, Timestamp(123, 0), Exemplar({"a": "d"}, 4, Timestamp(123, 0)))
self.assertEqual([hfm], list(families))

def test_counter_exemplars(self):
families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total 0 123 # {a="b"} 0.5
# EOF
""")
cfm = CounterMetricFamily("a", "help")
cfm.add_sample("a_total", {}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
self.assertEqual([cfm], list(families))

def test_simple_info(self):
families = text_string_to_metric_families("""# TYPE a info
# HELP a help
Expand Down Expand Up @@ -616,9 +626,11 @@ def test_invalid_input(self):
('# TYPE a histogram\na_sum 1 # {a="b"} 0.5\n# EOF\n'),
('# TYPE a gaugehistogram\na_sum 1 # {a="b"} 0.5\n# EOF\n'),
('# TYPE a_bucket gauge\na_bucket 1 # {a="b"} 0.5\n# EOF\n'),
('# TYPE a counter\na_created 1 # {a="b"} 0.5\n# EOF\n'),
# Exemplars on unallowed metric types.
('# TYPE a counter\na_total 1 # {a="b"} 1\n# EOF\n'),
('# TYPE a gauge\na 1 # {a="b"} 1\n# EOF\n'),
('# TYPE a info\na_info 1 # {a="b"} 1\n# EOF\n'),
('# TYPE a stateset\na{a="b"} 1 # {c="d"} 1\n# EOF\n'),
# Bad stateset/info values.
('# TYPE a stateset\na 2\n# EOF\n'),
('# TYPE a info\na 2\n# EOF\n'),
Expand Down

0 comments on commit 4712878

Please sign in to comment.