8000 Allow counters to emit exemplars via OpenMetrics · quanuw/client_python@921fd67 · GitHub
[go: up one dir, main page]

Skip to content

Commit 921fd67

Browse files
committed
Allow counters to emit exemplars via OpenMetrics
Signed-off-by: Chris Marchbanks <csmarchbanks@gmail.com>
1 parent b6cad95 commit 921fd67

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

prometheus_client/openmetrics/exposition.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
"""Content type of the latest OpenMetrics text format"""
99

1010

11+
def _is_valid_exemplar_metric(metric, sample):
12+
if metric.type == 'counter' and sample.name.endswith('_total'):
13+
return True
14+
if metric.type in ('histogram', 'gaugehistogram') and sample.name.endswith('_bucket'):
15+
return True
16+
return False
17+
18+
1119
def generate_latest(registry):
1220
'''Returns the metrics from the registry in latest text format as a string.'''
1321
output = []
@@ -28,8 +36,8 @@ def generate_latest(registry):
2836
else:
2937
labelstr = ''
3038
if s.exemplar:
31-
if metric.type not in ('histogram', 'gaugehistogram') or not s.name.endswith('_bucket'):
32-
raise ValueError("Metric {0} has exemplars, but is not a histogram bucket".format(metric.name))
39+
if not _is_valid_exemplar_metric(metric, s):
40+
raise ValueError("Metric {0} has exemplars, but is not a histogram bucket or counter".format(metric.name))
3341
labels = '{{{0}}}'.format(','.join(
3442
['{0}="{1}"'.format(
3543
k, v.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"'))

tests/openmetrics/test_exposition.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,23 @@ def collect(self):
136136
# EOF
137137
""", generate_latest(self.registry))
138138

139-
def test_nonhistogram_exemplar(self):
139+
def test_counter_exemplar(self):
140+
class MyCollector(object):
141+
def collect(self):
142+
metric = Metric("cc", "A counter", 'counter')
143+
metric.add_sample("cc_total", {}, 1, None, Exemplar({'a': 'b'}, 1.0, 123.456))
144+
metric.add_sample("cc_created", {}, 123.456, None, None)
145+
yield metric
146+
147+
self.registry.register(MyCollector())
148+
self.assertEqual(b"""# HELP cc A counter
149+
# TYPE cc counter
150+
cc_total 1.0 # {a="b"} 1.0 123.456
151+
cc_created 123.456
152+
# EOF
153+
""", generate_latest(self.registry))
154+
155+
def test_untyped_exemplar(self):
140156
class MyCollector(object):
141157
def collect(self):
142158
metric = Metric("hh", "help", 'untyped')
@@ -148,7 +164,7 @@ def collect(self):
148164
with self.assertRaises(ValueError):
149165
generate_latest(self.registry)
150166

151-
def test_nonhistogram_bucket_exemplar(self):
167+
def test_histogram_non_bucket_exemplar(self):
152168
class MyCollector(object):
153169
def collect(self):
154170
metric = Metric("hh", "help", 'histogram')
@@ -160,6 +176,18 @@ def collect(self):
160176
with self.assertRaises(ValueError):
161177
generate_latest(self.registry)
162178

179+
def test_counter_non_total_exemplar(self):
180+
class MyCollector(object):
181+
def collect(self):
182+
metric = Metric("cc", "A counter", 'counter')
183+
metric.add_sample("cc_total", {}, 1, None, None)
184+
metric.add_sample("cc_created", {}, 123.456, None, Exemplar({'a': 'b'}, 1.0, 123.456))
185+
yield metric
186+
187+
self.registry.register(MyCollector())
188+
with self.assertRaises(ValueError):
189+
generate_latest(self.registry)
190+
163191
def test_gaugehistogram(self):
164192
self.custom_collector(
165193
GaugeHistogramMetricFamily('gh', 'help', buckets=[('1.0', 4), ('+Inf', (5))], gsum_value=7))

0 commit comments

Comments
 (0)
0