Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(412)

Side by Side Diff: client/third_party/infra_libs/ts_mon/common/metrics.py

Issue 2705273003: Roll infra_libs and gae_ts_mon in luci-py, and add field_specs to all metrics (Closed)
Patch Set: Rebase Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Classes representing individual metrics that can be sent.""" 5 """Classes representing individual metrics that can be sent."""
6 6
7 import copy 7 import copy
8 import re
8 9
9 from infra_libs.ts_mon.protos.current import metrics_pb2 10 from infra_libs.ts_mon.protos.current import metrics_pb2
10 from infra_libs.ts_mon.protos.new import metrics_pb2 as new_metrics_pb2 11 from infra_libs.ts_mon.protos.new import metrics_pb2 as new_metrics_pb2
11 12
12 from infra_libs.ts_mon.common import distribution 13 from infra_libs.ts_mon.common import distribution
13 from infra_libs.ts_mon.common import errors 14 from infra_libs.ts_mon.common import errors
14 from infra_libs.ts_mon.common import interface 15 from infra_libs.ts_mon.common import interface
15 16
16 17
17 MICROSECONDS_PER_SECOND = 1000000 18 MICROSECONDS_PER_SECOND = 1000000
18 19
19 20
21 class Field(object):
22 FIELD_NAME_PATTERN = re.compile(r'[A-Za-z_][A-Za-z0-9_]*')
23
24 allowed_python_types = None
25 v1_type = None
26 v2_type = None
27 v1_field = None
28 v2_field = None
29
30 def __init__(self, name):
31 if not self.FIELD_NAME_PATTERN.match(name):
32 raise errors.MetricDefinitionError(
33 'Invalid metric field name "%s" - must match the regex "%s"' % (
34 name, self.FIELD_NAME_PATTERN.pattern))
35
36 self.name = name
37
38 def validate_value(self, metric_name, value):
39 if not isinstance(value, self.allowed_python_types):
40 raise errors.MonitoringInvalidFieldTypeError(
41 metric_name, self.name, value)
42
43 def populate_proto_v1(self, proto, value):
44 setattr(proto, self.v1_field, value)
45
46 def populate_proto_v2(self, proto, value):
47 setattr(proto, self.v2_field, value)
48
49
50 class StringField(Field):
51 allowed_python_types = basestring
52 v1_type = metrics_pb2.MetricsField.STRING
53 v2_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor.STRING
54 v1_field = 'string_value'
55 v2_field = 'string_value'
56
57
58 class IntegerField(Field):
59 allowed_python_types = (int, long)
60 v1_type = metrics_pb2.MetricsField.INT
61 v2_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor.INT64
62 v1_field = 'int_value'
63 v2_field = 'int64_value'
64
65
66 class BooleanField(Field):
67 allowed_python_types = bool
68 v1_type = metrics_pb2.MetricsField.BOOL
69 v2_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor.BOOL
70 v1_field = 'bool_value'
71 v2_field = 'bool_value'
72
73
20 class Metric(object): 74 class Metric(object):
21 """Abstract base class for a metric. 75 """Abstract base class for a metric.
22 76
23 A Metric is an attribute that may be monitored across many targets. Examples 77 A Metric is an attribute that may be monitored across many targets. Examples
24 include disk usage or the number of requests a server has received. A single 78 include disk usage or the number of requests a server has received. A single
25 process may keep track of many metrics. 79 process may keep track of many metrics.
26 80
27 Note that Metric objects may be initialized at any time (for example, at the 81 Note that Metric objects may be initialized at any time (for example, at the
28 top of a library), but cannot be sent until the underlying Monitor object 82 top of a library), but cannot be sent until the underlying Monitor object
29 has been set up (usually by the top-level process parsing the command line). 83 has been set up (usually by the top-level process parsing the command line).
(...skipping 14 matching lines...) Expand all
44 * StringMetric for metrics with string value 98 * StringMetric for metrics with string value
45 * BooleanMetric for metrics with boolean values 99 * BooleanMetric for metrics with boolean values
46 * CounterMetric for metrics with monotonically increasing integer values 100 * CounterMetric for metrics with monotonically increasing integer values
47 * GaugeMetric for metrics with arbitrarily varying integer values 101 * GaugeMetric for metrics with arbitrarily varying integer values
48 * CumulativeMetric for metrics with monotonically increasing float values 102 * CumulativeMetric for metrics with monotonically increasing float values
49 * FloatMetric for metrics with arbitrarily varying float values 103 * FloatMetric for metrics with arbitrarily varying float values
50 104
51 See http://go/inframon-doc for help designing and using your metrics. 105 See http://go/inframon-doc for help designing and using your metrics.
52 """ 106 """
53 107
54 def __init__(self, name, fields=None, description=None, units=None): 108 def __init__(self, name, description, field_spec, units=None):
55 """Create an instance of a Metric. 109 """Create an instance of a Metric.
56 110
57 Args: 111 Args:
58 name (str): the file-like name of this metric 112 name (str): the file-like name of this metric
59 fields (dict): a set of key-value pairs to be set as default metric fields
60 description (string): help string for the metric. Should be enough to 113 description (string): help string for the metric. Should be enough to
61 know what the metric is about. 114 know what the metric is about.
115 field_spec (list): a list of Field subclasses to define the fields that
116 are allowed on this metric. Pass a list of either
117 StringField, IntegerField or BooleanField here.
62 units (int): the unit used to measure data for given 118 units (int): the unit used to measure data for given
63 metric. Please use the attributes of MetricDataUnit to find 119 metric. Please use the attributes of MetricDataUnit to find
64 valid integer values for this argument. 120 valid integer values for this argument.
65 """ 121 """
122 field_spec = field_spec or []
123
66 self._name = name.lstrip('/') 124 self._name = name.lstrip('/')
125
126 if not isinstance(description, basestring):
127 raise errors.MetricDefinitionError('Metric description must be a string')
128 if not description:
129 raise errors.MetricDefinitionError('Metric must have a description')
130 if (not isinstance(field_spec, (list, tuple)) or
131 any(not isinstance(x, Field) for x in field_spec)):
132 raise errors.MetricDefinitionError(
133 'Metric constructor takes a list of Fields, or None')
134 if len(field_spec) > 7:
135 raise errors.MonitoringTooManyFieldsError(self._name, field_spec)
136
67 self._start_time = None 137 self._start_time = None
68 fields = fields or {} 138 self._field_spec = field_spec
69 if len(fields) > 7: 139 self._sorted_field_names = sorted(x.name for x in field_spec)
70 raise errors.MonitoringTooManyFieldsError(self._name, fields)
71 self._fields = fields
72 self._normalized_fields = self._normalize_fields(self._fields)
73 self._description = description 140 self._description = description
74 self._units = units 141 self._units = units
75 142
76 interface.register(self) 143 interface.register(self)
77 144
78 @property 145 @property
79 def name(self): 146 def name(self):
80 return self._name 147 return self._name
81 148
82 @property 149 @property
83 def start_time(self): 150 def start_time(self):
84 return self._start_time 151 return self._start_time
85 152
86 def is_cumulative(self): 153 def is_cumulative(self):
87 raise NotImplementedError() 154 raise NotImplementedError()
88 155
89 def __eq__(self, other):
90 return (self.name == other.name and
91 self._fields == other._fields and
92 type(self) == type(other))
93
94 def unregister(self): 156 def unregister(self):
95 interface.unregister(self) 157 interface.unregister(self)
96 158
97 @staticmethod 159 @staticmethod
98 def _map_units_to_string(units): 160 def _map_units_to_string(units):
99 """Map MetricsDataUnits to the corresponding string according to: 161 """Map MetricsDataUnits to the corresponding string according to:
100 http://unitsofmeasure.org/ucum.html because that's what the new proto 162 http://unitsofmeasure.org/ucum.html because that's what the new proto
101 requires.""" 163 requires."""
102 if units in _UNITS_TO_STRING: 164 if units in _UNITS_TO_STRING:
103 return _UNITS_TO_STRING[units] 165 return _UNITS_TO_STRING[units]
104 else: 166 else:
105 return '{unknown}' 167 return '{unknown}'
106 168
107 def _populate_data_set(self, data_set, fields): 169 def _populate_data_set(self, data_set):
108 """Populate MetricsDataSet.""" 170 """Populate MetricsDataSet."""
109 data_set.metric_name = '%s%s' % (interface.state.metric_name_prefix, 171 data_set.metric_name = '%s%s' % (interface.state.metric_name_prefix,
110 self._name) 172 self._name)
111 data_set.description = self._description or '' 173 data_set.description = self._description or ''
112 data_set.annotations.unit = self._map_units_to_string(self._units) 174 data_set.annotations.unit = self._map_units_to_string(self._units)
113 175
114 if self.is_cumulative(): 176 if self.is_cumulative():
115 data_set.stream_kind = new_metrics_pb2.CUMULATIVE 177 data_set.stream_kind = new_metrics_pb2.CUMULATIVE
116 else: 178 else:
117 data_set.stream_kind = new_metrics_pb2.GAUGE 179 data_set.stream_kind = new_metrics_pb2.GAUGE
118 180
119 self._populate_value_type(data_set) 181 self._populate_value_type(data_set)
120 self._populate_field_descriptors(data_set, fields) 182 self._populate_field_descriptors(data_set)
121 183
122 def _populate_data(self, data, start_time, end_time, fields, value): 184 def _populate_data(self, data, start_time, end_time, fields, value):
123 """Populate a new metrics_pb2.MetricsData. 185 """Populate a new metrics_pb2.MetricsData.
124 186
125 Args: 187 Args:
126 data_ (new_metrics_pb2.MetricsData): protocol buffer into 188 data_ (new_metrics_pb2.MetricsData): protocol buffer into
127 which to populate the current metric values. 189 which to populate the current metric values.
128 start_time (int): timestamp in microseconds since UNIX epoch. 190 start_time (int): timestamp in microseconds since UNIX epoch.
129 """ 191 """
130 data.start_timestamp.seconds = int(start_time) 192 data.start_timestamp.seconds = int(start_time)
131 data.end_timestamp.seconds = int(end_time) 193 data.end_timestamp.seconds = int(end_time)
132 194
133 self._populate_fields_new(data, fields) 195 self._populate_fields_new(data, fields)
134 self._populate_value_new(data, value) 196 self._populate_value_new(data, value)
135 197
136 def serialize_to(self, metric_pb, start_time, fields, value, target): 198 def serialize_to(self, metric_pb, start_time, fields, value, target):
137 """Generate metrics_pb2.MetricsData messages for this metric. 199 """Generate metrics_pb2.MetricsData messages for this metric.
138 200
139 Args: 201 Args:
140 metric_pb (metrics_pb2.MetricsData): protocol buffer into which 202 metric_pb (metrics_pb2.MetricsData): protocol buffer into which
141 to serialize the current metric values. 203 to serialize the current metric values.
142 start_time (int): timestamp in microseconds since UNIX epoch. 204 start_time (int): timestamp in microseconds since UNIX epoch.
143 target (Target): a Target to use. 205 target (Target): a Target to use.
144 """ 206 """
145 207
146 metric_pb.metric_name_prefix = interface.state.metric_name_prefix 208 metric_pb.metric_name_prefix = interface.state.metric_name_prefix
147 metric_pb.name = self._name 209 metric_pb.name = self._name
148 if self._description is not None: 210 metric_pb.description = self._description
149 metric_pb.description = self._description
150 if self._units is not None: 211 if self._units is not None:
151 metric_pb.units = self._units 212 metric_pb.units = self._units
152 213
153 self._populate_value(metric_pb, value, start_time) 214 self._populate_value(metric_pb, value, start_time)
154 self._populate_fields(metric_pb, fields) 215 self._populate_fields(metric_pb, fields)
155 216
156 target._populate_target_pb(metric_pb) 217 target._populate_target_pb(metric_pb)
157 218
158 def _populate_field_descriptors(self, data_set, fields): 219 def _populate_field_descriptors(self, data_set):
159 """Populate `field_descriptor` in MetricsDataSet. 220 """Populate `field_descriptor` in MetricsDataSet.
160 221
161 Args: 222 Args:
162 data_set (new_metrics_pb2.MetricsDataSet): a data set protobuf to 223 data_set (new_metrics_pb2.MetricsDataSet): a data set protobuf to populate
163 populate 224 """
164 fields (list of (key, value) tuples): normalized metric fields 225 for spec in self._field_spec:
226 descriptor = data_set.field_descriptor.add()
227 descriptor.name = spec.name
228 descriptor.field_type = spec.v2_type
165 229
166 Raises: 230 def _populate_fields_new(self, data, field_values):
167 MonitoringInvalidFieldTypeError: if a field has a value of unknown type
168 """
169 field_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor
170 for key, value in fields:
171 descriptor = data_set.field_descriptor.add()
172 descriptor.name = key
173 if isinstance(value, basestring):
174 descriptor.field_type = field_type.STRING
175 elif isinstance(value, bool):
176 descriptor.field_type = field_type.BOOL
177 elif isinstance(value, int):
178 descriptor.field_type = field_type.INT64
179 else:
180 raise errors.MonitoringInvalidFieldTypeError(self._name, key, value)
181
182 def _populate_fields_new(self, data, fields):
183 """Fill in the fields attribute of a metric protocol buffer. 231 """Fill in the fields attribute of a metric protocol buffer.
184 232
185 Args: 233 Args:
186 metric (metrics_pb2.MetricsData): a metrics protobuf to populate 234 metric (metrics_pb2.MetricsData): a metrics protobuf to populate
187 fields (list of (key, value) tuples): normalized metric fields 235 field_values (tuple): field values
236 """
237 for spec, value in zip(self._field_spec, field_values):
238 field = data.field.add()
239 field.name = spec.name
240 spec.populate_proto_v2(field, value)
188 241
189 Raises: 242 def _populate_fields(self, metric, field_values):
190 MonitoringInvalidFieldTypeError: if a field has a value of unknown type
191 """
192 for key, value in fields:
193 field = data.field.add()
194 field.name = key
195 if isinstance(value, basestring):
196 field.string_value = value
197 elif isinstance(value, bool):
198 field.bool_value = value
199 elif isinstance(value, int):
200 field.int64_value = value
201 else:
202 raise errors.MonitoringInvalidFieldTypeError(self._name, key, value)
203
204 def _populate_fields(self, metric, fields):
205 """Fill in the fields attribute of a metric protocol buffer. 243 """Fill in the fields attribute of a metric protocol buffer.
206 244
207 Args: 245 Args:
208 metric (metrics_pb2.MetricsData): a metrics protobuf to populate 246 metric (metrics_pb2.MetricsData): a metrics protobuf to populate
209 fields (list of (key, value) tuples): normalized metric fields 247 field_values (tuple): field values
248 """
249 for spec, value in zip(self._field_spec, field_values):
250 field = metric.fields.add()
251 field.name = spec.name
252 field.type = spec.v1_type
253 spec.populate_proto_v1(field, value)
254
255 def _validate_fields(self, fields):
256 """Checks the correct number and types of field values were provided.
257
258 Args:
259 fields (dict): A dict of field values given by the user, or None.
260
261 Returns:
262 fields' values as a tuple, in the same order as the field_spec.
210 263
211 Raises: 264 Raises:
212 MonitoringInvalidFieldTypeError: if a field has a value of unknown type 265 WrongFieldsError: if you provide a different number of fields to those
266 the metric was defined with.
267 MonitoringInvalidFieldTypeError: if the field value was the wrong type for
268 the field spec.
213 """ 269 """
214 for key, value in fields: 270 fields = fields or {}
215 field = metric.fields.add()
216 field.name = key
217 if isinstance(value, basestring):
218 field.type = metrics_pb2.MetricsField.STRING
219 field.string_value = value
220 elif isinstance(value, bool):
221 field.type = metrics_pb2.MetricsField.BOOL
222 field.bool_value = value
223 elif isinstance(value, int):
224 field.type = metrics_pb2.MetricsField.INT
225 field.int_value = value
226 else:
227 raise errors.MonitoringInvalidFieldTypeError(self._name, key, value)
228 271
229 def _normalize_fields(self, fields): 272 if not isinstance(fields, dict):
230 """Merges the fields with the default fields and returns something hashable. 273 raise ValueError('fields should be a dict, got %r (%s)' % (
274 fields, type(fields)))
231 275
232 Args: 276 if sorted(fields) != self._sorted_field_names:
233 fields (dict): A dict of fields passed by the user, or None. 277 raise errors.WrongFieldsError(
278 self.name, fields.keys(), self._sorted_field_names)
234 279
235 Returns: 280 for spec in self._field_spec:
236 A tuple of (key, value) tuples, ordered by key. This whole tuple is used 281 spec.validate_value(self.name, fields[spec.name])
237 as the key in the self._values dict to identify the cell for a value.
238 282
239 Raises: 283 return tuple(fields[spec.name] for spec in self._field_spec)
240 MonitoringTooManyFieldsError: if there are more than seven metric fields
241 """
242 if fields is None:
243 return self._normalized_fields
244
245 all_fields = copy.copy(self._fields)
246 all_fields.update(fields)
247
248 if len(all_fields) > 7:
249 raise errors.MonitoringTooManyFieldsError(self._name, all_fields)
250
251 return tuple(sorted(all_fields.iteritems()))
252 284
253 def _populate_value(self, metric, value, start_time): 285 def _populate_value(self, metric, value, start_time):
254 """Fill in the the data values of a metric protocol buffer. 286 """Fill in the the data values of a metric protocol buffer.
255 287
256 Args: 288 Args:
257 metric (metrics_pb2.MetricsData): a metrics protobuf to populate 289 metric (metrics_pb2.MetricsData): a metrics protobuf to populate
258 value (see concrete class): the value of the metric to be set 290 value (see concrete class): the value of the metric to be set
259 start_time (int): timestamp in microseconds since UNIX epoch. 291 start_time (int): timestamp in microseconds since UNIX epoch.
260 """ 292 """
261 raise NotImplementedError() 293 raise NotImplementedError()
(...skipping 17 matching lines...) Expand all
279 raise NotImplementedError() 311 raise NotImplementedError()
280 312
281 def set(self, value, fields=None, target_fields=None): 313 def set(self, value, fields=None, target_fields=None):
282 """Set a new value for this metric. Results in sending a new value. 314 """Set a new value for this metric. Results in sending a new value.
283 315
284 The subclass should do appropriate type checking on value and then call 316 The subclass should do appropriate type checking on value and then call
285 self._set_and_send_value. 317 self._set_and_send_value.
286 318
287 Args: 319 Args:
288 value (see concrete class): the value of the metric to be set 320 value (see concrete class): the value of the metric to be set
289 fields (dict): additional metric fields to complement those on self 321 fields (dict): metric field values
290 target_fields (dict): overwrite some of the default target fields 322 target_fields (dict): overwrite some of the default target fields
291 """ 323 """
292 raise NotImplementedError() 324 raise NotImplementedError()
293 325
294 def get(self, fields=None, target_fields=None): 326 def get(self, fields=None, target_fields=None):
295 """Returns the current value for this metric. 327 """Returns the current value for this metric.
296 328
297 Subclasses should never use this to get a value, modify it and set it again. 329 Subclasses should never use this to get a value, modify it and set it again.
298 Instead use _incr with a modify_fn. 330 Instead use _incr with a modify_fn.
299 """ 331 """
300 return interface.state.store.get( 332 return interface.state.store.get(
301 self.name, self._normalize_fields(fields), target_fields) 333 self.name, self._validate_fields(fields), target_fields)
302 334
303 def get_all(self): 335 def get_all(self):
304 return interface.state.store.iter_field_values(self.name) 336 return interface.state.store.iter_field_values(self.name)
305 337
306 def reset(self): 338 def reset(self):
307 """Clears the values of this metric. Useful in unit tests. 339 """Clears the values of this metric. Useful in unit tests.
308 340
309 It might be easier to call ts_mon.reset_for_unittest() in your setUp() 341 It might be easier to call ts_mon.reset_for_unittest() in your setUp()
310 method instead of resetting every individual metric. 342 method instead of resetting every individual metric.
311 """ 343 """
312 344
313 interface.state.store.reset_for_unittest(self.name) 345 interface.state.store.reset_for_unittest(self.name)
314 346
315 def _set(self, fields, target_fields, value, enforce_ge=False): 347 def _set(self, fields, target_fields, value, enforce_ge=False):
316 interface.state.store.set(self.name, self._normalize_fields(fields), 348 interface.state.store.set(
317 target_fields, value, enforce_ge=enforce_ge) 349 self.name, self._validate_fields(fields), target_fields,
350 value, enforce_ge=enforce_ge)
318 351
319 def _incr(self, fields, target_fields, delta, modify_fn=None): 352 def _incr(self, fields, target_fields, delta, modify_fn=None):
320 interface.state.store.incr(self.name, self._normalize_fields(fields), 353 interface.state.store.incr(
321 target_fields, delta, modify_fn=modify_fn) 354 self.name, self._validate_fields(fields), target_fields,
355 delta, modify_fn=modify_fn)
322 356
323 357
324 class StringMetric(Metric): 358 class StringMetric(Metric):
325 """A metric whose value type is a string.""" 359 """A metric whose value type is a string."""
326 360
327 def _populate_value(self, metric, value, start_time): 361 def _populate_value(self, metric, value, start_time):
328 metric.string_value = value 362 metric.string_value = value
329 363
330 def _populate_value_new(self, data, value): 364 def _populate_value_new(self, data, value):
331 data.string_value = value 365 data.string_value = value
(...skipping 26 matching lines...) Expand all
358 if not isinstance(value, bool): 392 if not isinstance(value, bool):
359 raise errors.MonitoringInvalidValueTypeError(self._name, value) 393 raise errors.MonitoringInvalidValueTypeError(self._name, value)
360 self._set(fields, target_fields, value) 394 self._set(fields, target_fields, value)
361 395
362 def is_cumulative(self): 396 def is_cumulative(self):
363 return False 397 return False
364 398
365 399
366 class NumericMetric(Metric): # pylint: disable=abstract-method 400 class NumericMetric(Metric): # pylint: disable=abstract-method
367 """Abstract base class for numeric (int or float) metrics.""" 401 """Abstract base class for numeric (int or float) metrics."""
368 # TODO(agable): Figure out if there's a way to send units with these metrics.
369 402
370 def increment(self, fields=None, target_fields=None): 403 def increment(self, fields=None, target_fields=None):
371 self._incr(fields, target_fields, 1) 404 self._incr(fields, target_fields, 1)
372 405
373 def increment_by(self, step, fields=None, target_fields=None): 406 def increment_by(self, step, fields=None, target_fields=None):
374 self._incr(fields, target_fields, step) 407 self._incr(fields, target_fields, step)
375 408
376 409
377 class CounterMetric(NumericMetric): 410 class CounterMetric(NumericMetric):
378 """A metric whose value type is a monotonically increasing integer.""" 411 """A metric whose value type is a monotonically increasing integer."""
379 412
380 def __init__(self, name, fields=None, start_time=None, description=None, 413 def __init__(self, name, description, field_spec=None, start_time=None,
381 units=None): 414 units=None):
382 super(CounterMetric, self).__init__( 415 super(CounterMetric, self).__init__(
383 name, fields=fields, description=description, units=units) 416 name, description, field_spec, units=units)
384 self._start_time = start_time 417 self._start_time = start_time
385 418
386 def _populate_value(self, metric, value, start_time): 419 def _populate_value(self, metric, value, start_time):
387 metric.counter = value 420 metric.counter = value
388 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND) 421 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
389 422
390 def _populate_value_new(self, data, value): 423 def _populate_value_new(self, data, value):
391 data.int64_value = value 424 data.int64_value = value
392 425
393 def _populate_value_type(self, data_set): 426 def _populate_value_type(self, data_set):
(...skipping 30 matching lines...) Expand all
424 raise errors.MonitoringInvalidValueTypeError(self._name, value) 457 raise errors.MonitoringInvalidValueTypeError(self._name, value)
425 self._set(fields, target_fields, value) 458 self._set(fields, target_fields, value)
426 459
427 def is_cumulative(self): 460 def is_cumulative(self):
428 return False 461 return False
429 462
430 463
431 class CumulativeMetric(NumericMetric): 464 class CumulativeMetric(NumericMetric):
432 """A metric whose value type is a monotonically increasing float.""" 465 """A metric whose value type is a monotonically increasing float."""
433 466
434 def __init__(self, name, fields=None, start_time=None, description=None, 467 def __init__(self, name, description, field_spec=None, start_time=None,
435 units=None): 468 units=None):
436 super(CumulativeMetric, self).__init__( 469 super(CumulativeMetric, self).__init__(
437 name, fields=fields, description=description, units=units) 470 name, description, field_spec, units=units)
438 self._start_time = start_time 471 self._start_time = start_time
439 472
440 def _populate_value(self, metric, value, start_time): 473 def _populate_value(self, metric, value, start_time):
441 metric.cumulative_double_value = value 474 metric.cumulative_double_value = value
442 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND) 475 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
443 476
444 def _populate_value_new(self, data, value): 477 def _populate_value_new(self, data, value):
445 data.double_value = value 478 data.double_value = value
446 479
447 def _populate_value_type(self, data_set): 480 def _populate_value_type(self, data_set):
(...skipping 22 matching lines...) Expand all
470 503
471 def set(self, value, fields=None, target_fields=None): 504 def set(self, value, fields=None, target_fields=None):
472 if not isinstance(value, (float, int)): 505 if not isinstance(value, (float, int)):
473 raise errors.MonitoringInvalidValueTypeError(self._name, value) 506 raise errors.MonitoringInvalidValueTypeError(self._name, value)
474 self._set(fields, target_fields, float(value)) 507 self._set(fields, target_fields, float(value))
475 508
476 def is_cumulative(self): 509 def is_cumulative(self):
477 return False 510 return False
478 511
479 512
480 class DistributionMetric(Metric): 513 class _DistributionMetricBase(Metric):
481 """A metric that holds a distribution of values. 514 """A metric that holds a distribution of values.
482 515
483 By default buckets are chosen from a geometric progression, each bucket being 516 By default buckets are chosen from a geometric progression, each bucket being
484 approximately 1.59 times bigger than the last. In practice this is suitable 517 approximately 1.59 times bigger than the last. In practice this is suitable
485 for many kinds of data, but you may want to provide a FixedWidthBucketer or 518 for many kinds of data, but you may want to provide a FixedWidthBucketer or
486 GeometricBucketer with different parameters.""" 519 GeometricBucketer with different parameters."""
487 520
488 CANONICAL_SPEC_TYPES = { 521 CANONICAL_SPEC_TYPES = {
489 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2, 522 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2,
490 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2, 523 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2,
491 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10, 524 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10,
492 } 525 }
493 526
494 def __init__(self, name, is_cumulative=True, bucketer=None, fields=None, 527 def __init__(self, name, description, field_spec=None, is_cumulative=True,
495 start_time=None, description=None, units=None): 528 bucketer=None, start_time=None, units=None):
496 super(DistributionMetric, self).__init__( 529 super(_DistributionMetricBase, self).__init__(
497 name, fields=fields, description=description, units=units) 530 name, description, field_spec, units=units)
498 self._start_time = start_time 531 self._start_time = start_time
499 532
500 if bucketer is None: 533 if bucketer is None:
501 bucketer = distribution.GeometricBucketer() 534 bucketer = distribution.GeometricBucketer()
502 535
503 self._is_cumulative = is_cumulative 536 self._is_cumulative = is_cumulative
504 self.bucketer = bucketer 537 self.bucketer = bucketer
505 538
506 def _populate_value(self, metric, value, start_time): 539 def _populate_value(self, metric, value, start_time):
507 pb = metric.distribution 540 pb = metric.distribution
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 if self._is_cumulative: 633 if self._is_cumulative:
601 raise TypeError( 634 raise TypeError(
602 'Cannot set() a cumulative DistributionMetric (use add() instead)') 635 'Cannot set() a cumulative DistributionMetric (use add() instead)')
603 636
604 if not isinstance(value, distribution.Distribution): 637 if not isinstance(value, distribution.Distribution):
605 raise errors.MonitoringInvalidValueTypeError(self._name, value) 638 raise errors.MonitoringInvalidValueTypeError(self._name, value)
606 639
607 self._set(fields, target_fields, value) 640 self._set(fields, target_fields, value)
608 641
609 def is_cumulative(self): 642 def is_cumulative(self):
610 raise NotImplementedError() # Keep this class abstract. 643 return self._is_cumulative
611 644
612 645
613 class CumulativeDistributionMetric(DistributionMetric): 646 class CumulativeDistributionMetric(_DistributionMetricBase):
614 """A DistributionMetric with is_cumulative set to True.""" 647 """A DistributionMetric with is_cumulative set to True."""
615 648
616 def __init__(self, name, bucketer=None, fields=None, 649 def __init__(self, name, description, field_spec=None, bucketer=None,
617 description=None, units=None): 650 units=None):
618 super(CumulativeDistributionMetric, self).__init__( 651 super(CumulativeDistributionMetric, self).__init__(
619 name, 652 name, description, field_spec,
620 is_cumulative=True, 653 is_cumulative=True,
621 bucketer=bucketer, 654 bucketer=bucketer,
622 fields=fields,
623 description=description,
624 units=units) 655 units=units)
625 656
626 def is_cumulative(self):
627 return True
628 657
629 658 class NonCumulativeDistributionMetric(_DistributionMetricBase):
630 class NonCumulativeDistributionMetric(DistributionMetric):
631 """A DistributionMetric with is_cumulative set to False.""" 659 """A DistributionMetric with is_cumulative set to False."""
632 660
633 def __init__(self, name, bucketer=None, fields=None, 661 def __init__(self, name, description, field_spec=None, bucketer=None,
634 description=None, units=None): 662 units=None):
635 super(NonCumulativeDistributionMetric, self).__init__( 663 super(NonCumulativeDistributionMetric, self).__init__(
636 name, 664 name, description, field_spec,
637 is_cumulative=False, 665 is_cumulative=False,
638 bucketer=bucketer, 666 bucketer=bucketer,
639 fields=fields,
640 description=description,
641 units=units) 667 units=units)
642 668
643 def is_cumulative(self):
644 return False
645
646 669
647 class MetaMetricsDataUnits(type): 670 class MetaMetricsDataUnits(type):
648 """Metaclass to populate the enum values of metrics_pb2.MetricsData.Units.""" 671 """Metaclass to populate the enum values of metrics_pb2.MetricsData.Units."""
649 def __new__(mcs, name, bases, attrs): 672 def __new__(mcs, name, bases, attrs):
650 attrs.update(metrics_pb2.MetricsData.Units.items()) 673 attrs.update(metrics_pb2.MetricsData.Units.items())
651 return super(MetaMetricsDataUnits, mcs).__new__(mcs, name, bases, attrs) 674 return super(MetaMetricsDataUnits, mcs).__new__(mcs, name, bases, attrs)
652 675
653 676
654 class MetricsDataUnits(object): 677 class MetricsDataUnits(object):
655 """An enumeration class for units of measurement for Metrics data. 678 """An enumeration class for units of measurement for Metrics data.
(...skipping 12 matching lines...) Expand all
668 MetricsDataUnits.KILOBYTES: 'kBy', 691 MetricsDataUnits.KILOBYTES: 'kBy',
669 MetricsDataUnits.MEGABYTES: 'MBy', 692 MetricsDataUnits.MEGABYTES: 'MBy',
670 MetricsDataUnits.GIGABYTES: 'GBy', 693 MetricsDataUnits.GIGABYTES: 'GBy',
671 MetricsDataUnits.KIBIBYTES: 'kiBy', 694 MetricsDataUnits.KIBIBYTES: 'kiBy',
672 MetricsDataUnits.MEBIBYTES: 'MiBy', 695 MetricsDataUnits.MEBIBYTES: 'MiBy',
673 MetricsDataUnits.GIBIBYTES: 'GiBy', 696 MetricsDataUnits.GIBIBYTES: 'GiBy',
674 MetricsDataUnits.AMPS: 'A', 697 MetricsDataUnits.AMPS: 'A',
675 MetricsDataUnits.MILLIAMPS : 'mA', 698 MetricsDataUnits.MILLIAMPS : 'mA',
676 MetricsDataUnits.DEGREES_CELSIUS: 'Cel' 699 MetricsDataUnits.DEGREES_CELSIUS: 'Cel'
677 } 700 }
OLDNEW
« no previous file with comments | « client/third_party/infra_libs/ts_mon/common/interface.py ('k') | client/third_party/infra_libs/ts_mon/common/monitors.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698