1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# -*- coding: utf-8 -*-
# Copyright 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class BaseMetric(object):
def __init__(self, name, keys=[]):
self.name = name
self.keys = keys # OK not to clone as we never write it
def _render_key(self, values):
# TODO: some kind of value escape
return ",".join(["%s=%s" % kv for kv in zip(self.keys, values)])
class CounterMetric(BaseMetric):
"""The simplest kind of metric; one that stores a monotonically-increasing
integer that counts events."""
def __init__(self, *args, **kwargs):
super(CounterMetric, self).__init__(*args, **kwargs)
self.counts = {}
# Scalar metrics are never empty
if not len(self.keys):
self.counts[()] = 0
def inc(self, *values):
if len(values) != len(self.keys):
raise ValueError("Expected as many values to inc() as keys (%d)" %
(len(self.keys))
)
# TODO: should assert that the tag values are all strings
if values not in self.counts:
self.counts[values] = 1
else:
self.counts[values] += 1
def fetch(self):
return dict(self.counts)
def render(self):
if not len(self.keys):
return ["%s %d" % (self.name, self.counts[()])]
return ["%s{%s} %d" % (self.name, self._render_key(k), self.counts[k])
for k in sorted(self.counts.keys())]
class CallbackMetric(BaseMetric):
"""A metric that returns the numeric value returned by a callback whenever
it is rendered. Typically this is used to implement gauges that yield the
size or other state of some in-memory object by actively querying it."""
def __init__(self, name, callback, keys=[]):
super(CallbackMetric, self).__init__(name, keys=keys)
self.callback = callback
def render(self):
# TODO(paul): work out something we can do with keys and vectors
return ["%s %d" % (self.name, self.callback())]
class CacheCounterMetric(object):
"""A combination of two CounterMetrics, one to count cache hits and one to
count misses.
This metric generates standard metric name pairs, so that monitoring rules
can easily be applied to measure hit ratio."""
def __init__(self, name, keys=[]):
self.name = name
self.hits = CounterMetric(name + ":hits", keys=keys)
self.misses = CounterMetric(name + ":misses", keys=keys)
def inc_hits(self, *values):
self.hits.inc(*values)
def inc_misses(self, *values):
self.misses.inc(*values)
def render(self):
return self.hits.render() + self.misses.render()
|