@@ -88,11 +88,34 @@ def add_sample(self, name, labels, value):
8888 self ._samples .append ((name , labels , value ))
8989
9090
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+
91113class _LabelWrapper (object ):
92114 '''Handles labels for the wrapped metric.'''
93- def __init__ (self , wrappedClass , labelnames , ** kwargs ):
115+ def __init__ (self , wrappedClass , name , labelnames , ** kwargs ):
94116 self ._wrappedClass = wrappedClass
95117 self ._type = wrappedClass ._type
118+ self ._name = name
96119 self ._labelnames = labelnames
97120 self ._kwargs = kwargs
98121 self ._lock = Lock ()
@@ -122,7 +145,7 @@ def labels(self, *labelvalues):
122145 labelvalues = tuple ([unicode (l ) for l in labelvalues ])
123146 with self ._lock :
124147 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 )
126149 return self ._metrics [labelvalues ]
127150
128151 def remove (self , * labelvalues ):
@@ -145,24 +168,25 @@ def _samples(self):
145168def _MetricWrapper (cls ):
146169 '''Provides common functionality for metrics.'''
147170 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+
148178 if labelnames :
179+ labelnames = tuple (labelnames )
149180 for l in labelnames :
150181 if not _METRIC_LABEL_NAME_RE .match (l ):
151182 raise ValueError ('Invalid label metric name: ' + l )
152183 if _RESERVED_METRIC_LABEL_NAME_RE .match (l ):
153184 raise ValueError ('Reserved label metric name: ' + l )
154185 if l in cls ._reserved_labelnames :
155186 raise ValueError ('Reserved label metric name: ' + l )
156- collector = _LabelWrapper (cls , labelnames , ** kwargs )
187+ collector = _LabelWrapper (cls , name , labelnames , ** kwargs )
157188 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 )
166190
167191 if not _METRIC_NAME_RE .match (full_name ):
168192 raise ValueError ('Invalid metric name: ' + full_name )
@@ -203,16 +227,14 @@ class Counter(object):
203227 _type = 'counter'
204228 _reserved_labelnames = []
205229
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 )
209232
210233 def inc (self , amount = 1 ):
211234 '''Increment counter by the given amount.'''
212235 if amount < 0 :
213236 raise ValueError ('Counters can only be incremented by non-negative amounts.' )
214- with self ._lock :
215- self ._value += amount
237+ self ._value .inc (amount )
216238
217239 def count_exceptions (self , exception = Exception ):
218240 '''Count exceptions in a block of code or function.
@@ -243,8 +265,7 @@ def wrapped(*args, **kwargs):
243265 return ExceptionCounter (self )
244266
245267 def _samples (self ):
246- with self ._lock :
247- return (('' , {}, self ._value ), )
268+ return (('' , {}, self ._value .get ()), )
248269
249270
250271@_MetricWrapper
@@ -269,24 +290,20 @@ class Gauge(object):
269290 _type = 'gauge'
270291 _reserved_labelnames = []
271292
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 )
275295
276296 def inc (self , amount = 1 ):
277297 '''Increment gauge by the given amount.'''
278- with self ._lock :
279- self ._value += amount
298+ self ._value .inc (amount )
280299
281300 def dec (self , amount = 1 ):
282301 '''Decrement gauge by the given amount.'''
283- with self ._lock :
284- self ._value -= amount
302+ self ._value .inc (- amount )
285303
286304 def set (self , value ):
287305 '''Set gauge to the given value.'''
288- with self ._lock :
289- self ._value = float (value )
306+ self ._value .set (float (value ))
290307
291308 def set_to_current_time (self ):
292309 '''Set gauge to the current unixtime.'''
@@ -357,8 +374,7 @@ def samples(self):
357374 self ._samples = types .MethodType (samples , self )
358375
359376 def _samples (self ):
360- with self ._lock :
361- return (('' , {}, self ._value ), )
377+ return (('' , {}, self ._value .get ()), )
362378
363379
364380@_MetricWrapper
@@ -388,16 +404,14 @@ def create_response(request):
388404 _type = 'summary'
389405 _reserved_labelnames = ['quantile' ]
390406
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 )
395410
396411 def observe (self , amount ):
397412 '''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 )
401415
402416 def time (self ):
403417 '''Time a block of code or function, and observe the duration in seconds.
@@ -426,10 +440,9 @@ def wrapped(*args, **kwargs):
426440 return Timer (self )
427441
428442 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 ()))
433446
434447
435448def _floatToGoString (d ):
@@ -473,9 +486,8 @@ def create_response(request):
473486 _type = 'histogram'
474487 _reserved_labelnames = ['histogram' ]
475488
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 )
479491 buckets = [float (b ) for b in buckets ]
480492 if buckets != sorted (buckets ):
481493 # 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
486498 if len (buckets ) < 2 :
487499 raise ValueError ('Must have at least two buckets' )
488500 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 ),)))
490505
491506 def observe (self , amount ):
492507 '''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
499513
500514 def time (self ):
501515 '''Time a block of code or function, and observe the duration in seconds.
@@ -524,13 +538,12 @@ def wrapped(*args, **kwargs):
524538 return Timer (self )
525539
526540 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 )
536549
0 commit comments