@@ -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 )
@@ -186,16 +210,14 @@ class Counter(object):
186210 _type = 'counter'
187211 _reserved_labelnames = []
188212
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 )
192215
193216 def inc (self , amount = 1 ):
194217 '''Increment counter by the given amount.'''
195218 if amount < 0 :
196219 raise ValueError ('Counters can only be incremented by non-negative amounts.' )
197- with self ._lock :
198- self ._value += amount
220+ self ._value .inc (amount )
199221
200222 def count_exceptions (self , exception = Exception ):
201223 '''Count exceptions in a block of code or function.
@@ -226,33 +248,28 @@ def wrapped(*args, **kwargs):
226248 return ExceptionCounter (self )
227249
228250 def _samples (self ):
229- with self ._lock :
230- return (('' , {}, self ._value ), )
251+ return (('' , {}, self ._value .get ()), )
231252
232253
233254@_MetricWrapper
234255class Gauge (object ):
235256 _type = 'gauge'
236257 _reserved_labelnames = []
237258
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 )
241261
242262 def inc (self , amount = 1 ):
243263 '''Increment gauge by the given amount.'''
244- with self ._lock :
245- self ._value += amount
264+ self ._value .inc (amount )
246265
247266 def dec (self , amount = 1 ):
248267 '''Decrement gauge by the given amount.'''
249- with self ._lock :
250- self ._value -= amount
268+ self ._value .inc (- amount )
251269
252270 def set (self , value ):
253271 '''Set gauge to the given value.'''
254- with self ._lock :
255- self ._value = float (value )
272+ self ._value .set (float (value ))
256273
257274 def set_to_current_time (self ):
258275 '''Set gauge to the current unixtime.'''
@@ -323,25 +340,22 @@ def samples(self):
323340 self ._samples = types .MethodType (samples , self )
324341
325342 def _samples (self ):
326- with self ._lock :
327- return (('' , {}, self ._value ), )
343+ return (('' , {}, self ._value .get ()), )
328344
329345
330346@_MetricWrapper
331347class Summary (object ):
332348 _type = 'summary'
333349 _reserved_labelnames = ['quantile' ]
334350
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 )
339354
340355 def observe (self , amount ):
341356 '''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 )
345359
346360 def time (self ):
347361 '''Time a block of code or function, and observe the duration in seconds.
@@ -370,10 +384,9 @@ def wrapped(*args, **kwargs):
370384 return Timer (self )
371385
372386 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 ()))
377390
378391
379392def _floatToGoString (d ):
@@ -390,9 +403,8 @@ class Histogram(object):
390403 _type = 'histogram'
391404 _reserved_labelnames = ['histogram' ]
392405
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 )
396408 buckets = [float (b ) for b in buckets ]
397409 if buckets != sorted (buckets ):
398410 # 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
403415 if len (buckets ) < 2 :
404416 raise ValueError ('Must have at least two buckets' )
405417 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 ),)))
407422
408423 def observe (self , amount ):
409424 '''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
416430
417431 def time (self ):
418432 '''Time a block of code or function, and observe the duration in seconds.
@@ -441,13 +455,12 @@ def wrapped(*args, **kwargs):
441455 return Timer (self )
442456
443457 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 )
453466
0 commit comments