forked from MirrorHub/synapse
Initial hack at a TimerMetric; for storing counts + duration accumulators
This commit is contained in:
parent
e1a7e3564f
commit
72625f2f4d
|
@ -14,6 +14,15 @@
|
|||
# limitations under the License.
|
||||
|
||||
|
||||
from itertools import chain
|
||||
|
||||
|
||||
# TODO(paul): I can't believe Python doesn't have one of these
|
||||
def map_concat(func, items):
|
||||
# flatten a list-of-lists
|
||||
return list(chain.from_iterable(map(func, items)))
|
||||
|
||||
|
||||
class BaseMetric(object):
|
||||
|
||||
def __init__(self, name, keys=[]):
|
||||
|
@ -87,6 +96,45 @@ class CallbackMetric(BaseMetric):
|
|||
return ["%s{%s} %d" % (self.name, self._render_key(k), value[k])
|
||||
for k in sorted(value.keys())]
|
||||
|
||||
|
||||
class TimerMetric(CounterMetric):
|
||||
"""A combination of an event counter and a time accumulator, which counts
|
||||
both the number of events and how long each one takes.
|
||||
|
||||
TODO(paul): Try to export some heatmap-style stats?
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TimerMetric, self).__init__(*args, **kwargs)
|
||||
|
||||
self.times = {}
|
||||
|
||||
# Scalar metrics are never empty
|
||||
if self.is_scalar():
|
||||
self.times[()] = 0
|
||||
|
||||
def inc_time(self, msec, *values):
|
||||
self.inc(*values)
|
||||
|
||||
if values not in self.times:
|
||||
self.times[values] = msec
|
||||
else:
|
||||
self.times[values] += msec
|
||||
|
||||
def render(self):
|
||||
if self.is_scalar():
|
||||
return ["%s:count %d" % (self.name, self.counts[()]),
|
||||
"%s:msec %d" % (self.name, self.times[()])]
|
||||
|
||||
def render_item(k):
|
||||
keystr = self._render_key(k)
|
||||
|
||||
return ["%s{%s}:count %d" % (self.name, keystr, self.counts[k]),
|
||||
"%s{%s}:msec %d" % (self.name, keystr, self.times[k])]
|
||||
|
||||
return map_concat(render_item, sorted(self.counts.keys()))
|
||||
|
||||
|
||||
class CacheMetric(object):
|
||||
"""A combination of two CounterMetrics, one to count cache hits and one to
|
||||
count misses, and a callback metric to yield the current size.
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
from tests import unittest
|
||||
|
||||
from synapse.metrics.metric import (
|
||||
CounterMetric, CallbackMetric, CacheMetric
|
||||
CounterMetric, CallbackMetric, TimerMetric, CacheMetric
|
||||
)
|
||||
|
||||
|
||||
|
@ -97,6 +97,40 @@ class CallbackMetricTestCase(unittest.TestCase):
|
|||
])
|
||||
|
||||
|
||||
class TimerMetricTestCase(unittest.TestCase):
|
||||
|
||||
def test_scalar(self):
|
||||
metric = TimerMetric("thing")
|
||||
|
||||
self.assertEquals(metric.render(), [
|
||||
"thing:count 0",
|
||||
"thing:msec 0",
|
||||
])
|
||||
|
||||
metric.inc_time(500)
|
||||
|
||||
self.assertEquals(metric.render(), [
|
||||
"thing:count 1",
|
||||
"thing:msec 500",
|
||||
])
|
||||
|
||||
def test_vector(self):
|
||||
metric = TimerMetric("queries", keys=["verb"])
|
||||
|
||||
self.assertEquals(metric.render(), [])
|
||||
|
||||
metric.inc_time(300, "SELECT")
|
||||
metric.inc_time(200, "SELECT")
|
||||
metric.inc_time(800, "INSERT")
|
||||
|
||||
self.assertEquals(metric.render(), [
|
||||
"queries{verb=INSERT}:count 1",
|
||||
"queries{verb=INSERT}:msec 800",
|
||||
"queries{verb=SELECT}:count 2",
|
||||
"queries{verb=SELECT}:msec 500",
|
||||
])
|
||||
|
||||
|
||||
class CacheMetricTestCase(unittest.TestCase):
|
||||
|
||||
def test_cache(self):
|
||||
|
|
Loading…
Reference in a new issue