| Index: client/third_party/infra_libs/ts_mon/common/metrics.py
|
| diff --git a/client/third_party/infra_libs/ts_mon/common/metrics.py b/client/third_party/infra_libs/ts_mon/common/metrics.py
|
| index 9cb565abca6f178f1122c9f2c8da6fad8ebcedee..5ef401c34e96ec46981ae32110cd3026108bb78f 100644
|
| --- a/client/third_party/infra_libs/ts_mon/common/metrics.py
|
| +++ b/client/third_party/infra_libs/ts_mon/common/metrics.py
|
| @@ -7,8 +7,7 @@
|
| import copy
|
| import re
|
|
|
| -from infra_libs.ts_mon.protos.current import metrics_pb2
|
| -from infra_libs.ts_mon.protos.new import metrics_pb2 as new_metrics_pb2
|
| +from infra_libs.ts_mon.protos import metrics_pb2
|
|
|
| from infra_libs.ts_mon.common import distribution
|
| from infra_libs.ts_mon.common import errors
|
| @@ -22,10 +21,8 @@ class Field(object):
|
| FIELD_NAME_PATTERN = re.compile(r'[A-Za-z_][A-Za-z0-9_]*')
|
|
|
| allowed_python_types = None
|
| - v1_type = None
|
| - v2_type = None
|
| - v1_field = None
|
| - v2_field = None
|
| + type_enum = None
|
| + field_name = None
|
|
|
| def __init__(self, name):
|
| if not self.FIELD_NAME_PATTERN.match(name):
|
| @@ -40,35 +37,26 @@ class Field(object):
|
| raise errors.MonitoringInvalidFieldTypeError(
|
| metric_name, self.name, value)
|
|
|
| - def populate_proto_v1(self, proto, value):
|
| - setattr(proto, self.v1_field, value)
|
| -
|
| - def populate_proto_v2(self, proto, value):
|
| - setattr(proto, self.v2_field, value)
|
| + def populate_proto(self, proto, value):
|
| + setattr(proto, self.field_name, value)
|
|
|
|
|
| class StringField(Field):
|
| allowed_python_types = basestring
|
| - v1_type = metrics_pb2.MetricsField.STRING
|
| - v2_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor.STRING
|
| - v1_field = 'string_value'
|
| - v2_field = 'string_value'
|
| + type_enum = metrics_pb2.MetricsDataSet.MetricFieldDescriptor.STRING
|
| + field_name = 'string_value'
|
|
|
|
|
| class IntegerField(Field):
|
| allowed_python_types = (int, long)
|
| - v1_type = metrics_pb2.MetricsField.INT
|
| - v2_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor.INT64
|
| - v1_field = 'int_value'
|
| - v2_field = 'int64_value'
|
| + type_enum = metrics_pb2.MetricsDataSet.MetricFieldDescriptor.INT64
|
| + field_name = 'int64_value'
|
|
|
|
|
| class BooleanField(Field):
|
| allowed_python_types = bool
|
| - v1_type = metrics_pb2.MetricsField.BOOL
|
| - v2_type = new_metrics_pb2.MetricsDataSet.MetricFieldDescriptor.BOOL
|
| - v1_field = 'bool_value'
|
| - v2_field = 'bool_value'
|
| + type_enum = metrics_pb2.MetricsDataSet.MetricFieldDescriptor.BOOL
|
| + field_name = 'bool_value'
|
|
|
|
|
| class Metric(object):
|
| @@ -87,11 +75,10 @@ class Metric(object):
|
| set() or increment() methods to modify a particular value, or passed to the
|
| constructor in which case they will be used as the defaults for this Metric.
|
|
|
| - The unit of measurement for Metric data can be specified with MetricsDataUnits
|
| - when a Metric object is created:
|
| + The unit of measurement for Metric data should be specified with
|
| + MetricsDataUnits when a Metric object is created:
|
| e.g., MetricsDataUnits.SECONDS, MetricsDataUnits.BYTES, and etc..,
|
| - A full list of supported units can be found in the following protobuf file
|
| - : infra_libs/ts_mon/protos/metrics.proto
|
| + See `MetricsDataUnits` class for a full list of units.
|
|
|
| Do not directly instantiate an object of this class.
|
| Use the concrete child classes instead:
|
| @@ -115,9 +102,9 @@ class Metric(object):
|
| field_spec (list): a list of Field subclasses to define the fields that
|
| are allowed on this metric. Pass a list of either
|
| StringField, IntegerField or BooleanField here.
|
| - units (int): the unit used to measure data for given
|
| - metric. Please use the attributes of MetricDataUnit to find
|
| - valid integer values for this argument.
|
| + units (string): the unit used to measure data for given metric. Some
|
| + common units are pre-defined in the MetricsDataUnits
|
| + class.
|
| """
|
| field_spec = field_spec or []
|
|
|
| @@ -143,6 +130,10 @@ class Metric(object):
|
| interface.register(self)
|
|
|
| @property
|
| + def field_spec(self):
|
| + return list(self._field_spec)
|
| +
|
| + @property
|
| def name(self):
|
| return self._name
|
|
|
| @@ -150,84 +141,58 @@ class Metric(object):
|
| def start_time(self):
|
| return self._start_time
|
|
|
| + @property
|
| + def units(self):
|
| + return self._units
|
| +
|
| def is_cumulative(self):
|
| raise NotImplementedError()
|
|
|
| def unregister(self):
|
| interface.unregister(self)
|
|
|
| - @staticmethod
|
| - def _map_units_to_string(units):
|
| - """Map MetricsDataUnits to the corresponding string according to:
|
| - http://unitsofmeasure.org/ucum.html because that's what the new proto
|
| - requires."""
|
| - if units in _UNITS_TO_STRING:
|
| - return _UNITS_TO_STRING[units]
|
| - else:
|
| - return '{unknown}'
|
| -
|
| - def _populate_data_set(self, data_set):
|
| + def populate_data_set(self, data_set):
|
| """Populate MetricsDataSet."""
|
| data_set.metric_name = '%s%s' % (interface.state.metric_name_prefix,
|
| self._name)
|
| data_set.description = self._description or ''
|
| - data_set.annotations.unit = self._map_units_to_string(self._units)
|
| + if self._units is not None:
|
| + data_set.annotations.unit = self._units
|
|
|
| if self.is_cumulative():
|
| - data_set.stream_kind = new_metrics_pb2.CUMULATIVE
|
| + data_set.stream_kind = metrics_pb2.CUMULATIVE
|
| else:
|
| - data_set.stream_kind = new_metrics_pb2.GAUGE
|
| + data_set.stream_kind = metrics_pb2.GAUGE
|
|
|
| self._populate_value_type(data_set)
|
| self._populate_field_descriptors(data_set)
|
|
|
| - def _populate_data(self, data, start_time, end_time, fields, value):
|
| + def populate_data(self, data, start_time, end_time, fields, value):
|
| """Populate a new metrics_pb2.MetricsData.
|
|
|
| Args:
|
| - data_ (new_metrics_pb2.MetricsData): protocol buffer into
|
| + data (metrics_pb2.MetricsData): protocol buffer into
|
| which to populate the current metric values.
|
| start_time (int): timestamp in microseconds since UNIX epoch.
|
| """
|
| data.start_timestamp.seconds = int(start_time)
|
| data.end_timestamp.seconds = int(end_time)
|
|
|
| - self._populate_fields_new(data, fields)
|
| - self._populate_value_new(data, value)
|
| -
|
| - def serialize_to(self, metric_pb, start_time, fields, value, target):
|
| - """Generate metrics_pb2.MetricsData messages for this metric.
|
| -
|
| - Args:
|
| - metric_pb (metrics_pb2.MetricsData): protocol buffer into which
|
| - to serialize the current metric values.
|
| - start_time (int): timestamp in microseconds since UNIX epoch.
|
| - target (Target): a Target to use.
|
| - """
|
| -
|
| - metric_pb.metric_name_prefix = interface.state.metric_name_prefix
|
| - metric_pb.name = self._name
|
| - metric_pb.description = self._description
|
| - if self._units is not None:
|
| - metric_pb.units = self._units
|
| -
|
| - self._populate_value(metric_pb, value, start_time)
|
| - self._populate_fields(metric_pb, fields)
|
| -
|
| - target._populate_target_pb(metric_pb)
|
| + self._populate_fields(data, fields)
|
| + self._populate_value(data, value)
|
|
|
| def _populate_field_descriptors(self, data_set):
|
| """Populate `field_descriptor` in MetricsDataSet.
|
|
|
| Args:
|
| - data_set (new_metrics_pb2.MetricsDataSet): a data set protobuf to populate
|
| + data_set (metrics_pb2.MetricsDataSet): a data set protobuf to populate
|
| """
|
| for spec in self._field_spec:
|
| descriptor = data_set.field_descriptor.add()
|
| descriptor.name = spec.name
|
| - descriptor.field_type = spec.v2_type
|
| + descriptor.field_type = spec.type_enum
|
|
|
| - def _populate_fields_new(self, data, field_values):
|
| + def _populate_fields(self, data, field_values):
|
| """Fill in the fields attribute of a metric protocol buffer.
|
|
|
| Args:
|
| @@ -237,20 +202,7 @@ class Metric(object):
|
| for spec, value in zip(self._field_spec, field_values):
|
| field = data.field.add()
|
| field.name = spec.name
|
| - spec.populate_proto_v2(field, value)
|
| -
|
| - def _populate_fields(self, metric, field_values):
|
| - """Fill in the fields attribute of a metric protocol buffer.
|
| -
|
| - Args:
|
| - metric (metrics_pb2.MetricsData): a metrics protobuf to populate
|
| - field_values (tuple): field values
|
| - """
|
| - for spec, value in zip(self._field_spec, field_values):
|
| - field = metric.fields.add()
|
| - field.name = spec.name
|
| - field.type = spec.v1_type
|
| - spec.populate_proto_v1(field, value)
|
| + spec.populate_proto(field, value)
|
|
|
| def _validate_fields(self, fields):
|
| """Checks the correct number and types of field values were provided.
|
| @@ -282,17 +234,7 @@ class Metric(object):
|
|
|
| return tuple(fields[spec.name] for spec in self._field_spec)
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - """Fill in the the data values of a metric protocol buffer.
|
| -
|
| - Args:
|
| - metric (metrics_pb2.MetricsData): a metrics protobuf to populate
|
| - value (see concrete class): the value of the metric to be set
|
| - start_time (int): timestamp in microseconds since UNIX epoch.
|
| - """
|
| - raise NotImplementedError()
|
| -
|
| - def _populate_value_new(self, data, value):
|
| + def _populate_value(self, data, value):
|
| """Fill in the the data values of a metric protocol buffer.
|
|
|
| Args:
|
| @@ -358,14 +300,11 @@ class Metric(object):
|
| class StringMetric(Metric):
|
| """A metric whose value type is a string."""
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - metric.string_value = value
|
| -
|
| - def _populate_value_new(self, data, value):
|
| + def _populate_value(self, data, value):
|
| data.string_value = value
|
|
|
| def _populate_value_type(self, data_set):
|
| - data_set.value_type = new_metrics_pb2.STRING
|
| + data_set.value_type = metrics_pb2.STRING
|
|
|
| def set(self, value, fields=None, target_fields=None):
|
| if not isinstance(value, basestring):
|
| @@ -379,14 +318,11 @@ class StringMetric(Metric):
|
| class BooleanMetric(Metric):
|
| """A metric whose value type is a boolean."""
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - metric.boolean_value = value
|
| -
|
| - def _populate_value_new(self, data, value):
|
| + def _populate_value(self, data, value):
|
| data.bool_value = value
|
|
|
| def _populate_value_type(self, data_set):
|
| - data_set.value_type = new_metrics_pb2.BOOL
|
| + data_set.value_type = metrics_pb2.BOOL
|
|
|
| def set(self, value, fields=None, target_fields=None):
|
| if not isinstance(value, bool):
|
| @@ -410,21 +346,17 @@ class NumericMetric(Metric): # pylint: disable=abstract-method
|
| class CounterMetric(NumericMetric):
|
| """A metric whose value type is a monotonically increasing integer."""
|
|
|
| - def __init__(self, name, description, field_spec=None, start_time=None,
|
| + def __init__(self, name, description, field_spec, start_time=None,
|
| units=None):
|
| super(CounterMetric, self).__init__(
|
| name, description, field_spec, units=units)
|
| self._start_time = start_time
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - metric.counter = value
|
| - metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
|
| -
|
| - def _populate_value_new(self, data, value):
|
| + def _populate_value(self, data, value):
|
| data.int64_value = value
|
|
|
| def _populate_value_type(self, data_set):
|
| - data_set.value_type = new_metrics_pb2.INT64
|
| + data_set.value_type = metrics_pb2.INT64
|
|
|
| def set(self, value, fields=None, target_fields=None):
|
| if not isinstance(value, (int, long)):
|
| @@ -443,14 +375,11 @@ class CounterMetric(NumericMetric):
|
| class GaugeMetric(NumericMetric):
|
| """A metric whose value type is an integer."""
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - metric.gauge = value
|
| -
|
| - def _populate_value_new(self, data, value):
|
| + def _populate_value(self, data, value):
|
| data.int64_value = value
|
|
|
| def _populate_value_type(self, data_set):
|
| - data_set.value_type = new_metrics_pb2.INT64
|
| + data_set.value_type = metrics_pb2.INT64
|
|
|
| def set(self, value, fields=None, target_fields=None):
|
| if not isinstance(value, (int, long)):
|
| @@ -464,21 +393,17 @@ class GaugeMetric(NumericMetric):
|
| class CumulativeMetric(NumericMetric):
|
| """A metric whose value type is a monotonically increasing float."""
|
|
|
| - def __init__(self, name, description, field_spec=None, start_time=None,
|
| + def __init__(self, name, description, field_spec, start_time=None,
|
| units=None):
|
| super(CumulativeMetric, self).__init__(
|
| name, description, field_spec, units=units)
|
| self._start_time = start_time
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - metric.cumulative_double_value = value
|
| - metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
|
| -
|
| - def _populate_value_new(self, data, value):
|
| + def _populate_value(self, data, value):
|
| data.double_value = value
|
|
|
| def _populate_value_type(self, data_set):
|
| - data_set.value_type = new_metrics_pb2.DOUBLE
|
| + data_set.value_type = metrics_pb2.DOUBLE
|
|
|
| def set(self, value, fields=None, target_fields=None):
|
| if not isinstance(value, (float, int)):
|
| @@ -492,14 +417,11 @@ class CumulativeMetric(NumericMetric):
|
| class FloatMetric(NumericMetric):
|
| """A metric whose value type is a float."""
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - metric.noncumulative_double_value = value
|
| -
|
| - def _populate_value_new(self, metric, value):
|
| + def _populate_value(self, metric, value):
|
| metric.double_value = value
|
|
|
| def _populate_value_type(self, data_set_pb):
|
| - data_set_pb.value_type = new_metrics_pb2.DOUBLE
|
| + data_set_pb.value_type = metrics_pb2.DOUBLE
|
|
|
| def set(self, value, fields=None, target_fields=None):
|
| if not isinstance(value, (float, int)):
|
| @@ -518,13 +440,7 @@ class _DistributionMetricBase(Metric):
|
| for many kinds of data, but you may want to provide a FixedWidthBucketer or
|
| GeometricBucketer with different parameters."""
|
|
|
| - CANONICAL_SPEC_TYPES = {
|
| - 2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_2,
|
| - 10**0.2: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10_P_0_2,
|
| - 10: metrics_pb2.PrecomputedDistribution.CANONICAL_POWERS_OF_10,
|
| - }
|
| -
|
| - def __init__(self, name, description, field_spec=None, is_cumulative=True,
|
| + def __init__(self, name, description, field_spec, is_cumulative=True,
|
| bucketer=None, start_time=None, units=None):
|
| super(_DistributionMetricBase, self).__init__(
|
| name, description, field_spec, units=units)
|
| @@ -536,45 +452,13 @@ class _DistributionMetricBase(Metric):
|
| self._is_cumulative = is_cumulative
|
| self.bucketer = bucketer
|
|
|
| - def _populate_value(self, metric, value, start_time):
|
| - pb = metric.distribution
|
| -
|
| - pb.is_cumulative = self._is_cumulative
|
| - if self._is_cumulative:
|
| - metric.start_timestamp_us = int(start_time * MICROSECONDS_PER_SECOND)
|
| -
|
| - # Copy the bucketer params.
|
| - if (value.bucketer.width == 0 and
|
| - value.bucketer.growth_factor in self.CANONICAL_SPEC_TYPES):
|
| - pb.spec_type = self.CANONICAL_SPEC_TYPES[value.bucketer.growth_factor]
|
| - else:
|
| - pb.spec_type = metrics_pb2.PrecomputedDistribution.CUSTOM_PARAMETERIZED
|
| - pb.width = value.bucketer.width
|
| - pb.growth_factor = value.bucketer.growth_factor
|
| - pb.num_buckets = value.bucketer.num_finite_buckets
|
| -
|
| - # Copy the distribution bucket values. Only include the finite buckets, not
|
| - # the overflow buckets on each end.
|
| - pb.bucket.extend(self._running_zero_generator(
|
| - value.buckets.get(i, 0) for i in
|
| - xrange(1, value.bucketer.total_buckets - 1)))
|
| -
|
| - # Add the overflow buckets if present.
|
| - if value.bucketer.underflow_bucket in value.buckets:
|
| - pb.underflow = value.buckets[value.bucketer.underflow_bucket]
|
| - if value.bucketer.overflow_bucket in value.buckets:
|
| - pb.overflow = value.buckets[value.bucketer.overflow_bucket]
|
| -
|
| - if value.count != 0:
|
| - pb.mean = float(value.sum) / value.count
|
| -
|
| - def _populate_value_new(self, metric, value):
|
| + def _populate_value(self, metric, value):
|
| pb = metric.distribution_value
|
|
|
| # Copy the bucketer params.
|
| if value.bucketer.width == 0:
|
| pb.exponential_buckets.growth_factor = value.bucketer.growth_factor
|
| - pb.exponential_buckets.scale = 1.0
|
| + pb.exponential_buckets.scale = value.bucketer.scale
|
| pb.exponential_buckets.num_finite_buckets = (
|
| value.bucketer.num_finite_buckets)
|
| else:
|
| @@ -592,25 +476,7 @@ class _DistributionMetricBase(Metric):
|
| pb.mean = float(value.sum) / max(value.count, 1)
|
|
|
| def _populate_value_type(self, data_set_pb):
|
| - data_set_pb.value_type = new_metrics_pb2.DISTRIBUTION
|
| -
|
| - @staticmethod
|
| - def _running_zero_generator(iterable):
|
| - """Compresses sequences of zeroes in the iterable into negative zero counts.
|
| -
|
| - For example an input of [1, 0, 0, 0, 2] is converted to [1, -3, 2].
|
| - """
|
| -
|
| - count = 0
|
| -
|
| - for value in iterable:
|
| - if value == 0:
|
| - count += 1
|
| - else:
|
| - if count != 0:
|
| - yield -count
|
| - count = 0
|
| - yield value
|
| + data_set_pb.value_type = metrics_pb2.DISTRIBUTION
|
|
|
| def add(self, value, fields=None, target_fields=None):
|
| def modify_fn(dist, value):
|
| @@ -646,8 +512,7 @@ class _DistributionMetricBase(Metric):
|
| class CumulativeDistributionMetric(_DistributionMetricBase):
|
| """A DistributionMetric with is_cumulative set to True."""
|
|
|
| - def __init__(self, name, description, field_spec=None, bucketer=None,
|
| - units=None):
|
| + def __init__(self, name, description, field_spec, bucketer=None, units=None):
|
| super(CumulativeDistributionMetric, self).__init__(
|
| name, description, field_spec,
|
| is_cumulative=True,
|
| @@ -658,8 +523,7 @@ class CumulativeDistributionMetric(_DistributionMetricBase):
|
| class NonCumulativeDistributionMetric(_DistributionMetricBase):
|
| """A DistributionMetric with is_cumulative set to False."""
|
|
|
| - def __init__(self, name, description, field_spec=None, bucketer=None,
|
| - units=None):
|
| + def __init__(self, name, description, field_spec, bucketer=None, units=None):
|
| super(NonCumulativeDistributionMetric, self).__init__(
|
| name, description, field_spec,
|
| is_cumulative=False,
|
| @@ -667,34 +531,22 @@ class NonCumulativeDistributionMetric(_DistributionMetricBase):
|
| units=units)
|
|
|
|
|
| -class MetaMetricsDataUnits(type):
|
| - """Metaclass to populate the enum values of metrics_pb2.MetricsData.Units."""
|
| - def __new__(mcs, name, bases, attrs):
|
| - attrs.update(metrics_pb2.MetricsData.Units.items())
|
| - return super(MetaMetricsDataUnits, mcs).__new__(mcs, name, bases, attrs)
|
| -
|
| -
|
| class MetricsDataUnits(object):
|
| - """An enumeration class for units of measurement for Metrics data.
|
| - See infra_libs/ts_mon/protos/metrics.proto for a full list of supported units.
|
| - """
|
| - __metaclass__ = MetaMetricsDataUnits
|
| -
|
| -_UNITS_TO_STRING = {
|
| - MetricsDataUnits.UNKNOWN_UNITS: '{unknown}',
|
| - MetricsDataUnits.SECONDS: 's',
|
| - MetricsDataUnits.MILLISECONDS: 'ms',
|
| - MetricsDataUnits.MICROSECONDS: 'us',
|
| - MetricsDataUnits.NANOSECONDS: 'ns',
|
| - MetricsDataUnits.BITS: 'B',
|
| - MetricsDataUnits.BYTES: 'By',
|
| - MetricsDataUnits.KILOBYTES: 'kBy',
|
| - MetricsDataUnits.MEGABYTES: 'MBy',
|
| - MetricsDataUnits.GIGABYTES: 'GBy',
|
| - MetricsDataUnits.KIBIBYTES: 'kiBy',
|
| - MetricsDataUnits.MEBIBYTES: 'MiBy',
|
| - MetricsDataUnits.GIBIBYTES: 'GiBy',
|
| - MetricsDataUnits.AMPS: 'A',
|
| - MetricsDataUnits.MILLIAMPS : 'mA',
|
| - MetricsDataUnits.DEGREES_CELSIUS: 'Cel'
|
| -}
|
| + """An container for units of measurement for Metrics data."""
|
| +
|
| + UNKNOWN_UNITS = '{unknown}'
|
| + SECONDS = 's'
|
| + MILLISECONDS = 'ms'
|
| + MICROSECONDS = 'us'
|
| + NANOSECONDS = 'ns'
|
| + BITS = 'B'
|
| + BYTES = 'By'
|
| + KILOBYTES = 'kBy'
|
| + MEGABYTES = 'MBy'
|
| + GIGABYTES = 'GBy'
|
| + KIBIBYTES = 'kiBy'
|
| + MEBIBYTES = 'MiBy'
|
| + GIBIBYTES = 'GiBy'
|
| + AMPS = 'A'
|
| + MILLIAMPS = 'mA'
|
| + DEGREES_CELSIUS = 'Cel'
|
|
|