8000 Add support for parsing timestamps · sxlstevengit/client_python@e325c66 · GitHub
[go: up one dir, main page]

Skip to content
< 8000 header class="HeaderMktg header-logged-out js-details-container js-header Details f4 py-3" role="banner" data-is-top="true" data-color-mode=light data-light-theme=light data-dark-theme=dark>

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e325c66

Browse files
committed
Add support for parsing timestamps
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
1 parent 53726b3 commit e325c66

File tree

3 files changed

+77
-32
lines changed

3 files changed

+77
-32
lines changed

prometheus_client/core.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,15 @@ def __init__(self, sec, nsec):
5454
def __str__(self):
5555
return "{0}.{1:09d}".format(self.sec, self.nsec)
5656

57+
def __repr__(self):
58+
return "Timestamp({0}, {1})".format(self.sec, self.nsec)
59+
5760
def __float__(self):
5861
return float(self.sec) + float(self.nsec) / 1e9
5962

63+
def __eq__(self, other):
64+
return self.sec == other.sec and self.nsec == other.nsec
65+
6066

6167
class CollectorRegistry(object):
6268
'''Metric collector registry.
@@ -221,6 +227,7 @@ def __repr__(self):
221227
return "Metric(%s, %s, %s, %s, %s)" % (self.name, self.documentation,
222228
self.type, self.unit, self.samples)
223229

230+
224231
class UntypedMetricFamily(Metric):
225232
'''A single untyped metric and its samples.
226233
For use by custom collectors.
@@ -235,13 +242,13 @@ def __init__(self, name, documentation, value=None, labels=None):
235242
if value is not None:
236243
self.add_metric([], value)
237244

238-
def add_metric(self, labels, value):
245+
def add_metric(self, labels, value, timestamp=None):
239246
'''Add a metric to the metric family.
240247
Args:
241248
labels: A list of label values
242249
value: The value of the metric.
243250
'''
244-
self.samples.append(Sample(self.name, dict(zip(self._labelnames, labels)), value))
251+
self.samples.append(Sample(self.name, dict(zip(self._labelnames, labels)), value, timestamp))
245252

246253

247254
class CounterMetricFamily(Metric):
@@ -262,17 +269,17 @@ def __init__(self, name, documentation, value=None, labels=None, created=None):
262269
if value is not None:
263270
self.add_metric([], value, created)
264271

265-
def add_metric(self, labels, value, created=None):
272+
def add_metric(self, labels, value, created=None, timestamp=None):
266273
'''Add a metric to the metric family.
267274
268275
Args:
269276
labels: A list of label values
270277
value: The value of the metric
271278
created: Optional unix timestamp the child was created at.
272279
'''
273-
self.samples.append(Sample(self.name + '_total', dict(zip(self._labelnames, labels)), value))
280+
self.samples.append(Sample(self.name + '_total', dict(zip(self._labelnames, labels)), value, timestamp))
274281
if created is not None:
275-
self.samples.append(Sample(self.name + '_created', dict(zip(self._labelnames, labels)), created))
282+
self.samples.append(Sample(self.name + '_created', dict(zip(self._labelnames, labels)), created, timestamp))
276283

277284

278285
class GaugeMetricFamily(Metric):
@@ -290,14 +297,14 @@ def __init__(self, name, documentation, value=None, labels=None, unit=''):
290297
if value is not None:
291298
self.add_metric([], value)
292299

293-
def add_metric(self, labels, value):
300+
def add_metric(self, labels, value, timestamp=None):
294301
'''Add a metric to the metric family.
295302
296303
Args:
297304
labels: A list of label values
298305
value: A float
299306
1E79 '''
300-
self.samples.append(Sample(self.name, dict(zip(self._labelnames, labels)), value))
307+
self.samples.append(Sample(self.name, dict(zip(self._labelnames, labels)), value, timestamp))
301308

302309

303310
class SummaryMetricFamily(Metric):
@@ -317,16 +324,16 @@ def __init__(self, name, documentation, count_value=None, sum_value=None, labels
317324
if count_value is not None:
318325
self.add_metric([], count_value, sum_value)
319326

320-
def add_metric(self, labels, count_value, sum_value):
327+
def add_metric(self, labels, count_value, sum_value, timestamp=None):
321328
'''Add a metric to the metric family.
322329
323330
Args:
324331
labels: A list of label values
325332
count_value: The count value of the metric.
326333
sum_value: The sum value of the metric.
327334
'''
328-
self.samples.append(Sample(self.name + '_count', dict(zip(self._labelnames, labels)), count_value))
329-
self.samples.append(Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value))
335+
self.samples.append(Sample(self.name + '_count', dict(zip(self._labelnames, labels)), count_value, timestamp))
336+
self.samples.append(Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value, timestamp))
330337

331338

332339
class HistogramMetricFamily(Metric):
@@ -346,7 +353,7 @@ def __init__(self, name, documentation, buckets=None, sum_value=None, labels=Non
346353
if buckets is not None:
347354
self.add_metric([], buckets, sum_value)
348355

349-
def add_metric(self, labels, buckets, sum_value):
356+
def add_metric(self, labels, buckets, sum_value, timestamp=None):
350357
'''Add a metric to the metric family.
351358
352359
Args:
@@ -356,10 +363,10 @@ def add_metric(self, labels, buckets, sum_value):
356363
sum_value: The sum value of the metric.
357364
'''
358365
for bucket, value in buckets:
359-
self.samples.append(Sample(self.name + '_bucket', dict(list(zip(self._labelnames, labels)) + [('le', bucket)]), value))
366+
self.samples.append(Sample(self.name + '_bucket', dict(list(zip(self._labelnames, labels)) + [('le', bucket)]), value, timestamp))
360367
# +Inf is last and provides the count value.
361-
self.samples.append(Sample(self.name + '_count', dict(zip(self._labelnames, labels)), buckets[-1][1]))
362-
self.samples.append(Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value))
368+
self.samples.append(Sample(self.name + '_count', dict(zip(self._labelnames, labels)), buckets[-1][1], timestamp))
369+
self.samples.append(Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value, timestamp))
363370

364371

365372
class GaugeHistogramMetricFamily(Metric):
@@ -377,7 +384,7 @@ def __init__(self, name, documentation, buckets=None, labels=None, unit=''):
377384
if buckets is not None:
378385
self.add_metric([], buckets)
379386

380-
def add_metric(self, labels, buckets):
387+
def add_metric(self, labels, buckets, timestamp=None):
381388
'''Add a metric to the metric family.
382389
383390
Args:
@@ -389,7 +396,7 @@ def add_metric(self, labels, buckets):
389396
self.samples.append(Sample(
390397
self.name + '_bucket',
391398
dict(list(zip(self._labelnames, labels)) + [('le', bucket)]),
392-
value))
399+
value, timestamp))
393400

394401

395402
class InfoMetricFamily(Metric):
@@ -407,15 +414,15 @@ def __init__(self, name, documentation, value=None, labels=None):
407414
if value is not None:
408415
self.add_metric([], value)
409416

410-
def add_metric(self, labels, value):
417+
def add_metric(self, labels, value, timestamp=None):
411418
'''Add a metric to the metric family.
412419
413420
Args:
414421
labels: A list of label values
415422
value: A dict of labels
416423
'''
417424
self.samples.append(Sample(self.name + '_info',
418-
dict(dict(zip(self._labelnames, labels)), **value), 1))
425+
dict(dict(zip(self._labelnames, labels)), **value), 1, timestamp))
419426

420427

421428
class StateSetMetricFamily(Metric):
@@ -433,7 +440,7 @@ def __init__(self, name, documentation, value=None, labels=None):
433440
if value is not None:
434441
self.add_metric([], value)
435442

436-
def add_metric(self, labels, value):
443+
def add_metric(self, labels, value, timestamp=None):
437444
'''Add a metric to the metric family.
438445
439446
Args:
@@ -444,7 +451,7 @@ def add_metric(self, labels, value):
444451
for state, enabled in value.items():
445452
v = (1 if enabled else 0)
446453
self.samples.append(Sample(self.name,
447-
dict(zip(self._labelnames + (self.name,), labels + (state,))), v))
454+
dict(zip(self._labelnames + (self.name,), labels + (state,))), v, timestamp))
448455

449456

450457
class _MutexValue(object):

prometheus_client/openmetrics/parser.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def _parse_sample(text):
5252
labelname = []
5353
labelvalue = []
5454
value = []
55+
timestamp = []
5556
labels = {}
5657

5758
state = 'name'
@@ -115,20 +116,42 @@ def _parse_sample(text):
115116
else:
116117
raise ValueError("Invalid line: " + text)
117118
elif state == 'value':
118-
if char == ' ' or char == '#':
119-
# Timestamps and examplars are not supported, halt
120-
break
119+
if char == ' ':
120+
state = 'timestamp'
121121
else:
122122
value.append(char)
123+
elif state == 'timestamp':
124+
if char == ' ':
125+
# examplars are not supported, halt
126+
break
127+
else:
128+
timestamp.append(char)
129+
123130
if not value:
124131
raise ValueError("Invalid line: " + text)
132+
value = ''.join(value)
125133
val = None
126134
try:
127-
val = int(''.join(value))
135+
val = int(value)
128136
except ValueError:
129-
val = float(''.join(value))
137+
val = float(value)
138+
139+
ts = None
140+
timestamp = ''.join(timestamp)
141+
if timestamp:
142+
try:
143+
# Simple int.
144+
ts = core.Timestamp(int(timestamp), 0)
145+
except ValueError:
146+
try:
147+
# aaaa.bbbb. Nanosecond resolution supported.
148+
parts = timestamp.split('.', 1)
149+
ts = core.Timestamp(int(parts[0]), int(parts[1][:9].ljust(9, "0")))
150+
except ValueError:
151+
# Float.
152+
ts = float(timestamp)
130153

131-
return core.Sample(''.join(name), labels, val)
154+
return core.Sample(''.join(name), labels, val, ts)
132155

133156

134157
def text_fd_to_metric_families(fd):

tests/openmetrics/test_parser.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
Metric,
1818
Sample,
1919
SummaryMetricFamily,
20+
Timestamp,
2021
)
2122
from prometheus_client.openmetrics.exposition import (
2223
generate_latest,
@@ -67,7 +68,7 @@ def test_unit_gauge(self):
6768
a_seconds 1
6869
# EOF
6970
""")
70-
self.assertEqual([GaugeMetricFamily("a_seconds", "help", value=1)], list(families))
71+
self.assertEqual([GaugeMetricFamily("a_seconds", "help", value=1, unit='seconds')], list(families))
7 F438 172

7273
def test_simple_summary(self):
7374
families = text_string_to_metric_families("""# TYPE a summary
@@ -254,18 +255,27 @@ def test_escaping(self):
254255
metric_family.add_metric(["b\\a\\z"], 2)
255256
self.assertEqual([metric_family], list(families))
256257

257-
def test_timestamps_discarded(self):
258+
def test_timestamps(self):
258259
families = text_string_to_metric_families("""# TYPE a counter
259260
# HELP a help
260-
a_total{foo="bar"} 1 000
261+
a_total{foo="1"} 1 000
262+
a_total{foo="2"} 1 0.0
263+
a_total{foo="3"} 1 1.1
264+
a_total{foo="4"} 1 12345678901234567890.1234567890
265+
a_total{foo="5"} 1 1.5e3
261266
# TYPE b counter
262267
# HELP b help
263-
b_total 2 1234567890
268+
b_total 2 1234567890
264269
# EOF
265270
""")
266271
a = CounterMetricFamily("a", "help", labels=["foo"])
267-
a.add_metric(["bar"], 1)
268-
b = CounterMetricFamily("b", "help", value=2)
272+
a.add_metric(["1"], 1, timestamp=Timestamp(0, 0))
273+
a.add_metric(["2"], 1, timestamp=Timestamp(0, 0))
274+
a.add_metric(["3"], 1, timestamp=Timestamp(1, 100000000))
275+
a.add_metric(["4"], 1, timestamp=Timestamp(12345678901234567890, 123456789))
276+
a.add_metric(["5"], 1, timestamp=1500.0)
277+
b = CounterMetricFamily("b", "help")
278+
b.add_metric([], 2, timestamp=Timestamp(1234567890, 0))
269279
self.assertEqual([a, b], list(families))
270280

271281
@unittest.skipIf(sys.version_info < (2, 7), "Test requires Python 2.7+.")
@@ -363,6 +373,11 @@ def test_invalid_input(self):
363373
('0a 1\n# EOF\n'),
364374
('a.b 1\n# EOF\n'),
365375
('a-b 1\n# EOF\n'),
376+
# Bad timestamp.
377+
('a 1 1 \n# EOF\n'),
378+
('a 1 z\n# EOF\n'),
379+
('a 1 1z\n# EOF\n'),
380+
('a 1 1.1.1\n# EOF\n'),
366381
]:
367382
with self.assertRaises(ValueError):
368383
list(text_string_to_metric_families(case))

0 commit comments

Comments
 (0)
0