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

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

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