@@ -88,11 +88,34 @@ def add_sample(self, name, labels, value):
88
88
self ._samples .append ((name , labels , value ))
89
89
90
90
91
+ class _MutexValue (object ):
92
+ '''A float protected by a mutex.'''
93
+
94
+ def __init__ (self , name , labelnames , labelvalues ):
95
+ self ._value = 0.0
96
+ self ._lock = Lock ()
97
+
98
+ def inc (self , amount ):
99
+ with self ._lock :
100
+ self ._value += amount
101
+
102
+ def set (self , value ):
103
+ with self ._lock :
104
+ self ._value = value
105
+
106
+ def get (self ):
107
+ with self ._lock :
108
+ return self ._value
109
+
110
+ _ValueClass = _MutexValue
111
+
112
+
91
113
class _LabelWrapper (object ):
92
114
'''Handles labels for the wrapped metric.'''
93
- def __init__ (self , wrappedClass , labelnames , ** kwargs ):
115
+ def __init__ (self , wrappedClass , name , labelnames , ** kwargs ):
94
116
self ._wrappedClass = wrappedClass
95
117
self ._type = wrappedClass ._type
118
+ self ._name = name
96
119
self ._labelnames = labelnames
97
120
self ._kwargs = kwargs
98
121
self ._lock = Lock ()
@@ -122,7 +145,7 @@ def labels(self, *labelvalues):
122
145
labelvalues = tuple ([unicode (l ) for l in labelvalues ])
123
146
with self ._lock :
124
147
if labelvalues not in self ._metrics :
125
- self ._metrics [labelvalues ] = self ._wrappedClass (** self ._kwargs )
148
+ self ._metrics [labelvalues ] = self ._wrappedClass (self . _name , self . _labelnames , labelvalues , ** self ._kwargs )
126
149
return self ._metrics [labelvalues ]
127
150
128
151
def remove (self , * labelvalues ):
@@ -145,24 +168,25 @@ def _samples(self):
145
168
def _MetricWrapper (cls ):
146
169
'''Provides common functionality for metrics.'''
147
170
def init (name , documentation , labelnames = (), namespace = '' , subsystem = '' , registry = REGISTRY , ** kwargs ):
171
+ full_name = ''
172
+ if namespace :
173
+ full_name += namespace + '_'
174
+ if subsystem :
175
+ full_name += subsystem + '_'
176
+ full_name += name
177
+
148
178
if labelnames :
179
+ labelnames = tuple (labelnames )
149
180
for l in labelnames :
150
181
if not _METRIC_LABEL_NAME_RE .match (l ):
151
182
raise ValueError ('Invalid label metric name: ' + l )
152
183
if _RESERVED_METRIC_LABEL_NAME_RE .match (l ):
153
184
raise ValueError ('Reserved label metric name: ' + l )
154
185
if l in cls ._reserved_labelnames :
155
186
raise ValueError ('Reserved label metric name: ' + l )
156
- collector = _LabelWrapper (cls , labelnames , ** kwargs )
187
+ collector = _LabelWrapper (cls , name , labelnames , ** kwargs )
157
188
else :
158
- collector = cls (** kwargs )
159
-
160
- full_name = ''
161
- if namespace :
162
- full_name += namespace + '_'
163
- if subsystem :
164
- full_name += subsystem + '_'
165
- full_name += name
189
+ collector = cls (name , labelnames , (), ** kwargs )
166
190
167
191
if not _METRIC_NAME_RE .match (full_name ):
168
192
raise ValueError ('Invalid metric name: ' + full_name )
@@ -186,16 +210,14 @@ class Counter(object):
186
210
_type = 'counter'
187
211
_reserved_labelnames = []
188
212
189
- def __init__ (self ):
190
- self ._value = 0.0
191
- self ._lock = Lock ()
213
+ def __init__ (self , name , labelnames , labelvalues ):
214
+ self ._value = _ValueClass (name , labelnames , labelvalues )
192
215
193
216
def inc (self , amount = 1 ):
194
217
'''Increment counter by the given amount.'''
195
218
if amount < 0 :
196
219
raise ValueError ('Counters can only be incremented by non-negative amounts.' )
197
- with self ._lock :
198
- self ._value += amount
220
+ self ._value .inc (amount )
199
221
200
222
def count_exceptions (self , exception = Exception ):
201
223
'''Count exceptions in a block of code or function.
@@ -226,33 +248,28 @@ def wrapped(*args, **kwargs):
226
248
return ExceptionCounter (self )
227
249
228
250
def _samples (self ):
229
- with self ._lock :
230
- return (('' , {}, self ._value ), )
251
+ return (('' , {}, self ._value .get ()), )
231
252
232
253
233
254
@_MetricWrapper
234
255
class Gauge (object ):
235
256
_type = 'gauge'
236
257
_reserved_labelnames = []
237
258
238
- def __init__ (self ):
239
- self ._value = 0.0
240
- self ._lock = Lock ()
259
+ def __init__ (self , name , labelnames , labelvalues ):
260
+ self ._value = _ValueClass (name , labelnames , labelvalues )
241
261
242
262
def inc (self , amount = 1 ):
243
263
'''Increment gauge by the given amount.'''
244
- with self ._lock :
245
- self ._value += amount
264
+ self ._value .inc (amount )
246
265
247
266
def dec (self , amount = 1 ):
248
267
'''Decrement gauge by the given amount.'''
249
- with self ._lock :
250
- self ._value -= amount
268
+ self ._value .inc (- amount )
251
269
252
270
def set (self , value ):
253
271
'''Set gauge to the given value.'''
254
- with self ._lock :
255
- self ._value = float (value )
272
+ self ._value .set (float (value ))
256
273
257
274
def set_to_current_time (self ):
258
275
'''Set gauge to the current unixtime.'''
@@ -323,25 +340,22 @@ def samples(self):
323
340
self ._samples = types .MethodType (samples , self )
324
341
325
342
def _samples (self ):
326
- with self ._lock :
327
- return (('' , {}, self ._value ), )
343
+ return (('' , {}, self ._value .get ()), )
328
344
329
345
330
346
@_MetricWrapper
331
347
class Summary (object ):
332
348
_type = 'summary'
333
349
_reserved_labelnames = ['quantile' ]
334
350
335
- def __init__ (self ):
336
- self ._count = 0.0
337
- self ._sum = 0.0
338
- self ._lock = Lock ()
351
+ def __init__ (self , name , labelnames , labelvalues ):
352
+ self ._count = _ValueClass (name + '_count' , labelnames , labelvalues )
353
+ self ._sum = _ValueClass (name + '_sum' , labelnames , labelvalues )
339
354
340
355
def observe (self , amount ):
341
356
'''Observe the given amount.'''
342
- with self ._lock :
343
- self ._count += 1
344
- self ._sum += amount
357
+ self ._count .inc (1 )
358
+ self ._sum .inc (amount )
345
359
346
360
def time (self ):
347
361
'''Time a block of code or function, and observe the duration in seconds.
@@ -370,10 +384,9 @@ def wrapped(*args, **kwargs):
370
384
return Timer (self )
371
385
372
386
def _samples (self ):
373
- with self ._lock :
374
- return (
375
- ('_count' , {}, self ._count ),
376
- ('_sum' , {}, self ._sum ))
387
+ return (
388
+ ('_count' , {}, self ._count .get ()),
389
+ ('_sum' , {}, self ._sum .get ()))
377
390
378
391
379
392
def _floatToGoString (d ):
@@ -390,9 +403,8 @@ class Histogram(object):
390
403
_type = 'histogram'
391
404
_reserved_labelnames = ['histogram' ]
392
405
393
- def __init__ (self , buckets = (.005 , .01 , .025 , .05 , .075 , .1 , .25 , .5 , .75 , 1.0 , 2.5 , 5.0 , 7.5 , 10.0 , _INF )):
394
- self ._sum = 0.0
395
- self ._lock = Lock ()
406
+ def __init__ (self , name , labelnames , labelvalues , buckets = (.005 , .01 , .025 , .05 , .075 , .1 , .25 , .5 , .75 , 1.0 , 2.5 , 5.0 , 7.5 , 10.0 , _INF )):
407
+ self ._sum = _ValueClass (name + '_sum' , labelnames , labelvalues )
396
408
buckets = [float (b ) for b in buckets ]
397
409
if buckets != sorted (buckets ):
398
410
# This is probably an error on the part of the user,
@@ -403,16 +415,18 @@ def __init__(self, buckets=(.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2
403
415
if len (buckets ) < 2 :
404
416
raise ValueError ('Must have at least two buckets' )
405
417
self ._upper_bounds = buckets
406
- self ._buckets = [0.0 ] * len (buckets )
418
+ self ._buckets = []
419
+ bucket_labelnames = labelnames + ('le' ,)
420
+ for b in buckets :
421
+ self ._buckets .append (_ValueClass (name + '_bucket' , bucket_labelnames , labelvalues + (_floatToGoString (b ),)))
407
422
408
423
def observe (self , amount ):
409
424
'''Observe the given amount.'''
410
- with self ._lock :
411
- self ._sum += amount
412
- for i , bound in enumerate (self ._upper_bounds ):
413
- if amount <= bound :
414
- self ._buckets [i ] += 1
415
- break
425
+ self ._sum .inc (amount )
426
+ for i , bound in enumerate (self ._upper_bounds ):
427
+ if amount <= bound :
428
+ self ._buckets [i ].inc (1 )
429
+ break
416
430
417
431
def time (self ):
418
432
'''Time a block of code or function, and observe the duration in seconds.
@@ -441,13 +455,12 @@ def wrapped(*args, **kwargs):
441
455
return Timer (self )
442
456
443
457
def _samples (self ):
444
- with self ._lock :
445
- samples = []
446
- acc = 0
447
- for i , bound in enumerate (self ._upper_bounds ):
448
- acc += self ._buckets [i ]
449
- samples .append (('_bucket' , {'le' : _floatToGoString (bound )}, acc ))
450
- samples .append (('_count' , {}, acc ))
451
- samples .append (('_sum' , {}, self ._sum ))
452
- return tuple (samples )
458
+ samples = []
459
+ acc = 0
460
+ for i , bound in enumerate (self ._upper_bounds ):
461
+ acc += self ._buckets [i ].get ()
462
+ samples .append (('_bucket' , {'le' : _floatToGoString (bound )}, acc ))
463
+ samples .append (('_count' , {}, acc ))
464
+ samples .append (('_sum' , {}, self ._sum .get ()))
465
+ return tuple (samples )
453
466
0 commit comments