24
24
_MINUS_INF = float ("-inf" )
25
25
26
26
27
-
28
27
class CollectorRegistry (object ):
29
28
'''Metric collector registry.
30
-
29
+
31
30
Collectors must have a no-argument method 'collect' that returns a list of
32
31
Metric objects. The returned metrics should be consistent with the Prometheus
33
32
exposition formats.
@@ -57,7 +56,7 @@ def collect(self):
57
56
58
57
def get_sample_value (self , name , labels = None ):
59
58
'''Returns the sample value, or None if not found.
60
-
59
+
61
60
This is inefficient, and intended only for use in unittests.
62
61
'''
63
62
if labels is None :
@@ -74,6 +73,7 @@ def get_sample_value(self, name, labels=None):
74
73
75
74
_METRIC_TYPES = ('counter' , 'gauge' , 'summary' , 'histogram' , 'untyped' )
76
75
76
+
77
77
class Metric (object ):
78
78
'''A single metric and it's samples.'''
79
79
def __init__ (self , name , documentation , typ ):
@@ -168,10 +168,12 @@ def collect():
168
168
169
169
return init
170
170
171
+
171
172
@_MetricWrapper
172
173
class Counter (object ):
173
174
_type = 'counter'
174
175
_reserved_labelnames = []
176
+
175
177
def __init__ (self ):
176
178
self ._value = 0.0
177
179
self ._lock = Lock ()
@@ -193,10 +195,14 @@ def count_exceptions(self, exception=Exception):
193
195
class ExceptionCounter (object ):
194
196
def __init__ (self , counter ):
195
197
self ._counter = counter
196
- def __enter__ (self ): pass
198
+
199
+ def __enter__ (self ):
200
+ pass
201
+
197
202
def __exit__ (self , typ , value , traceback ):
198
203
if isinstance (value , exception ):
199
204
self ._counter .inc ()
205
+
200
206
def __call__ (self , f ):
201
207
@wraps (f )
202
208
def wrapped (* args , ** kwargs ):
@@ -209,10 +215,12 @@ def _samples(self):
209
215
with self ._lock :
210
216
return (('' , {}, self ._value ), )
211
217
218
+
212
219
@_MetricWrapper
213
220
class Gauge (object ):
214
221
_type = 'gauge'
215
222
_reserved_labelnames = []
223
+
216
224
def __init__ (self ):
217
225
self ._value = 0.0
218
226
self ._lock = Lock ()
@@ -246,10 +254,13 @@ def track_inprogress(self):
246
254
class InprogressTracker (object ):
247
255
def __init__ (self , gauge ):
248
256
self ._gauge = gauge
257
+
249
258
def __enter__ (self ):
250
259
self ._gauge .inc ()
260
+
251
261
def __exit__ (self , typ , value , traceback ):
252
262
self ._gauge .dec ()
263
+
253
264
def __call__ (self , f ):
254
265
@wraps (f )
255
266
def wrapped (* args , ** kwargs ):
@@ -262,10 +273,12 @@ def _samples(self):
262
273
with self ._lock :
263
274
return (('' , {}, self ._value ), )
264
275
276
+
265
277
@_MetricWrapper
266
278
class Summary (object ):
267
279
_type = 'summary'
268
280
_reserved_labelnames = ['quantile' ]
281
+
269
282
def __init__ (self ):
270
283
self ._count = 0.0
271
284
self ._sum = 0.0
@@ -285,11 +298,14 @@ def time(self):
285
298
class Timer (object ):
286
299
def __init__ (self , summary ):
287
300
self ._summary = summary
301
+
288
302
def __enter__ (self ):
289
303
self ._start = time .time ()
304
+
290
305
def __exit__ (self , typ , value , traceback ):
291
306
# Time can go backwards.
292
307
self ._summary .observe (max (time .time () - self ._start , 0 ))
308
+
293
309
def __call__ (self , f ):
294
310
@wraps (f )
295
311
def wrapped (* args , ** kwargs ):
@@ -304,6 +320,7 @@ def _samples(self):
304
320
('_count' , {}, self ._count ),
305
321
('_sum' , {}, self ._sum ))
306
322
323
+
307
324
def _floatToGoString (d ):
308
325
if d == _INF :
309
326
return '+Inf'
@@ -312,14 +329,16 @@ def _floatToGoString(d):
312
329
else :
313
330
return repr (d )
314
331
332
+
315
333
@_MetricWrapper
316
334
class Histogram (object ):
317
335
_type = 'histogram'
318
336
_reserved_labelnames = ['histogram' ]
337
+
319
338
def __init__ (self , buckets = (.005 , .01 , .025 , .05 , .075 , .1 , .25 , .5 , .75 , 1.0 , 2.5 , 5.0 , 7.5 , 10.0 , _INF )):
320
339
self ._sum = 0.0
321
340
self ._lock = Lock ()
322
- buckets = [float (b ) for b in buckets ]
341
+ buckets = [float (b ) for b in buckets ]
323
342
if buckets != sorted (buckets ):
324
343
# This is probably an error on the part of the user,
325
344
# so raise rather than sorting for them.
@@ -348,11 +367,14 @@ def time(self):
348
367
class Timer (object ):
349
368
def __init__ (self , histogram ):
350
369
self ._histogram = histogram
370
+
351
371
def __enter__ (self ):
352
372
self ._start = time .time ()
373
+
353
374
def __exit__ (self , typ , value , traceback ):
354
375
# Time can go backwards.
355
376
self ._histogram .observe (max (time .time () - self ._start , 0 ))
377
+
356
378
def __call__ (self , f ):
357
379
@wraps (f )
358
380
def wrapped (* args , ** kwargs ):
@@ -372,10 +394,11 @@ def _samples(self):
372
394
samples .append (('_sum' , {}, self ._sum ))
373
395
return tuple (samples )
374
396
375
-
397
+
376
398
CONTENT_TYPE_LATEST = 'text/plain; version=0.0.4; charset=utf-8'
377
399
'''Content type of the latest text format'''
378
400
401
+
379
402
def generate_latest (registry = REGISTRY ):
380
403
'''Returns the metrics from the registry in latest text format as a string.'''
381
404
output = []
@@ -402,6 +425,7 @@ def do_GET(self):
402
425
self .end_headers ()
403
426
self .wfile .write (generate_latest (REGISTRY ))
404
427
428
+
405
429
def write_to_textfile (path , registry ):
406
430
'''Write metrics to the given path.
407
431
@@ -417,13 +441,13 @@ def write_to_textfile(path, registry):
417
441
if __name__ == '__main__' :
418
442
c = Counter ('cc' , 'A counter' )
419
443
c .inc ()
420
-
444
+
421
445
g = Gauge ('gg' , 'A gauge' )
422
446
g .set (17 )
423
-
447
+
424
448
s = Summary ('ss' , 'A summary' , ['a' , 'b' ])
425
449
s .labels ('c' , 'd' ).observe (17 )
426
-
450
+
427
451
h = Histogram ('hh' , 'A histogram' )
428
452
h .observe (.6 )
429
453
0 commit comments