3
3
from __future__ import unicode_literals
4
4
5
5
import copy
6
+ import math
6
7
import re
7
8
import time
8
9
import types
@@ -61,7 +62,7 @@ def get_sample_value(self, name, labels=None):
61
62
if labels is None :
62
63
labels = {}
63
64
for metric in self .collect ():
64
- for n , l , value in metric ._samples :
65
+ for n , l , value in metric .samples :
65
66
if n == name and l == labels :
66
67
return value
67
68
return None
@@ -74,18 +75,145 @@ def get_sample_value(self, name, labels=None):
74
75
75
76
76
77
class 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
+ '''
78
85
def __init__ (self , name , documentation , typ ):
79
- self ._name = name
80
- self ._documentation = documentation
86
+ self .name = name
87
+ self .documentation = documentation
81
88
if typ not in _METRIC_TYPES :
82
89
raise ValueError ('Invalid metric type: ' + typ )
83
- self ._type = typ
84
- self ._samples = []
90
+ self .type = typ
91
+ self .samples = []
85
92
86
- '''Add a sample to the metric'''
87
93
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 ))
89
217
90
218
91
219
class _MutexValue (object ):
@@ -271,7 +399,7 @@ def _samples(self):
271
399
@_MetricWrapper
272
400
class Gauge (object ):
273
401
'''Gauge metric, to report instantaneous values.
274
-
402
+
275
403
Examples of Gauges include:
276
404
Inprogress requests
277
405
Number of items in a queue
@@ -450,6 +578,8 @@ def _floatToGoString(d):
450
578
return '+Inf'
451
579
elif d == _MINUS_INF :
452
580
return '-Inf'
581
+ elif math .isnan (d ):
582
+ return 'NaN'
453
583
else :
454
584
return repr (float (d ))
455
585
0 commit comments