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

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

Issue 2111473003: Issue 623854: Support unit annotations in ts_mon metrics (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Split off go changes to a separate CL: https://codereview.chromium.org/2125943003 Created 4 years, 5 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
« no previous file with comments | « infra_libs/ts_mon/__init__.py ('k') | infra_libs/ts_mon/common/test/metrics_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 8
9 from infra_libs.ts_mon.protos import metrics_pb2 9 from infra_libs.ts_mon.protos import metrics_pb2
10 10
(...skipping 14 matching lines...) Expand all
25 25
26 Note that Metric objects may be initialized at any time (for example, at the 26 Note that Metric objects may be initialized at any time (for example, at the
27 top of a library), but cannot be sent until the underlying Monitor object 27 top of a library), but cannot be sent until the underlying Monitor object
28 has been set up (usually by the top-level process parsing the command line). 28 has been set up (usually by the top-level process parsing the command line).
29 29
30 A Metric can actually store multiple values that are identified by a set of 30 A Metric can actually store multiple values that are identified by a set of
31 fields (which are themselves key-value pairs). Fields can be passed to the 31 fields (which are themselves key-value pairs). Fields can be passed to the
32 set() or increment() methods to modify a particular value, or passed to the 32 set() or increment() methods to modify a particular value, or passed to the
33 constructor in which case they will be used as the defaults for this Metric. 33 constructor in which case they will be used as the defaults for this Metric.
34 34
35 The unit of measurement for Metric data can be specified with MetricsDataUnits
36 when a Metric object is created:
37 e.g., MetricsDataUnits.SECONDS, MetricsDataUnits.BYTES, and etc..,
38 A full list of supported units can be found in the following protobuf file
39 : infra_libs/ts_mon/protos/metrics.proto
40
35 Do not directly instantiate an object of this class. 41 Do not directly instantiate an object of this class.
36 Use the concrete child classes instead: 42 Use the concrete child classes instead:
37 * StringMetric for metrics with string value 43 * StringMetric for metrics with string value
38 * BooleanMetric for metrics with boolean values 44 * BooleanMetric for metrics with boolean values
39 * CounterMetric for metrics with monotonically increasing integer values 45 * CounterMetric for metrics with monotonically increasing integer values
40 * GaugeMetric for metrics with arbitrarily varying integer values 46 * GaugeMetric for metrics with arbitrarily varying integer values
41 * CumulativeMetric for metrics with monotonically increasing float values 47 * CumulativeMetric for metrics with monotonically increasing float values
42 * FloatMetric for metrics with arbitrarily varying float values 48 * FloatMetric for metrics with arbitrarily varying float values
43 49
44 See http://go/inframon-doc for help designing and using your metrics. 50 See http://go/inframon-doc for help designing and using your metrics.
45 """ 51 """
46 52
47 def __init__(self, name, fields=None, description=None): 53 def __init__(self, name, fields=None, description=None, units=None):
48 """Create an instance of a Metric. 54 """Create an instance of a Metric.
49 55
50 Args: 56 Args:
51 name (str): the file-like name of this metric 57 name (str): the file-like name of this metric
52 fields (dict): a set of key-value pairs to be set as default metric fields 58 fields (dict): a set of key-value pairs to be set as default metric fields
53 description (string): help string for the metric. Should be enough to 59 description (string): help string for the metric. Should be enough to
54 know what the metric is about. 60 know what the metric is about.
61 units (int): the unit used to measure data for given
62 metric. Please use the attributes of MetricDataUnit to find
63 valid integer values for this argument.
55 """ 64 """
56 self._name = name.lstrip('/') 65 self._name = name.lstrip('/')
57 self._start_time = None 66 self._start_time = None
58 fields = fields or {} 67 fields = fields or {}
59 if len(fields) > 7: 68 if len(fields) > 7:
60 raise errors.MonitoringTooManyFieldsError(self._name, fields) 69 raise errors.MonitoringTooManyFieldsError(self._name, fields)
61 self._fields = fields 70 self._fields = fields
62 self._normalized_fields = self._normalize_fields(self._fields) 71 self._normalized_fields = self._normalize_fields(self._fields)
63 self._description = description 72 self._description = description
73 self._units = units
64 74
65 interface.register(self) 75 interface.register(self)
66 76
67 @property 77 @property
68 def name(self): 78 def name(self):
69 return self._name 79 return self._name
70 80
71 @property 81 @property
72 def start_time(self): 82 def start_time(self):
73 return self._start_time 83 return self._start_time
(...skipping 17 matching lines...) Expand all
91 to add the current metric values. 101 to add the current metric values.
92 start_time (int): timestamp in microseconds since UNIX epoch. 102 start_time (int): timestamp in microseconds since UNIX epoch.
93 target (Target): a Target to use. 103 target (Target): a Target to use.
94 """ 104 """
95 105
96 metric_pb = collection_pb.data.add() 106 metric_pb = collection_pb.data.add()
97 metric_pb.metric_name_prefix = interface.state.metric_name_prefix 107 metric_pb.metric_name_prefix = interface.state.metric_name_prefix
98 metric_pb.name = self._name 108 metric_pb.name = self._name
99 if self._description is not None: 109 if self._description is not None:
100 metric_pb.description = self._description 110 metric_pb.description = self._description
111 if self._units is not None:
112 metric_pb.units = self._units
101 113
102 self._populate_value(metric_pb, value, start_time) 114 self._populate_value(metric_pb, value, start_time)
103 self._populate_fields(metric_pb, fields) 115 self._populate_fields(metric_pb, fields)
104 116
105 target._populate_target_pb(metric_pb) 117 target._populate_target_pb(metric_pb)
106 118
107 def _populate_fields(self, metric, fields): 119 def _populate_fields(self, metric, fields):
108 """Fill in the fields attribute of a metric protocol buffer. 120 """Fill in the fields attribute of a metric protocol buffer.
109 121
110 Args: 122 Args:
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 def increment(self, fields=None, target_fields=None): 255 def increment(self, fields=None, target_fields=None):
244 self._incr(fields, target_fields, 1) 256 self._incr(fields, target_fields, 1)
245 257
246 def increment_by(self, step, fields=None, target_fields=None): 258 def increment_by(self, step, fields=None, target_fields=None):
247 self._incr(fields, target_fields, step) 259 self._incr(fields, target_fields, step)
248 260
249 261
250 class CounterMetric(NumericMetric): 262 class CounterMetric(NumericMetric):
251 """A metric whose value type is a monotonically increasing integer.""" 263 """A metric whose value type is a monotonically increasing integer."""
252 264
253 def __init__(self, name, fields=None, start_time=None, description=None): 265 def __init__(self, name, fields=None, start_time=None, description=None,
266 units=None):
254 super(CounterMetric, self).__init__( 267 super(CounterMetric, self).__init__(
255 name, fields=fields, description=description) 268 name, fields=fields, description=description, units=units)
256 self._start_time = start_time 269 self._start_time = start_time
257 270
258 def _populate_value(self, metric, value, start_time): 271 def _populate_value(self, metric, value, start_time):
259 metric.counter = value 272 metric.counter = value
260 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND) 273 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
261 274
262 def set(self, value, fields=None, target_fields=None): 275 def set(self, value, fields=None, target_fields=None):
263 if not isinstance(value, (int, long)): 276 if not isinstance(value, (int, long)):
264 raise errors.MonitoringInvalidValueTypeError(self._name, value) 277 raise errors.MonitoringInvalidValueTypeError(self._name, value)
265 self._set(fields, target_fields, value, enforce_ge=True) 278 self._set(fields, target_fields, value, enforce_ge=True)
(...skipping 18 matching lines...) Expand all
284 raise errors.MonitoringInvalidValueTypeError(self._name, value) 297 raise errors.MonitoringInvalidValueTypeError(self._name, value)
285 self._set(fields, target_fields, value) 298 self._set(fields, target_fields, value)
286 299
287 def is_cumulative(self): 300 def is_cumulative(self):
288 return False 301 return False
289 302
290 303
291 class CumulativeMetric(NumericMetric): 304 class CumulativeMetric(NumericMetric):
292 """A metric whose value type is a monotonically increasing float.""" 305 """A metric whose value type is a monotonically increasing float."""
293 306
294 def __init__(self, name, fields=None, start_time=None, description=None): 307 def __init__(self, name, fields=None, start_time=None, description=None,
308 units=None):
295 super(CumulativeMetric, self).__init__( 309 super(CumulativeMetric, self).__init__(
296 name, fields=fields, description=description) 310 name, fields=fields, description=description, units=units)
297 self._start_time = start_time 311 self._start_time = start_time
298 312
299 def _populate_value(self, metric, value, start_time): 313 def _populate_value(self, metric, value, start_time):
300 metric.cumulative_double_value = value 314 metric.cumulative_double_value = value
301 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND) 315 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
302 316
303 def set(self, value, fields=None, target_fields=None): 317 def set(self, value, fields=None, target_fields=None):
304 if not isinstance(value, (float, int)): 318 if not isinstance(value, (float, int)):
305 raise errors.MonitoringInvalidValueTypeError(self._name, value) 319 raise errors.MonitoringInvalidValueTypeError(self._name, value)
306 self._set(fields, target_fields, float(value), enforce_ge=True) 320 self._set(fields, target_fields, float(value), enforce_ge=True)
(...skipping 25 matching lines...) Expand all
332 for many kinds of data, but you may want to provide a FixedWidthBucketer or 346 for many kinds of data, but you may want to provide a FixedWidthBucketer or
333 GeometricBucketer with different parameters.""" 347 GeometricBucketer with different parameters."""
334 348
335 CANONICAL_SPEC_TYPES = { 349 CANONICAL_SPEC_TYPES = {
336 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2, 350 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2,
337 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2, 351 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2,
338 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10, 352 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10,
339 } 353 }
340 354
341 def __init__(self, name, is_cumulative=True, bucketer=None, fields=None, 355 def __init__(self, name, is_cumulative=True, bucketer=None, fields=None,
342 start_time=None, description=None): 356 start_time=None, description=None, units=None):
343 super(DistributionMetric, self).__init__( 357 super(DistributionMetric, self).__init__(
344 name, fields=fields, description=description) 358 name, fields=fields, description=description, units=units)
345 self._start_time = start_time 359 self._start_time = start_time
346 360
347 if bucketer is None: 361 if bucketer is None:
348 bucketer = distribution.GeometricBucketer() 362 bucketer = distribution.GeometricBucketer()
349 363
350 self._is_cumulative = is_cumulative 364 self._is_cumulative = is_cumulative
351 self.bucketer = bucketer 365 self.bucketer = bucketer
352 366
353 def _populate_value(self, metric, value, start_time): 367 def _populate_value(self, metric, value, start_time):
354 pb = metric.distribution 368 pb = metric.distribution
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 self._set(fields, target_fields, value) 442 self._set(fields, target_fields, value)
429 443
430 def is_cumulative(self): 444 def is_cumulative(self):
431 raise NotImplementedError() # Keep this class abstract. 445 raise NotImplementedError() # Keep this class abstract.
432 446
433 447
434 class CumulativeDistributionMetric(DistributionMetric): 448 class CumulativeDistributionMetric(DistributionMetric):
435 """A DistributionMetric with is_cumulative set to True.""" 449 """A DistributionMetric with is_cumulative set to True."""
436 450
437 def __init__(self, name, bucketer=None, fields=None, 451 def __init__(self, name, bucketer=None, fields=None,
438 description=None): 452 description=None, units=None):
439 super(CumulativeDistributionMetric, self).__init__( 453 super(CumulativeDistributionMetric, self).__init__(
440 name, 454 name,
441 is_cumulative=True, 455 is_cumulative=True,
442 bucketer=bucketer, 456 bucketer=bucketer,
443 fields=fields, 457 fields=fields,
444 description=description) 458 description=description,
459 units=units)
445 460
446 def is_cumulative(self): 461 def is_cumulative(self):
447 return True 462 return True
448 463
449 464
450 class NonCumulativeDistributionMetric(DistributionMetric): 465 class NonCumulativeDistributionMetric(DistributionMetric):
451 """A DistributionMetric with is_cumulative set to False.""" 466 """A DistributionMetric with is_cumulative set to False."""
452 467
453 def __init__(self, name, bucketer=None, fields=None, 468 def __init__(self, name, bucketer=None, fields=None,
454 description=None): 469 description=None, units=None):
455 super(NonCumulativeDistributionMetric, self).__init__( 470 super(NonCumulativeDistributionMetric, self).__init__(
456 name, 471 name,
457 is_cumulative=False, 472 is_cumulative=False,
458 bucketer=bucketer, 473 bucketer=bucketer,
459 fields=fields, 474 fields=fields,
460 description=description) 475 description=description,
476 units=units)
461 477
462 def is_cumulative(self): 478 def is_cumulative(self):
463 return False 479 return False
480
481
482 class MetaMetricsDataUnits(type):
483 """Metaclass to populate the enum values of metrics_pb2.MetricsData.Units."""
484 def __new__(mcs, name, bases, attrs):
485 attrs.update(metrics_pb2.MetricsData.Units.items())
486 return super(MetaMetricsDataUnits, mcs).__new__(mcs, name, bases, attrs)
487
488
489 class MetricsDataUnits(object):
490 """An enumeration class for units of measurement for Metrics data.
491 See infra_libs/ts_mon/protos/metrics.proto for a full list of supported units.
492 """
493 __metaclass__ = MetaMetricsDataUnits
OLDNEW
« no previous file with comments | « infra_libs/ts_mon/__init__.py ('k') | infra_libs/ts_mon/common/test/metrics_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698