33from __future__ import unicode_literals
44
55import copy
6+ import math
67import re
78import time
89import types
@@ -61,7 +62,7 @@ def get_sample_value(self, name, labels=None):
6162 if labels is None :
6263 labels = {}
6364 for metric in self .collect ():
64- for n , l , value in metric ._samples :
65+ for n , l , value in metric .samples :
6566 if n == name and l == labels :
6667 return value
6768 return None
@@ -74,18 +75,145 @@ def get_sample_value(self, name, labels=None):
7475
7576
7677class Metric (object ):
77- '''A single metric and it's samples.'''
78+ '''A single metric family and its samples.
79+
80+ This is intended only for internal use by the instrumentation client.
81+
82+ Custom collectors should use GaugeMetricFamily, CounterMetricFamily
83+ and SummaryMetricFamily instead.
84+ '''
7885 def __init__ (self , name , documentation , typ ):
79- self ._name = name
80- self ._documentation = documentation
86+ self .name = name
87+ self .documentation = documentation
8188 if typ not in _METRIC_TYPES :
8289 raise ValueError ('Invalid metric type: ' + typ )
83- self ._type = typ
84- self ._samples = []
90+ self .type = typ
91+ self .samples = []
8592
86- '''Add a sample to the metric'''
8793 def add_sample (self , name , labels , value ):
88- self ._samples .append ((name , labels , value ))
94+ '''Add a sample to the metric.
95+
96+ Internal-only, do not use.'''
97+ self .samples .append ((name , labels , value ))
98+
99+ def __eq__ (self , other ):
100+ return (isinstance (other , Metric )
101+ and self .name == other .name
102+ and self .documentation == other .documentation
103+ and self .type == other .type
104+ and self .samples == other .samples )
105+
106+
107+ class CounterMetricFamily (Metric ):
108+ '''A single counter and its samples.
109+
110+ For use by custom collectors.
111+ '''
112+ def __init__ (self , name , documentation , value = None , labels = None ):
113+ Metric .__init__ (self , name , documentation , 'counter' )
114+ if labels is not None and value is not None :
115+ raise ValueError ('Can only specify at most one of value and labels.' )
116+ if labels is None :
117+ labels = []
118+ self ._labelnames = labels
119+ if value is not None :
120+ self .add_metric ([], value )
121+
122+ def add_metric (self , labels , value ):
123+ '''Add a metric to the metric family.
124+
125+ Args:
126+ labels: A list of label values
127+ value: The value of the metric.
128+ '''
129+ self .samples .append ((self .name , dict (zip (self ._labelnames , labels )), value ))
130+
131+
132+ class GaugeMetricFamily (Metric ):
133+ '''A single gauge and its samples.
134+
135+ For use by custom collectors.
136+ '''
137+ def __init__ (self , name , documentation , value = None , labels = None ):
138+ Metric .__init__ (self , name , documentation , 'gauge' )
139+ if labels is not None and value is not None :
140+ raise ValueError ('Can only specify at most one of value and labels.' )
141+ if labels is None :
142+ labels = []
143+ self ._labelnames = labels
144+ if value is not None :
145+ self .add_metric ([], value )
146+
147+ def add_metric (self , labels , value ):
148+ '''Add a metric to the metric family.
149+
150+ Args:
151+ labels: A list of label values
152+ value: A float
153+ '''
154+ self .samples .append ((self .name , dict (zip (self ._labelnames , labels )), value ))
155+
156+
157+ class SummaryMetricFamily (Metric ):
158+ '''A single summary and its samples.
159+
160+ For use by custom collectors.
161+ '''
162+ def __init__ (self , name , documentation , count_value = None , sum_value = None , labels = None ):
163+ Metric .__init__ (self , name , documentation , 'summary' )
164+ if (sum_value is None ) != (count_value is None ):
165+ raise ValueError ('count_value and sum_value must be provided together.' )
166+ if labels is not None and count_value is not None :
167+ raise ValueError ('Can only specify at most one of value and labels.' )
168+ if labels is None :
169+ labels = []
170+ self ._labelnames = labels
171+ if count_value is not None :
172+ self .add_metric ([], count_value , sum_value )
173+
174+ def add_metric (self , labels , count_value , sum_value ):
175+ '''Add a metric to the metric family.
176+
177+ Args:
178+ labels: A list of label values
179+ count_value: The count value of the metric.
180+ sum_value: The sum value of the metric.
181+ '''
182+ self .samples .append ((self .name + '_count' , dict (zip (self ._labelnames , labels )), count_value ))
183+ self .samples .append ((self .name + '_sum' , dict (zip (self ._labelnames , labels )), sum_value ))
184+
185+
186+ class HistogramMetricFamily (Metric ):
187+ '''A single histogram and its samples.
188+
189+ For use by custom collectors.
190+ '''
191+ def __init__ (self , name , documentation , buckets = None , sum_value = None , labels = None ):
192+ Metric .__init__ (self , name , documentation , 'histogram' )
193+ if (sum_value is None ) != (buckets is None ):
194+ raise ValueError ('buckets and sum_value must be provided together.' )
195+ if labels is not None and buckets is not None :
196+ raise ValueError ('Can only specify at most one of buckets and labels.' )
197+ if labels is None :
198+ labels = []
199+ self ._labelnames = labels
200+ if buckets is not None :
201+ self .add_metric ([], buckets , sum_value )
202+
203+ def add_metric (self , labels , buckets , sum_value ):
204+ '''Add a metric to the metric family.
205+
206+ Args:
207+ labels: A list of label values
208+ buckets: A list of pairs of bucket names and values.
209+ The buckets must be sorted, and +Inf present.
210+ sum_value: The sum value of the metric.
211+ '''
212+ for bucket , value in buckets :
213+ self .samples .append ((self .name + '_bucket' , dict (list (zip (self ._labelnames , labels )) + [('le' , bucket )]), value ))
214+ # +Inf is last and provides the count value.
215+ self .samples .append ((self .name + '_count' , dict (zip (self ._labelnames , labels )), buckets [- 1 ][1 ]))
216+ self .samples .append ((self .name + '_sum' , dict (zip (self ._labelnames , labels )), sum_value ))
89217
90218
91219class _MutexValue (object ):
@@ -271,7 +399,7 @@ def _samples(self):
271399@_MetricWrapper
272400class Gauge (object ):
273401 '''Gauge metric, to report instantaneous values.
274-
402+
275403 Examples of Gauges include:
276404 Inprogress requests
277405 Number of items in a queue
@@ -450,6 +578,8 @@ def _floatToGoString(d):
450578 return '+Inf'
451579 elif d == _MINUS_INF :
452580 return '-Inf'
581+ elif math .isnan (d ):
582+ return 'NaN'
453583 else :
454584 return repr (float (d ))
455585
0 commit comments