@@ -78,6 +78,12 @@ def register(self, class_path):
78
78
except ImportError as e :
79
79
logger .warning ("Could not register %s metricset: %s" , class_path , compat .text_type (e ))
80
80
81
+ def get_metricset (self , class_path ):
82
+ try :
83
+ return self ._metricsets [class_path ]
84
+ except KeyError :
85
+ raise MetricSetNotFound (class_path )
86
+
81
87
def collect (self ):
82
88
"""
83
89
Collect metrics from all registered metric sets and queues them for sending
@@ -104,48 +110,64 @@ def _stop_collect_timer(self):
104
110
class MetricsSet (object ):
105
111
def __init__ (self , registry ):
106
112
self ._lock = threading .Lock ()
107
- self ._counters = {}
108
- self ._gauges = {}
113
+ self ._counters = defaultdict (dict )
114
+ self ._gauges = defaultdict (dict )
115
+ self ._timers = defaultdict (dict )
109
116
self ._registry = registry
110
117
111
- def counter (self , name , ** labels ):
118
+ def counter (self , name , reset_on_collect = False , ** labels ):
112
119
"""
113
120
Returns an existing or creates and returns a new counter
114
121
:param name: name of the counter
122
+ :param reset_on_collect: indicate if the counter should be reset to 0 when collecting
115
123
:param labels: a flat key/value map of labels
116
124
:return: the counter object
117
125
"""
118
- labels = self ._labels_to_key (labels )
119
- key = (name , labels )
120
- with self ._lock :
121
- if key not in self ._counters :
122
- if self ._registry ._ignore_patterns and any (
123
- pattern .match (name ) for pattern in self ._registry ._ignore_patterns
124
- ):
125
- counter = noop_metric
126
- else :
127
- counter = Counter (name )
128
- self ._counters [key ] = counter
129
- return self ._counters [key ]
126
+ return self ._metric (self ._counters , Counter , name , reset_on_collect , labels )
130
127
131
- def gauge (self , name , ** labels ):
128
+ def gauge (self , name , reset_on_collect = False , ** labels ):
132
129
"""
133
130
Returns an existing or creates and returns a new gauge
134
131
:param name: name of the gauge
132
+ :param reset_on_collect: indicate if the gouge should be reset to 0 when collecting
133
+ :param labels: a flat key/value map of labels
135
134
:return: the gauge object
136
135
"""
136
+ return self ._metric (self ._gauges , Gauge , name , reset_on_collect , labels )
137
+
138
+ def timer (self , name , reset_on_collect = False , ** labels ):
139
+ """
140
+ Returns an existing or creates and returns a new timer
141
+ :param name: name of the timer
142
+ :param reset_on_collect: indicate if the timer should be reset to 0 when collecting
143
+ :param labels: a flat key/value map of labels
144
+ :return: the timer object
145
+ """
146
+ return self ._metric (self ._timers , Timer , name , reset_on_collect , labels )
147
+
148
+ def _metric (self , container , metric_class , name , reset_on_collect , labels ):
149
+ """
150
+ Returns an existing or creates and returns a metric
151
+ :param container: the container for the metric
152
+ :param metric_class: the class of the metric
153
+ :param name: name of the metric
154
+ :param reset_on_collect: indicate if the metric should be reset to 0 when collecting
155
+ :param labels: a flat key/value map of labels
156
+ :return: the metric object
157
+ """
158
+
137
159
labels = self ._labels_to_key (labels )
138
160
key = (name , labels )
139
161
with self ._lock :
140
- if key not in self . _gauges :
162
+ if key not in container :
141
163
if self ._registry ._ignore_patterns and any (
142
164
pattern .match (name ) for pattern in self ._registry ._ignore_patterns
143
165
):
144
- gauge = noop_metric
166
+ metric = noop_metric
145
167
else :
146
- gauge = Gauge (name )
147
- self . _gauges [key ] = gauge
148
- return self . _gauges [key ]
168
+ metric = metric_class (name , reset_on_collect = reset_on_collect )
169
+ container [key ] = metric
170
+ return container [key ]
149
171
150
172
def collect (self ):
151
173
"""
@@ -166,16 +188,28 @@ def collect(self):
166
188
for (name , labels ), c in compat .iteritems (self ._counters ):
167
189
if c is not noop_metric :
168
190
samples [labels ].update ({name : {"value" : c .val }})
191
+ if c .reset_on_collect :
192
+ c .reset ()
169
193
if self ._gauges :
170
194
for (name , labels ), g in compat .iteritems (self ._gauges ):
171
195
if g is not noop_metric :
172
196
samples [labels ].update ({name : {"value" : g .val }})
197
+ if g .reset_on_collect :
198
+ g .reset ()
199
+ if self ._timers :
200
+ for (name , labels ), t in compat .iteritems (self ._timers ):
201
+ if t is not noop_metric :
202
+ val , count = t .val
203
+ samples [labels ].update ({name + ".sum.us" : {"value" : int (val * 1000000 )}})
204
+ samples [labels ].update ({name + ".count" : {"value" : count }})
205
+ if t .reset_on_collect :
206
+ t .reset ()
173
207
if samples :
174
208
for labels , sample in compat .iteritems (samples ):
175
209
result = {"samples" : sample , "timestamp" : timestamp }
176
210
if labels :
177
211
result ["tags" ] = {k : v for k , v in labels }
178
- yield result
212
+ yield self . before_yield ( result )
179
213
180
214
def before_collect (self ):
181
215
"""
@@ -184,22 +218,39 @@ def before_collect(self):
184
218
"""
185
219
pass
186
220
221
+ def before_yield (self , data ):
222
+ return data
223
+
187
224
def _labels_to_key (self , labels ):
188
225
return tuple ((k , compat .text_type (v )) for k , v in sorted (compat .iteritems (labels )))
189
226
190
227
228
+ class SpanBoundMetricSet (MetricsSet ):
229
+ def before_yield (self , data ):
230
+ tags = data .get ("tags" , None )
231
+ if tags :
232
+ span_type , span_subtype = tags .pop ("span.type" , None ), tags .pop ("span.subtype" , "" )
233
+ if span_type or span_subtype :
234
+ data ["span" ] = {"type" : span_type , "subtype" : span_subtype }
235
+ transaction_name , transaction_type = tags .pop ("transaction.name" , None ), tags .pop ("transaction.type" , None )
236
+ if transaction_name or transaction_type :
237
+ data ["transaction" ] = {"name" : transaction_name , "type" : transaction_type }
238
+ return data
239
+
240
+
191
241
class Counter (object ):
192
- __slots__ = ("label " , "_lock" , "_initial_value" , "_val" )
242
+ __slots__ = ("name " , "_lock" , "_initial_value" , "_val" , "reset_on_collect " )
193
243
194
- def __init__ (self , label , initial_value = 0 ):
244
+ def __init__ (self , name , initial_value = 0 , reset_on_collect = False ):
195
245
"""
196
246
Creates a new counter
197
- :param label: label of the counter
247
+ :param name: name of the counter
198
248
:param initial_value: initial value of the counter, defaults to 0
199
249
"""
200
- self .label = label
250
+ self .name = name
201
251
self ._lock = threading .Lock ()
202
252
self ._val = self ._initial_value = initial_value
253
+ self .reset_on_collect = reset_on_collect
203
254
204
255
def inc (self , delta = 1 ):
205
256
"""
@@ -237,15 +288,16 @@ def val(self):
237
288
238
289
239
290
class Gauge (object ):
240
- __slots__ = ("label " , "_val" )
291
+ __slots__ = ("name " , "_val" , "reset_on_collect " )
241
292
242
- def __init__ (self , label ):
293
+ def __init__ (self , name , reset_on_collect = False ):
243
294
"""
244
295
Creates a new gauge
245
- :param label : label of the gauge
296
+ :param name : label of the gauge
246
297
"""
247
- self .label = label
298
+ self .name = name
248
299
self ._val = None
300
+ self .reset_on_collect = reset_on_collect
249
301
250
302
@property
251
303
def val (self ):
@@ -255,6 +307,35 @@ def val(self):
255
307
def val (self , value ):
256
308
self ._val = value
257
309
310
+ def reset (self ):
311
+ self ._val = 0
312
+
313
+
314
+ class Timer (object ):
315
+ __slots__ = ("name" , "_val" , "_count" , "_lock" , "reset_on_collect" )
316
+
317
+ def __init__ (self , name = None , reset_on_collect = False ):
318
+ self .name = name
319
+ self ._val = 0
320
+ self ._count = 0
321
+ self ._lock = threading .Lock ()
322
+ self .reset_on_collect = reset_on_collect
323
+
324
+ def update (self , duration , count = 1 ):
325
+ with self ._lock :
326
+ self ._val += duration
327
+ self ._count += count
328
+
329
+ def reset (self ):
330
+ with self ._lock :
331
+ self ._val = 0
332
+ self ._count = 0
333
+
334
+ @property
335
+ def val (self ):
336
+ with self ._lock :
337
+ return self ._val , self ._count
338
+
258
339
259
340
class NoopMetric (object ):
260
341
"""
@@ -285,3 +366,8 @@ def reset(self):
285
366
286
367
287
368
noop_metric = NoopMetric ("noop" )
369
+
370
+
371
+ class MetricSetNotFound (LookupError ):
372
+ def __init__ (self , class_path ):
373
+ super (MetricSetNotFound , self ).__init__ ("%s metric set not found" % class_path )
0 commit comments