| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |