mechanism to render metrics with alternative names

pull/2785/head
Richard van der Hoff 2018-01-15 16:58:41 +00:00
parent 80fa610f9c
commit 992018d1c0
1 changed files with 40 additions and 13 deletions

View File

@ -17,24 +17,33 @@
from itertools import chain from itertools import chain
# TODO(paul): I can't believe Python doesn't have one of these def flatten(items):
def map_concat(func, items): """Flatten a list of lists
# flatten a list-of-lists
return list(chain.from_iterable(map(func, items))) Args:
items: iterable[iterable[X]]
Returns:
list[X]: flattened list
"""
return list(chain.from_iterable(items))
class BaseMetric(object): class BaseMetric(object):
"""Base class for metrics which report a single value per label set """Base class for metrics which report a single value per label set
""" """
def __init__(self, name, labels=[]): def __init__(self, name, labels=[], alternative_names=[]):
""" """
Args: Args:
name (str): principal name for this metric name (str): principal name for this metric
labels (list(str)): names of the labels which will be reported labels (list(str)): names of the labels which will be reported
for this metric for this metric
alternative_names (iterable(str)): list of alternative names for
this metric. This can be useful to provide a migration path
when renaming metrics.
""" """
self.name = name self._names = [name] + list(alternative_names)
self.labels = labels # OK not to clone as we never write it self.labels = labels # OK not to clone as we never write it
def dimension(self): def dimension(self):
@ -55,6 +64,22 @@ class BaseMetric(object):
for k, v in zip(self.labels, values)]) for k, v in zip(self.labels, values)])
) )
def _render_for_labels(self, label_values, value):
"""Render this metric for a single set of labels
Args:
label_values (list[str]): values for each of the labels
value: value of the metric at with these labels
Returns:
iterable[str]: rendered metric
"""
rendered_labels = self._render_key(label_values)
return (
"%s%s %.12g" % (name, rendered_labels, value)
for name in self._names
)
def render(self): def render(self):
"""Render this metric """Render this metric
@ -110,11 +135,11 @@ class CounterMetric(BaseMetric):
def inc(self, *values): def inc(self, *values):
self.inc_by(1, *values) self.inc_by(1, *values)
def render_item(self, k):
return ["%s%s %.12g" % (self.name, self._render_key(k), self.counts[k])]
def render(self): def render(self):
return map_concat(self.render_item, sorted(self.counts.keys())) return flatten(
self._render_for_labels(k, self.counts[k])
for k in sorted(self.counts.keys())
)
class CallbackMetric(BaseMetric): class CallbackMetric(BaseMetric):
@ -131,10 +156,12 @@ class CallbackMetric(BaseMetric):
value = self.callback() value = self.callback()
if self.is_scalar(): if self.is_scalar():
return ["%s %.12g" % (self.name, value)] return list(self._render_for_labels([], value))
return ["%s%s %.12g" % (self.name, self._render_key(k), value[k]) return flatten(
for k in sorted(value.keys())] self._render_for_labels(k, value[k])
for k in sorted(value.keys())
)
class DistributionMetric(object): class DistributionMetric(object):