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

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: Update sysmon to pass units for metrics and renamed MetricDataUnit to MetricsDataUnits 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
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 13 matching lines...) Expand all
24 process may keep track of many metrics. 24 process may keep track of many metrics.
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
agable 2016/06/29 18:25:23 Please update this docstring to reference units as
ddoman 2016/07/06 03:35:37 Done.
35 Do not directly instantiate an object of this class. 35 Do not directly instantiate an object of this class.
36 Use the concrete child classes instead: 36 Use the concrete child classes instead:
37 * StringMetric for metrics with string value 37 * StringMetric for metrics with string value
38 * BooleanMetric for metrics with boolean values 38 * BooleanMetric for metrics with boolean values
39 * CounterMetric for metrics with monotonically increasing integer values 39 * CounterMetric for metrics with monotonically increasing integer values
40 * GaugeMetric for metrics with arbitrarily varying integer values 40 * GaugeMetric for metrics with arbitrarily varying integer values
41 * CumulativeMetric for metrics with monotonically increasing float values 41 * CumulativeMetric for metrics with monotonically increasing float values
42 * FloatMetric for metrics with arbitrarily varying float values 42 * FloatMetric for metrics with arbitrarily varying float values
43 43
44 See http://go/inframon-doc for help designing and using your metrics. 44 See http://go/inframon-doc for help designing and using your metrics.
45 """ 45 """
46 46
47 def __init__(self, name, fields=None, description=None): 47 def __init__(self, name, fields=None, description=None, units=None):
48 """Create an instance of a Metric. 48 """Create an instance of a Metric.
49 49
50 Args: 50 Args:
51 name (str): the file-like name of this metric 51 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 52 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 53 description (string): help string for the metric. Should be enough to
54 know what the metric is about. 54 know what the metric is about.
55 units (int): the unit used to measure data for given
56 metric. Please use the attributes of MetricDataUnit to find
57 valid integer values for this argument.
55 """ 58 """
56 self._name = name.lstrip('/') 59 self._name = name.lstrip('/')
57 self._start_time = None 60 self._start_time = None
58 fields = fields or {} 61 fields = fields or {}
59 if len(fields) > 7: 62 if len(fields) > 7:
60 raise errors.MonitoringTooManyFieldsError(self._name, fields) 63 raise errors.MonitoringTooManyFieldsError(self._name, fields)
61 self._fields = fields 64 self._fields = fields
62 self._normalized_fields = self._normalize_fields(self._fields) 65 self._normalized_fields = self._normalize_fields(self._fields)
63 self._description = description 66 self._description = description
67 self._units = units
64 68
65 interface.register(self) 69 interface.register(self)
66 70
67 @property 71 @property
68 def name(self): 72 def name(self):
69 return self._name 73 return self._name
70 74
71 @property 75 @property
72 def start_time(self): 76 def start_time(self):
73 return self._start_time 77 return self._start_time
(...skipping 17 matching lines...) Expand all
91 to add the current metric values. 95 to add the current metric values.
92 start_time (int): timestamp in microseconds since UNIX epoch. 96 start_time (int): timestamp in microseconds since UNIX epoch.
93 target (Target): a Target to use. 97 target (Target): a Target to use.
94 """ 98 """
95 99
96 metric_pb = collection_pb.data.add() 100 metric_pb = collection_pb.data.add()
97 metric_pb.metric_name_prefix = '/chrome/infra/' 101 metric_pb.metric_name_prefix = '/chrome/infra/'
98 metric_pb.name = self._name 102 metric_pb.name = self._name
99 if self._description is not None: 103 if self._description is not None:
100 metric_pb.description = self._description 104 metric_pb.description = self._description
105 if self._units is not None:
106 metric_pb.units = self._units
101 107
102 self._populate_value(metric_pb, value, start_time) 108 self._populate_value(metric_pb, value, start_time)
103 self._populate_fields(metric_pb, fields) 109 self._populate_fields(metric_pb, fields)
104 110
105 target._populate_target_pb(metric_pb) 111 target._populate_target_pb(metric_pb)
106 112
107 def _populate_fields(self, metric, fields): 113 def _populate_fields(self, metric, fields):
108 """Fill in the fields attribute of a metric protocol buffer. 114 """Fill in the fields attribute of a metric protocol buffer.
109 115
110 Args: 116 Args:
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 def increment(self, fields=None, target_fields=None): 246 def increment(self, fields=None, target_fields=None):
241 self._incr(fields, target_fields, 1) 247 self._incr(fields, target_fields, 1)
242 248
243 def increment_by(self, step, fields=None, target_fields=None): 249 def increment_by(self, step, fields=None, target_fields=None):
244 self._incr(fields, target_fields, step) 250 self._incr(fields, target_fields, step)
245 251
246 252
247 class CounterMetric(NumericMetric): 253 class CounterMetric(NumericMetric):
248 """A metric whose value type is a monotonically increasing integer.""" 254 """A metric whose value type is a monotonically increasing integer."""
249 255
250 def __init__(self, name, fields=None, start_time=None, description=None): 256 def __init__(self, name, fields=None, start_time=None, description=None,
257 units=None):
251 super(CounterMetric, self).__init__( 258 super(CounterMetric, self).__init__(
252 name, fields=fields, description=description) 259 name, fields=fields, description=description, units=units)
253 self._start_time = start_time 260 self._start_time = start_time
254 261
255 def _populate_value(self, metric, value, start_time): 262 def _populate_value(self, metric, value, start_time):
256 metric.counter = value 263 metric.counter = value
257 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND) 264 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
258 265
259 def set(self, value, fields=None, target_fields=None): 266 def set(self, value, fields=None, target_fields=None):
260 if not isinstance(value, (int, long)): 267 if not isinstance(value, (int, long)):
261 raise errors.MonitoringInvalidValueTypeError(self._name, value) 268 raise errors.MonitoringInvalidValueTypeError(self._name, value)
262 self._set(fields, target_fields, value, enforce_ge=True) 269 self._set(fields, target_fields, value, enforce_ge=True)
(...skipping 18 matching lines...) Expand all
281 raise errors.MonitoringInvalidValueTypeError(self._name, value) 288 raise errors.MonitoringInvalidValueTypeError(self._name, value)
282 self._set(fields, target_fields, value) 289 self._set(fields, target_fields, value)
283 290
284 def is_cumulative(self): 291 def is_cumulative(self):
285 return False 292 return False
286 293
287 294
288 class CumulativeMetric(NumericMetric): 295 class CumulativeMetric(NumericMetric):
289 """A metric whose value type is a monotonically increasing float.""" 296 """A metric whose value type is a monotonically increasing float."""
290 297
291 def __init__(self, name, fields=None, start_time=None, description=None): 298 def __init__(self, name, fields=None, start_time=None, description=None,
299 units=None):
292 super(CumulativeMetric, self).__init__( 300 super(CumulativeMetric, self).__init__(
293 name, fields=fields, description=description) 301 name, fields=fields, description=description, units=units)
294 self._start_time = start_time 302 self._start_time = start_time
295 303
296 def _populate_value(self, metric, value, start_time): 304 def _populate_value(self, metric, value, start_time):
297 metric.cumulative_double_value = value 305 metric.cumulative_double_value = value
298 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND) 306 metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
299 307
300 def set(self, value, fields=None, target_fields=None): 308 def set(self, value, fields=None, target_fields=None):
301 if not isinstance(value, (float, int)): 309 if not isinstance(value, (float, int)):
302 raise errors.MonitoringInvalidValueTypeError(self._name, value) 310 raise errors.MonitoringInvalidValueTypeError(self._name, value)
303 self._set(fields, target_fields, float(value), enforce_ge=True) 311 self._set(fields, target_fields, float(value), enforce_ge=True)
(...skipping 25 matching lines...) Expand all
329 for many kinds of data, but you may want to provide a FixedWidthBucketer or 337 for many kinds of data, but you may want to provide a FixedWidthBucketer or
330 GeometricBucketer with different parameters.""" 338 GeometricBucketer with different parameters."""
331 339
332 CANONICAL_SPEC_TYPES = { 340 CANONICAL_SPEC_TYPES = {
333 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2, 341 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2,
334 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2, 342 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2,
335 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10, 343 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10,
336 } 344 }
337 345
338 def __init__(self, name, is_cumulative=True, bucketer=None, fields=None, 346 def __init__(self, name, is_cumulative=True, bucketer=None, fields=None,
339 start_time=None, description=None): 347 start_time=None, description=None, units=None):
340 super(DistributionMetric, self).__init__( 348 super(DistributionMetric, self).__init__(
341 name, fields=fields, description=description) 349 name, fields=fields, description=description, units=units)
342 self._start_time = start_time 350 self._start_time = start_time
343 351
344 if bucketer is None: 352 if bucketer is None:
345 bucketer = distribution.GeometricBucketer() 353 bucketer = distribution.GeometricBucketer()
346 354
347 self._is_cumulative = is_cumulative 355 self._is_cumulative = is_cumulative
348 self.bucketer = bucketer 356 self.bucketer = bucketer
349 357
350 def _populate_value(self, metric, value, start_time): 358 def _populate_value(self, metric, value, start_time):
351 pb = metric.distribution 359 pb = metric.distribution
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
425 self._set(fields, target_fields, value) 433 self._set(fields, target_fields, value)
426 434
427 def is_cumulative(self): 435 def is_cumulative(self):
428 raise NotImplementedError() # Keep this class abstract. 436 raise NotImplementedError() # Keep this class abstract.
429 437
430 438
431 class CumulativeDistributionMetric(DistributionMetric): 439 class CumulativeDistributionMetric(DistributionMetric):
432 """A DistributionMetric with is_cumulative set to True.""" 440 """A DistributionMetric with is_cumulative set to True."""
433 441
434 def __init__(self, name, bucketer=None, fields=None, 442 def __init__(self, name, bucketer=None, fields=None,
435 description=None): 443 description=None, units=None):
436 super(CumulativeDistributionMetric, self).__init__( 444 super(CumulativeDistributionMetric, self).__init__(
437 name, 445 name,
438 is_cumulative=True, 446 is_cumulative=True,
439 bucketer=bucketer, 447 bucketer=bucketer,
440 fields=fields, 448 fields=fields,
441 description=description) 449 description=description,
450 units=units)
442 451
443 def is_cumulative(self): 452 def is_cumulative(self):
444 return True 453 return True
445 454
446 455
447 class NonCumulativeDistributionMetric(DistributionMetric): 456 class NonCumulativeDistributionMetric(DistributionMetric):
448 """A DistributionMetric with is_cumulative set to False.""" 457 """A DistributionMetric with is_cumulative set to False."""
449 458
450 def __init__(self, name, bucketer=None, fields=None, 459 def __init__(self, name, bucketer=None, fields=None,
451 description=None): 460 description=None, units=None):
452 super(NonCumulativeDistributionMetric, self).__init__( 461 super(NonCumulativeDistributionMetric, self).__init__(
453 name, 462 name,
454 is_cumulative=False, 463 is_cumulative=False,
455 bucketer=bucketer, 464 bucketer=bucketer,
456 fields=fields, 465 fields=fields,
457 description=description) 466 description=description,
467 units=units)
458 468
459 def is_cumulative(self): 469 def is_cumulative(self):
460 return False 470 return False
471
472
473 class MetaMetricsDataUnits(type):
474 """Metaclass to populate the enum values of metrics_pb2.MetricsData.Units
agable 2016/06/29 18:25:23 nit: all our docstrings start with a single senten
ddoman 2016/07/06 03:35:37 Done.
475 as class-level attributes.
476 """
477 def __new__(mcs, name, bases, attrs):
478 attrs.update(metrics_pb2.MetricsData.Units.items())
479 return super(MetaMetricsDataUnits, mcs).__new__(mcs, name, bases, attrs)
480
481
482 class MetricsDataUnits(object):
483 """Wrapper class for MetricsData.Units. Providing this wrapper class,
484 applications that use ts_mon.metrics don't need to import
485 metrics_pb2.MetricsData.
486
487 See infra_libs/ts_mon/protos/metrics.proto for a list of supported units.
488 """
489 __metaclass__ = MetaMetricsDataUnits
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698