Merge lp:~lucio.torre/txstatsd/sli-add-size into lp:txstatsd

Proposed by Lucio Torre
Status: Merged
Approved by: Lucio Torre
Approved revision: 96
Merged at revision: 96
Proposed branch: lp:~lucio.torre/txstatsd/sli-add-size
Merge into: lp:txstatsd
Diff against target: 330 lines (+154/-29)
6 files modified
twisted/plugins/sli_plugin.py (+4/-4)
txstatsd/metrics/extendedmetrics.py (+1/-0)
txstatsd/metrics/metrics.py (+27/-9)
txstatsd/metrics/slimetric.py (+30/-15)
txstatsd/tests/metrics/test_sli.py (+73/-1)
txstatsd/tests/test_metrics.py (+19/-0)
To merge this branch: bzr merge lp:~lucio.torre/txstatsd/sli-add-size
Reviewer Review Type Date Requested Status
Facundo Batista (community) Approve
Review via email: mp+110908@code.launchpad.net

Commit message

add sli size

Description of the change

Add size to sli reporting:

from the client side now you can do:
metrics.sli(path, duration, size) to report multi items slis or just
metrics.sli(path, duration) for single items or
metric.sli_error(path) for errors.

from the txstatsd side ive added the error counter and below and above now also take the optional slope value.

So,
 path => label IF below 5 2
means:
 label incoming metrics from path `path` with label `label` if duration < 5 + 2 * size

To post a comment you must log in.
Revision history for this message
Facundo Batista (facundo) wrote :

Like it

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'twisted/plugins/sli_plugin.py'
--- twisted/plugins/sli_plugin.py 2012-06-05 16:21:15 +0000
+++ twisted/plugins/sli_plugin.py 2012-06-18 20:53:18 +0000
@@ -63,11 +63,11 @@
63 cobj = method(*cparams.split(" "))63 cobj = method(*cparams.split(" "))
64 self.config[head][label] = cobj64 self.config[head][label] = cobj
6565
66 def build_above(self, value):66 def build_above(self, value, slope=0):
67 return AboveCondition(float(value))67 return AboveCondition(float(value), float(slope))
6868
69 def build_below(self, value):69 def build_below(self, value, slope=0):
70 return BelowCondition(float(value))70 return BelowCondition(float(value), float(slope))
7171
72 def build_between(self, low, hi):72 def build_between(self, low, hi):
73 return BetweenCondition(float(low), float(hi))73 return BetweenCondition(float(low), float(hi))
7474
=== modified file 'txstatsd/metrics/extendedmetrics.py'
--- txstatsd/metrics/extendedmetrics.py 2011-12-02 17:09:32 +0000
+++ txstatsd/metrics/extendedmetrics.py 2012-06-18 20:53:18 +0000
@@ -51,3 +51,4 @@
51 sample_rate)51 sample_rate)
52 self._metrics[name] = metric52 self._metrics[name] = metric
53 self._metrics[name].mark(duration)53 self._metrics[name].mark(duration)
54
5455
=== modified file 'txstatsd/metrics/metrics.py'
--- txstatsd/metrics/metrics.py 2012-02-13 21:15:25 +0000
+++ txstatsd/metrics/metrics.py 2012-06-18 20:53:18 +0000
@@ -7,13 +7,15 @@
77
88
9class GenericMetric(Metric):9class GenericMetric(Metric):
10 def __init__(self, connection, key, name, sample_rate=1):10 def __init__(self, connection, key, name):
11 super(GenericMetric, self).__init__(connection, name,11 super(GenericMetric, self).__init__(connection, name)
12 sample_rate=sample_rate)
13 self.key = key12 self.key = key
1413
15 def mark(self, value):14 def mark(self, value, extra=None):
16 self.send("%s|%s" % (value, self.key))15 if extra is None:
16 self.send("%s|%s" % (value, self.key))
17 else:
18 self.send("%s|%s|%s" % (value, self.key, extra))
1719
1820
19class Metrics(object):21class Metrics(object):
@@ -32,7 +34,7 @@
32 self._metrics = {}34 self._metrics = {}
33 self.last_time = 035 self.last_time = 0
3436
35 def report(self, name, value, metric_type, sample_rate=1):37 def report(self, name, value, metric_type, extra=None):
36 """Report a generic metric.38 """Report a generic metric.
3739
38 Used for server side plugins without client support.40 Used for server side plugins without client support.
@@ -41,10 +43,26 @@
41 if not name in self._metrics:43 if not name in self._metrics:
42 metric = GenericMetric(self.connection,44 metric = GenericMetric(self.connection,
43 metric_type,45 metric_type,
44 name,46 name)
45 sample_rate)
46 self._metrics[name] = metric47 self._metrics[name] = metric
47 self._metrics[name].mark(value)48 self._metrics[name].mark(value, extra)
49
50 def sli(self, name, duration, size=None):
51 """Report a service level metric.
52
53 The optional size parameter is used with linear thereshold slis.
54 So for example, to report a download you could use size and the size in
55 bytes of the file.
56 """
57 self.report(name, duration, "sli", size)
58
59 def sli_error(self, name):
60 """Report an error for a service level metric.
61
62 When something that is measures for service level errs (no time or size
63 are required/present) you can use this method to inform it.
64 """
65 self.report(name, "error", "sli")
4866
49 def gauge(self, name, value, sample_rate=1):67 def gauge(self, name, value, sample_rate=1):
50 """Report an instantaneous reading of a particular value."""68 """Report an instantaneous reading of a particular value."""
5169
=== modified file 'txstatsd/metrics/slimetric.py'
--- txstatsd/metrics/slimetric.py 2012-06-05 14:39:32 +0000
+++ txstatsd/metrics/slimetric.py 2012-06-18 20:53:18 +0000
@@ -3,20 +3,23 @@
33
4class BelowCondition(object):4class BelowCondition(object):
55
6 def __init__(self, value):6 def __init__(self, value, slope=0):
7 self.value = value7 self.value = value
8 self.slope = slope
89
9 def __call__(self, value):10 def __call__(self, value, size=1):
10 return value < self.value11 return value < self.value + self.slope * size
1112
1213
13class AboveCondition(object):14class AboveCondition(object):
1415
15 def __init__(self, value):16 def __init__(self, value, slope=0):
16 self.value = value17 self.value = value
1718 self.slope = slope
18 def __call__(self, value):19
19 return value > self.value20 def __call__(self, value, size=1):
21 return value > self.value + self.slope * size
22
2023
21class BetweenCondition(object):24class BetweenCondition(object):
2225
@@ -24,7 +27,7 @@
24 self.low = low27 self.low = low
25 self.hi = hi28 self.hi = hi
2629
27 def __call__(self, value):30 def __call__(self, value, size=1):
28 return self.low < value < self.hi31 return self.low < value < self.hi
2932
3033
@@ -38,16 +41,26 @@
38 def clear(self):41 def clear(self):
39 self.counts = dict((k, 0) for k in self.conditions)42 self.counts = dict((k, 0) for k in self.conditions)
40 self.count = 043 self.count = 0
44 self.error = 0
4145
42 def process(self, fields):46 def process(self, fields):
43 self.update(float(fields[0]))47 size = 1
4448 if len(fields) == 3:
45 def update(self, value):49 size = float(fields[2])
50
51 value = "error"
52 if value != fields[0]:
53 value = float(fields[0])
54 self.update(value, size)
55
56 def update(self, value, size=1):
46 self.count += 157 self.count += 1
47 for k, condition in self.conditions.items():58 if value == "error":
4859 self.error += 1
49 if condition(value):60 else:
50 self.counts[k] += 161 for k, condition in self.conditions.items():
62 if condition(value, size):
63 self.counts[k] += 1
5164
52 def flush(self, interval, timestamp):65 def flush(self, interval, timestamp):
53 metrics = []66 metrics = []
@@ -56,6 +69,8 @@
56 value, timestamp))69 value, timestamp))
57 metrics.append((self.name + ".count",70 metrics.append((self.name + ".count",
58 self.count, timestamp))71 self.count, timestamp))
72 metrics.append((self.name + ".error",
73 self.error, timestamp))
5974
60 self.clear()75 self.clear()
61 return metrics76 return metrics
6277
=== modified file 'txstatsd/tests/metrics/test_sli.py'
--- txstatsd/tests/metrics/test_sli.py 2012-06-05 16:21:15 +0000
+++ txstatsd/tests/metrics/test_sli.py 2012-06-18 20:53:18 +0000
@@ -7,6 +7,7 @@
7from txstatsd.metrics.slimetric import (7from txstatsd.metrics.slimetric import (
8 SLIMetricReporter, BetweenCondition, AboveCondition, BelowCondition)8 SLIMetricReporter, BetweenCondition, AboveCondition, BelowCondition)
9from txstatsd import service9from txstatsd import service
10from txstatsd.tests.test_processor import TestMessageProcessor
1011
1112
12class TestConditions(TestCase):13class TestConditions(TestCase):
@@ -27,6 +28,28 @@
27 self.assertEquals(c(6), False)28 self.assertEquals(c(6), False)
28 self.assertEquals(c(2.6), True)29 self.assertEquals(c(2.6), True)
2930
31 def test_below_linear(self):
32 c = BelowCondition(5, 1)
33 self.assertEquals(c(5.5, 1), True)
34 self.assertEquals(c(6.5, 2), True)
35 self.assertEquals(c(8.5, 3), False)
36
37 def test_above_linear(self):
38 c = AboveCondition(4, 1)
39 self.assertEquals(c(5.5, 1), True)
40 self.assertEquals(c(6.5, 2), True)
41 self.assertEquals(c(7, 3), False)
42
43
44class TestParsing(TestCase):
45 def setUp(self):
46 self.processor = TestMessageProcessor()
47
48 def test_parse(self):
49 self.processor.process('txstatsd.tests.users:100|sli')
50 self.processor.process('txstatsd.tests.users:100|sli|2')
51 self.processor.process('txstatsd.tests.users:error|sli')
52
3053
31class TestMetric(TestCase):54class TestMetric(TestCase):
32 def setUp(self):55 def setUp(self):
@@ -39,6 +62,13 @@
39 self.sli.update(1)62 self.sli.update(1)
40 self.assertEquals(self.sli.count, 2)63 self.assertEquals(self.sli.count, 2)
4164
65 def test_count_error(self):
66 self.sli.update(1)
67 self.sli.update("error")
68 self.assertEquals(self.sli.count, 2)
69 self.assertEquals(self.sli.error, 1)
70 self.assertEquals(self.sli.counts["red"], 1)
71
42 def test_count_threshold(self):72 def test_count_threshold(self):
43 self.assertEquals(self.sli.count, 0)73 self.assertEquals(self.sli.count, 0)
44 self.assertEquals(self.sli.counts["red"], 0)74 self.assertEquals(self.sli.counts["red"], 0)
@@ -55,7 +85,8 @@
55 self.assertEquals(85 self.assertEquals(
56 [("test.count", 6, 0),86 [("test.count", 6, 0),
57 ("test.count_red", 4, 0),87 ("test.count_red", 4, 0),
58 ("test.count_yellow", 2, 0)],88 ("test.count_yellow", 2, 0),
89 ("test.error", 0, 0)],
59 rows)90 rows)
6091
61 def test_clear(self):92 def test_clear(self):
@@ -67,6 +98,23 @@
67 self.assertEquals(self.sli.count, 1)98 self.assertEquals(self.sli.count, 1)
6899
69100
101class TestMetricLinear(TestCase):
102 def setUp(self):
103 self.sli = SLIMetricReporter('test', {
104 "red": BelowCondition(5, 1),
105 "yellow": BelowCondition(3, 1)})
106
107 def test_count_threshold(self):
108 self.assertEquals(self.sli.count, 0)
109 self.assertEquals(self.sli.counts["red"], 0)
110 self.assertEquals(self.sli.counts["yellow"], 0)
111 for i in range(1, 7):
112 self.sli.update(7, i)
113 self.assertEquals(self.sli.count, 6)
114 self.assertEquals(self.sli.counts["red"], 4)
115 self.assertEquals(self.sli.counts["yellow"], 2)
116
117
70class TestFactory(TestCase):118class TestFactory(TestCase):
71 def test_configure(self):119 def test_configure(self):
72 class TestOptions(service.OptionsGlue):120 class TestOptions(service.OptionsGlue):
@@ -95,3 +143,27 @@
95 rc = smr.conditions["red"]143 rc = smr.conditions["red"]
96 self.assertTrue(isinstance(rc, AboveCondition))144 self.assertTrue(isinstance(rc, AboveCondition))
97 self.assertEquals(rc.value, 4)145 self.assertEquals(rc.value, 4)
146
147 def test_configure_linear(self):
148 class TestOptions(service.OptionsGlue):
149 optParameters = [["test", "t", "default", "help"]]
150 config_section = "statsd"
151
152 o = TestOptions()
153 config_file = ConfigParser.RawConfigParser()
154 config_file.readfp(StringIO("[statsd]\n\n[plugin_sli]\n"
155 "rules = \n"
156 " test => red IF below 5 1\n"
157 " test => green IF above 3 1\n"))
158 o.configure(config_file)
159 smf = SLIMetricFactory()
160 smf.configure(o)
161 smr = smf.build_metric("", "test")
162 rc = smr.conditions["red"]
163 self.assertTrue(isinstance(rc, BelowCondition))
164 self.assertEquals(rc.value, 5)
165 self.assertEquals(rc.slope, 1)
166 rc = smr.conditions["green"]
167 self.assertTrue(isinstance(rc, AboveCondition))
168 self.assertEquals(rc.value, 3)
169 self.assertEquals(rc.slope, 1)
98170
=== modified file 'txstatsd/tests/test_metrics.py'
--- txstatsd/tests/test_metrics.py 2011-11-28 16:24:39 +0000
+++ txstatsd/tests/test_metrics.py 2012-06-18 20:53:18 +0000
@@ -91,6 +91,12 @@
91 self.assertEqual(self.connection.data,91 self.assertEqual(self.connection.data,
92 'txstatsd.tests.users:pepe|pd')92 'txstatsd.tests.users:pepe|pd')
9393
94 def test_generic_extra(self):
95 """Test the GenericMetric class."""
96 self.metrics.report('users', "pepe", "pd", 100)
97 self.assertEqual(self.connection.data,
98 'txstatsd.tests.users:pepe|pd|100')
99
94 def test_empty_namespace(self):100 def test_empty_namespace(self):
95 """Test reporting of an empty namespace."""101 """Test reporting of an empty namespace."""
96 self.metrics.namespace = None102 self.metrics.namespace = None
@@ -118,3 +124,16 @@
118 self.assertEqual(self.connection.data,124 self.assertEqual(self.connection.data,
119 'txstatsd.tests.counter:9|c')125 'txstatsd.tests.counter:9|c')
120126
127 def test_sli(self):
128 """Test SLI call."""
129 self.metrics.sli('users', 100)
130 self.assertEqual(self.connection.data,
131 'txstatsd.tests.users:100|sli')
132
133 self.metrics.sli('users', 200, 2)
134 self.assertEqual(self.connection.data,
135 'txstatsd.tests.users:200|sli|2')
136
137 self.metrics.sli_error('users')
138 self.assertEqual(self.connection.data,
139 'txstatsd.tests.users:error|sli')

Subscribers

People subscribed via source and target branches