8000 Merge pull request #54 from prometheus/parser · bugcy013/client_python@ac1c45f · GitHub
[go: up one dir, main page]

Skip to content

Commit ac1c45f

Browse files
committed
Merge pull request prometheus#54 from prometheus/parser
WIP: Text format parser for Python
2 parents fc5a6e3 + a2dae6c commit ac1c45f

9 files changed

+865
-206
lines changed

prometheus_client/bridge/graphite.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ def push(self, prefix=''):
5959
prefixstr = prefix + '.'
6060

6161
for metric in self._registry.collect():
62-
for name, labels, value in metric._samples:
62+
for name, labels, value in metric.samples:
6363
if labels:
6464
labelstr = '.' + '.'.join(
6565
['{0}.{1}'.format(
66-
_sanitize(k), _sanitize(v))
67-
for k, v in sorted(labels.items())])
66+
_sanitize(k), _sanitize(v))
67+
for k, v in sorted(labels.items())])
6868
else:
6969
labelstr = ''
7070
output.append('{0}{1}{2} {3} {4}\n'.format(

prometheus_client/core.py

Lines changed: 139 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import unicode_literals
44

55
import copy
6+
import math
67
import re
78
import time
89
import types
@@ -61,7 +62,7 @@ def get_sample_value(self, name, labels=None):
6162
if labels is None:
6263
labels = {}
6364
for metric in self.collect():
64-
for n, l, value in metric._samples:
65+
for n, l, value in metric.samples:
6566
if n == name and l == labels:
6667
return value
6768
return None
@@ -74,18 +75,145 @@ def get_sample_value(self, name, labels=None):
7475

7576

7677
class Metric(object):
77-
'''A single metric and it's samples.'''
78+
'''A single metric family and its samples.
79+
80+
This is intended only for internal use by the instrumentation client.
81+
82+
Custom collectors should use GaugeMetricFamily, CounterMetricFamily
83+
and SummaryMetricFamily instead.
84+
'''
7885
def __init__(self, name, documentation, typ):
79-
self._name = name
80-
self._documentation = documentation
86+
self.name = name
87+
self.documentation = documentation
8188
if typ not in _METRIC_TYPES:
8289
raise ValueError('Invalid metric type: ' + typ)
83-
self._type = typ
84-
self._samples = []
90+
self.type = typ
91+
self.samples = []
8592

86-
'''Add a sample to the metric'''
8793
def add_sample(self, name, labels, value):
88-
self._samples.append((name, labels, value))
94+
'''Add a sample to the metric.
95+
96+
Internal-only, do not use.'''
97+
self.samples.append((name, labels, value))
98+
99+
def __eq__(self, other):
100+
return (isinstance(other, Metric)
101+
and self.name == other.name
102+
and self.documentation == other.documentation
103+
and self.type == other.type
104+
and self.samples == other.samples)
105+
106+
107+
class CounterMetricFamily(Metric):
108+
'''A single counter and its samples.
109+
110+
For use by custom collectors.
111+
'''
112+
def __init__(self, name, documentation, value=None, labels=None):
113+
Metric.__init__(self, name, documentation, 'counter')
114+
if labels is not None and value is not None:
115+
raise ValueError('Can only specify at most one of value and labels.')
116+
if labels is None:
117+
labels = []
118+
self._labelnames = labels
119+
if value is not None:
120+
self.add_metric([], value)
121+
122+
def add_metric(self, labels, value):
123+
'''Add a metric to the metric family.
124+
125+
Args:
126+
labels: A list of label values
127+
value: The value of the metric.
128+
'''
129+
self.samples.append((self.name, dict(zip(self._labelnames, labels)), value))
130+
131+
132+
class GaugeMetricFamily(Metric):
133+
'''A single gauge and its samples.
134+
135+
For use by custom collectors.
136+
'''
137+
def __init__(self, name, documentation, value=None, labels=None):
138+
Metric.__init__(self, name, documentation, 'gauge')
139+
if labels is not None and value is not None:
140+
raise ValueError('Can only specify at most one of value and labels.')
141+
if labels is None:
142+
labels = []
143+
self._labelnames = labels
144+
if value is not None:
145+
self.add_metric([], value)
146+
147+
def add_metric(self, labels, value):
148+
'''Add a metric to the metric family.
149+
150+
Args:
151+
labels: A list of label values
152+
value: A float
153+
'''
154+
self.samples.append((self.name, dict(zip(self._labelnames, labels)), value))
155+
156+
157+
class SummaryMetricFamily(Metric):
158+
'''A single summary and its samples.
159+
160+
For use by custom collectors.
161+
'''
162+
def __init__(self, name, documentation, count_value=None, sum_value=None, labels=None):
163+
Metric.__init__(self, name, documentation, 'summary')
164+
if (sum_value is None) != (count_value is None):
165+
raise ValueError('count_value and sum_value must be provided together.')
166+
if labels is not None and count_value is not None:
167+
raise ValueError('Can only specify at most one of value and labels.')
168+
if labels is None:
169+
labels = []
170+
self._labelnames = labels
171+
if count_value is not None:
172+
self.add_metric([], count_value, sum_value)
173+
174+
def add_metric(self, labels, count_value, sum_value):
175+
'''Add a metric to the metric family.
176+
177+
Args:
178+
labels: A list of label values
179+
count_value: The count value of the metric.
180+
sum_value: The sum value of the metric.
181+
'''
182+
self.samples.append((self.name + '_count', dict(zip(self._labelnames, labels)), count_value))
183+
self.samples.append((self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value))
184+
185+
186+
class HistogramMetricFamily(Metric):
187+
'''A single histogram and its samples.
188+
189+
For use by custom collectors.
190+
'''
191+
def __init__(self, name, documentation, buckets=None, sum_value=None, labels=None):
192+
Metric.__init__(self, name, documentation, 'histogram')
193+
if (sum_value is None) != (buckets is None):
194+
raise ValueError('buckets and sum_value must be provided together.')
195+
if labels is not None and buckets is not None:
196+
raise ValueError('Can only specify at most one of buckets and labels.')
197+
if labels is None:
198+
labels = []
199+
self._labelnames = labels
200+
if buckets is not None:
201+
self.add_metric([], buckets, sum_value)
202+
203+
def add_metric(self, labels, buckets, sum_value):
204+
'''Add a metric to the metric family.
205+
206+
Args:
207+
labels: A list of label values
208+
buckets: A list of pairs of bucket names and values.
209+
The buckets must be sorted, and +Inf present.
210+
sum_value: The sum value of the metric.
211+
'''
212+
for bucket, value in buckets:
213+
self.samples.append((self.name + '_bucket', dict(list(zip(self._labelnames, labels)) + [('le', bucket)]), value))
214+
# +Inf is last and provides the count value.
215+
self.samples.append((self.name + '_count', dict(zip(self._labelnames, labels)), buckets[-1][1]))
216+
self.samples.append((self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value))
89217

90218

91219
class _MutexValue(object):
@@ -271,7 +399,7 @@ def _samples(self):
271399
@_MetricWrapper
272400
class Gauge(object):
273401
'''Gauge metric, to report instantaneous values.
274-
402+
275403
Examples of Gauges include:
276404
Inprogress requests
277405
Number of items in a queue
@@ -450,6 +578,8 @@ def _floatToGoString(d):
450578
return '+Inf'
451579
elif d == _MINUS_INF:
452580
return '-Inf'
581+
elif math.isnan(d):
582+
return 'NaN'
453583
else:
454584
return repr(float(d))
455585

prometheus_client/exposition.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ def generate_latest(registry=core.REGISTRY):
3232
output = []
3333
for metric in registry.collect():
3434
output.append('# HELP {0} {1}'.format(
35-
metric._name, metric._documentation.replace('\\', r'\\').replace('\n', r'\n')))
36-
output.append('\n# TYPE {0} {1}\n'.format(metric._name, metric._type))
37-
for name, labels, value in metric._samples:
35+
metric.name, metric.documentation.replace('\\', r'\\').replace('\n', r'\n')))
36+
output.append('\n# TYPE {0} {1}\n'.format(metric.name, metric.type))
37+
for name, labels, value in metric.samples:
3838
if labels:
3939
labelstr = '{{{0}}}'.format(','.join(
4040
['{0}="{1}"'.format(

0 commit comments

Comments
 (0)
0