@@ -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 )
@@ -203,16 +227,14 @@ class Counter(object):
203
227
_type = 'counter'
204
228
_reserved_labelnames = []
205
229
206
- def __init__ (self ):
207
- self ._value = 0.0
208
- self ._lock = Lock ()
230
+ def __init__ (self , name , labelnames , labelvalues ):
231
+ self ._value = _ValueClass (name , labelnames , labelvalues )
209
232
210
233
def inc (self , amount = 1 ):
211
234
'''Increment counter by the given amount.'''
212
235
if amount < 0 :
213
236
raise ValueError ('Counters can only be incremented by non-negative amounts.' )
214
- with self ._lock :
215
- self ._value += amount
237
+ self ._value .inc (amount )
216
238
217
239
def count_exceptions (self , exception = Exception ):
218
240
'''Count exceptions in a block of code or function.
@@ -243,8 +265,7 @@ def wrapped(*args, **kwargs):
243
265
return ExceptionCounter (self )
244
266
245
267
def _samples (self ):
246
- with self ._lock :
247
- return (('' , {}, self ._value ), )
268
+ return (('' , {}, self ._value .get ()), )
248
269
249
270
250
271
@_MetricWrapper
@@ -269,24 +290,20 @@ class Gauge(object):
269
290
_type = 'gauge'
270
291
_reserved_labelnames = []
271
292
272
- def __init__ (self ):
273
- self ._value = 0.0
274
- self ._lock = Lock ()
293
+ def __init__ (self , name , labelnames , labelvalues ):
294
+ self ._value = _ValueClass (name , labelnames , labelvalues )
275
295
276
296
def inc (self , amount = 1 ):
277
297
'''Increment gauge by the given amount.'''
278
- with self ._lock :
279
- self ._value += amount
298
+ self ._value .inc (amount )
280
299
281
300
def dec (self , amount = 1 ):
282
301
'''Decrement gauge by the given amount.'''
283
- with self ._lock :
284
- self ._value -= amount
302
+ self ._value .inc (- amount )
285
303
286
304
def set (self , value ):
287
305
'''Set gauge to the given value.'''
288
- with self ._lock :
289
- self ._value = float (value )
306
+ self ._value .set (float (value ))
290
307
291
308
def set_to_current_time (self ):
292
309
'''Set gauge to the current unixtime.'''
@@ -357,8 +374,7 @@ def samples(self):
357
374
self ._samples = types .MethodType (samples , self )
358
375
359
376
def _samples (self ):
360
- with self ._lock :
361
- return (('' , {}, self ._value ), )
377
+ return (('' , {}, self ._value .get ()), )
362
378
363
379
364
380
@_MetricWrapper
@@ -388,16 +404,14 @@ def create_response(request):
388
404
_type = 'summary'
389
405
_reserved_labelnames = ['quantile' ]
390
406
391
- def __init__ (self ):
392
- self ._count = 0.0
393
- self ._sum = 0.0
394
- self ._lock = Lock ()
407
+ def __init__ (self , name , labelnames , labelvalues ):
408
+ self ._count = _ValueClass (name + '_count' , labelnames , labelvalues )
409
+ self ._sum = _ValueClass (name + '_sum' , labelnames , labelvalues )
395
410
396
411
def observe (self , amount ):
397
412
'''Observe the given amount.'''
398
- with self ._lock :
399
- self ._count += 1
400
- self ._sum += amount
413
+ self ._count .inc (1 )
414
+ self ._sum .inc (amount )
401
415
402
416
def time (self ):
403
417
'''Time a block of code or function, and observe the duration in seconds.
@@ -426,10 +440,9 @@ def wrapped(*args, **kwargs):
426
440
return Timer (self )
427
441
428
442
def _samples (self ):
429
- with self ._lock :
430
- return (
431
- ('_count' , {}, self ._count ),
432
- ('_sum' , {}, self ._sum ))
443
+ return (
444
+ ('_count' , {}, self ._count .get ()),
445
+ ('_sum' , {}, self ._sum .get ()))
433
446
434
447
435
448
def _floatToGoString (d ):
@@ -473,9 +486,8 @@ def create_response(request):
473
486
_type = 'histogram'
474
487
_reserved_labelnames = ['histogram' ]
475
488
476
- def __init__ (self , buckets = (.005 , .01 , .025 , .05 , .075 , .1 , .25 , .5 , .75 , 1.0 , 2.5 , 5.0 , 7.5 , 10.0 , _INF )):
477
- self ._sum = 0.0
478
- self ._lock = Lock ()
489
+ 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 )):
490
+ self ._sum = _ValueClass (name + '_sum' , labelnames , labelvalues )
479
491
buckets = [float (b ) for b in buckets ]
480
492
if buckets != sorted (buckets ):
481
493
# This is probably an error on the part of the user,
@@ -486,16 +498,18 @@ def __init__(self, buckets=(.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2
486
498
if len (buckets ) < 2 :
487
499
raise ValueError ('Must have at least two buckets' )
488
500
self ._upper_bounds = buckets
489
- self ._buckets = [0.0 ] * len (buckets )
501
+ self ._buckets = []
502
+ bucket_labelnames = labelnames + ('le' ,)
503
+ for b in buckets :
504
+ self ._buckets .append (_ValueClass (name + '_bucket' , bucket_labelnames , labelvalues + (_floatToGoString (b ),)))
490
505
491
506
F42D
def observe (self , amount ):
492
507
'''Observe the given amount.'''
493
- with self ._lock :
494
- self ._sum += amount
495
- for i , bound in enumerate (self ._upper_bounds ):
496
- if amount <= bound :
497
- self ._buckets [i ] += 1
498
- break
508
+ self ._sum .inc (amount )
509
+ for i , bound in enumerate (self ._upper_bounds ):
510
+ if amount <= bound :
511
+ self ._buckets [i ].inc (1 )
512
+ break
499
513
500
514
def time (self ):
501
515
'''Time a block of code or function, and observe the duration in seconds.
@@ -524,13 +538,12 @@ def wrapped(*args, **kwargs):
524
538
return Timer (self )
525
539
526
540
def _samples (self ):
527
- with self ._lock :
528
- samples = []
529
- acc = 0
530
- for i , bound in enumerate (self ._upper_bounds ):
531
- acc += self ._buckets [i ]
532
- samples .append (('_bucket' , {'le' : _floatToGoString (bound )}, acc ))
533
- samples .append (('_count' , {}, acc ))
534
- samples .append (('_sum' , {}, self ._sum ))
535
- return tuple (samples )
541
+ samples = []
542
+ acc = 0
543
+ for i , bound in enumerate (self ._upper_bounds ):
544
+ acc += self ._buckets [i ].get ()
545
+ samples .append (('_bucket' , {'le' : _floatToGoString (bound )}, acc ))
546
+ samples .append (('_count' , {}, acc ))
547
+ samples .append (('_sum' , {}, self ._sum .get ()))
548
+ return tuple (samples )
536
549
0 commit comments