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