monasca-agent/monasca_agent/common/metrics.py

136 lines
3.9 KiB
Python

# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
""" Metric data types
"""
import logging
from monasca_agent.common.exceptions import UnknownValue
log = logging.getLogger(__name__)
class Metric(object):
"""A base metric class """
def __init__(self, name, dimensions, tenant):
self.metric = {'name': name,
'dimensions': dimensions.copy()}
self.value_meta = None
self.value = None
self.timestamp = None
self.tenant = tenant
def measurement(self, value, timestamp):
measurement = self.metric.copy()
if self.value_meta:
measurement['value_meta'] = self.value_meta.copy()
else:
measurement['value_meta'] = None
measurement['value'] = value
measurement['timestamp'] = timestamp * 1000
envelope = {'measurement': measurement,
'tenant_id': self.tenant}
return envelope
def sample(self, value, sample_rate, timestamp):
"""Save a sample. """
raise NotImplementedError()
def flush(self):
"""Flush current sample. """
raise NotImplementedError()
class Gauge(Metric):
"""A metric that tracks a value at particular points in time. """
def __init__(self, name, dimensions, tenant=None):
super(Gauge, self).__init__(name, dimensions, tenant)
def sample(self, value, sample_rate, timestamp):
self.value = value
self.timestamp = timestamp
def flush(self):
if not self.value:
return []
envelope = self.measurement(self.value, self.timestamp)
self.value = None
return [envelope]
class Counter(Metric):
"""A metric that tracks a counter value. """
def __init__(self, name, dimensions, tenant=None):
super(Counter, self).__init__(name, dimensions, tenant)
self.value = 0
def sample(self, value, sample_rate, timestamp):
try:
self.value += value * int(1 / sample_rate)
self.timestamp = timestamp
except TypeError:
log.error("metric {} value {} sample_rate {}".
format(self.name, value, sample_rate))
def flush(self):
envelope = self.measurement(self.value, self.timestamp)
self.value = 0
return [envelope]
class Rate(Metric):
"""Track the rate of metrics over each flush interval """
def __init__(self, name, dimensions, tenant=None):
super(Rate, self).__init__(name, dimensions, tenant)
self.samples = []
def sample(self, value, sample_rate, timestamp):
self.samples.append((int(timestamp), value))
self.timestamp = timestamp
if len(self.samples) < 2:
self.value = None
else:
self.value = self._rate(self.samples[-2], self.samples[-1])
self.samples = self.samples[-1:]
if len(self.samples) < 2:
self.value = None
else:
self.value = self._rate(self.samples[-2], self.samples[-1])
self.samples = self.samples[-1:]
def _rate(self, sample1, sample2):
delta_t = sample2[0] - sample1[0]
delta_v = sample2[1] - sample1[1]
rate = None
if delta_v < 0:
log.debug('Metric {0} has a rate < 0. New value = {1} and old '
'value = {2}. Counter may have been Reset.'.
format(self.name, sample2[1], sample1[1]))
return rate
try:
rate = delta_v / float(delta_t)
except ZeroDivisionError as e:
log.exception('Error in sampling metric {0}, time difference '
'between current time and last_update time is '
'0, returned {1}'.format(self.name, e))
return rate
def flush(self):
if not self.value:
return []
envelope = self.measurement(self.value, self.timestamp)
self.value = None
return [envelope]