Merge lp:~ricardokirkner/django-statsd/stable-update-0.3.9 into lp:~ubuntuone-pqm-team/django-statsd/stable

Proposed by Ricardo Kirkner
Status: Merged
Merged at revision: 72
Proposed branch: lp:~ricardokirkner/django-statsd/stable-update-0.3.9
Merge into: lp:~ubuntuone-pqm-team/django-statsd/stable
Diff against target: 794 lines (+357/-154)
16 files modified
README.rst (+12/-10)
django_statsd/__init__.py (+2/-0)
django_statsd/clients/log.py (+6/-2)
django_statsd/clients/moz_metlog.py (+12/-4)
django_statsd/clients/nose.py (+2/-0)
django_statsd/clients/null.py (+1/-1)
django_statsd/clients/toolbar.py (+12/-7)
django_statsd/middleware.py (+17/-3)
django_statsd/panel.py (+16/-9)
django_statsd/plugins.py (+81/-0)
django_statsd/templates/toolbar_statsd/statsd.html (+5/-5)
django_statsd/tests.py (+116/-103)
django_statsd/urls.py (+4/-1)
docs/index.rst (+60/-3)
requirements.txt (+2/-2)
setup.py (+9/-4)
To merge this branch: bzr merge lp:~ricardokirkner/django-statsd/stable-update-0.3.9
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+183135@code.launchpad.net

Description of the change

Pull latest changes from trunk

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.rst'
--- README.rst 2012-06-20 12:58:58 +0000
+++ README.rst 2013-08-30 11:59:47 +0000
@@ -1,13 +1,15 @@
1=============
1Django Statsd2Django Statsd
2=============3=============
34
4Integration between statsd and django. It allows you to use different clients,5Documentation is on `Read the Docs <https://django-statsd.readthedocs.org/>`_.
5sends timings as middleware and integrates with django debug toolbar.6
67-------
7Credits:8License
89-------
9- jbalogh and jsocol for statsd and commonware, which I just ripped parts out10
10 of and put in here.11BSD and MPL
11- robhudson for django-debug-toolbar12
1213Portions of this are from commonware:
13For more see our docs at: http://readthedocs.org/docs/django-statsd/en/latest/14
15https://github.com/jsocol/commonware/blob/master/LICENSE
1416
=== modified file 'django_statsd/__init__.py'
--- django_statsd/__init__.py 2011-12-18 00:10:49 +0000
+++ django_statsd/__init__.py 2013-08-30 11:59:47 +0000
@@ -1,2 +1,4 @@
1from django_statsd import patches1from django_statsd import patches
2from django_statsd import clients2from django_statsd import clients
3
4from django_statsd.plugins import NoseStatsd
35
=== modified file 'django_statsd/clients/log.py'
--- django_statsd/clients/log.py 2012-07-23 16:37:50 +0000
+++ django_statsd/clients/log.py 2013-08-30 11:59:47 +0000
@@ -1,12 +1,12 @@
1import logging1import logging
22
3from statsd.client import StatsClient3from django_statsd.clients.null import StatsClient
44
5log = logging.getLogger('statsd')5log = logging.getLogger('statsd')
66
77
8class StatsClient(StatsClient):8class StatsClient(StatsClient):
9 """A client that pushes things into a local cache."""9 """A client that sends messages to the logging framework."""
1010
11 def timing(self, stat, delta, rate=1):11 def timing(self, stat, delta, rate=1):
12 """Send new timing information. `delta` is in milliseconds."""12 """Send new timing information. `delta` is in milliseconds."""
@@ -19,3 +19,7 @@
19 def decr(self, stat, count=1, rate=1):19 def decr(self, stat, count=1, rate=1):
20 """Decrement a stat by `count`."""20 """Decrement a stat by `count`."""
21 log.info('Decrement: %s, %s, %s' % (stat, count, rate))21 log.info('Decrement: %s, %s, %s' % (stat, count, rate))
22
23 def gauge(self, stat, value, rate=1):
24 """Set a gauge value."""
25 log.info('Gauge: %s, %s, %s' % (stat, value, rate))
2226
=== modified file 'django_statsd/clients/moz_metlog.py'
--- django_statsd/clients/moz_metlog.py 2012-09-12 17:33:25 +0000
+++ django_statsd/clients/moz_metlog.py 2013-08-30 11:59:47 +0000
@@ -1,26 +1,34 @@
1from statsd.client import StatsClient1from django_statsd.clients.null import StatsClient
2from django.conf import settings2from django.conf import settings
33
44
5class StatsClient(StatsClient):5class StatsClient(StatsClient):
6 """A client that pushes messages to metlog """6 """A client that pushes messages to metlog """
77
8 def __init__(self, *args, **kw):8 def __init__(self, host='localhost', port=8125, prefix=None):
9 super(StatsClient, self).__init__(*args, **kw)9 super(StatsClient, self).__init__(host, port, prefix)
10 if prefix is None:
11 raise AttributeError(
12 "Metlog needs settings.STATSD_PREFIX to be defined")
13
14 self._prefix = prefix
10 if getattr(settings, 'METLOG', None) is None:15 if getattr(settings, 'METLOG', None) is None:
11 raise AttributeError(16 raise AttributeError(
12 "Metlog needs to be configured as settings.METLOG")17 "Metlog needs to be configured as settings.METLOG")
1318
14 self.metlog = settings.METLOG19 self.metlog = settings.METLOG
1520
16 def timing(self, stat, delta, rate=1):21 def timing(self, stat, delta, rate=1):
17 """Send new timing information. `delta` is in milliseconds."""22 """Send new timing information. `delta` is in milliseconds."""
23 stat = '%s.%s' % (self._prefix, stat)
18 self.metlog.timer_send(stat, delta, rate=rate)24 self.metlog.timer_send(stat, delta, rate=rate)
1925
20 def incr(self, stat, count=1, rate=1):26 def incr(self, stat, count=1, rate=1):
21 """Increment a stat by `count`."""27 """Increment a stat by `count`."""
28 stat = '%s.%s' % (self._prefix, stat)
22 self.metlog.incr(stat, count, rate=rate)29 self.metlog.incr(stat, count, rate=rate)
2330
24 def decr(self, stat, count=1, rate=1):31 def decr(self, stat, count=1, rate=1):
25 """Decrement a stat by `count`."""32 """Decrement a stat by `count`."""
33 stat = '%s.%s' % (self._prefix, stat)
26 self.metlog.incr(stat, -count, rate=rate)34 self.metlog.incr(stat, -count, rate=rate)
2735
=== added file 'django_statsd/clients/nose.py'
--- django_statsd/clients/nose.py 1970-01-01 00:00:00 +0000
+++ django_statsd/clients/nose.py 2013-08-30 11:59:47 +0000
@@ -0,0 +1,2 @@
1# This is just a place holder, the toolbar works well enough for now.
2from django_statsd.clients.toolbar import StatsClient
03
=== modified file 'django_statsd/clients/null.py'
--- django_statsd/clients/null.py 2011-12-14 06:33:09 +0000
+++ django_statsd/clients/null.py 2013-08-30 11:59:47 +0000
@@ -4,5 +4,5 @@
4class StatsClient(StatsClient):4class StatsClient(StatsClient):
5 """A null client that does nothing."""5 """A null client that does nothing."""
66
7 def _send(self, stat, value, rate):7 def _after(self, data):
8 pass8 pass
99
=== modified file 'django_statsd/clients/toolbar.py'
--- django_statsd/clients/toolbar.py 2012-06-10 06:48:33 +0000
+++ django_statsd/clients/toolbar.py 2013-08-30 11:59:47 +0000
@@ -1,6 +1,7 @@
1from collections import defaultdict
1from time import time2from time import time
23
3from statsd.client import StatsClient4from django_statsd.clients.null import StatsClient
45
56
6class StatsClient(StatsClient):7class StatsClient(StatsClient):
@@ -11,26 +12,30 @@
11 self.reset()12 self.reset()
1213
13 def reset(self):14 def reset(self):
14 self.cache = {}15 self.cache = defaultdict(list)
15 self.timings = []16 self.timings = []
1617
17 def timing(self, stat, delta, rate=1):18 def timing(self, stat, delta, rate=1):
18 """Send new timing information. `delta` is in milliseconds."""19 """Send new timing information. `delta` is in milliseconds."""
19 stat = '%s|timing' % stat20 stat = '%s|timing' % stat
20 now = time() * 100021 now = time() * 1000
21 self.timings.append([stat, now, delta, now + delta])22 self.timings.append([stat, now - delta, delta, now])
2223
23 def incr(self, stat, count=1, rate=1):24 def incr(self, stat, count=1, rate=1):
24 """Increment a stat by `count`."""25 """Increment a stat by `count`."""
25 stat = '%s|count' % stat26 stat = '%s|count' % stat
26 self.cache.setdefault(stat, [])
27 self.cache[stat].append([count, rate])27 self.cache[stat].append([count, rate])
2828
29 def decr(self, stat, count=1, rate=1):29 def decr(self, stat, count=1, rate=1):
30 """Decrement a stat by `count`."""30 """Decrement a stat by `count`."""
31 stat = '%s|count' % stat31 stat = '%s|count' % stat
32 self.cache.setdefault(stat, [])
33 self.cache[stat].append([-count, rate])32 self.cache[stat].append([-count, rate])
3433
35 def _send(self, stat, value, rate):34 def gauge(self, stat, value, rate=1):
36 pass35 """Set a gauge value."""
36 stat = '%s|gauge' % stat
37 self.cache[stat] = [[value, rate]]
38
39 def set(self, stat, value, rate=1):
40 stat = '%s|set' % stat
41 self.cache[stat].append([value, rate])
3742
=== modified file 'django_statsd/middleware.py'
--- django_statsd/middleware.py 2012-06-20 12:58:58 +0000
+++ django_statsd/middleware.py 2013-08-30 11:59:47 +0000
@@ -1,3 +1,4 @@
1from django.http import Http404
1from django_statsd.clients import statsd2from django_statsd.clients import statsd
2import inspect3import inspect
3import time4import time
@@ -12,9 +13,10 @@
12 return response13 return response
1314
14 def process_exception(self, request, exception):15 def process_exception(self, request, exception):
15 statsd.incr('response.500')16 if not isinstance(exception, Http404):
16 if hasattr(request, 'user') and request.user.is_authenticated():17 statsd.incr('response.500')
17 statsd.incr('response.auth.500')18 if hasattr(request, 'user') and request.user.is_authenticated():
19 statsd.incr('response.auth.500')
1820
1921
20class GraphiteRequestTimingMiddleware(object):22class GraphiteRequestTimingMiddleware(object):
@@ -46,3 +48,15 @@
46 statsd.timing('view.{module}.{name}.{method}'.format(**data), ms)48 statsd.timing('view.{module}.{name}.{method}'.format(**data), ms)
47 statsd.timing('view.{module}.{method}'.format(**data), ms)49 statsd.timing('view.{module}.{method}'.format(**data), ms)
48 statsd.timing('view.{method}'.format(**data), ms)50 statsd.timing('view.{method}'.format(**data), ms)
51
52
53class TastyPieRequestTimingMiddleware(GraphiteRequestTimingMiddleware):
54 """statd's timing specific to Tastypie."""
55
56 def process_view(self, request, view_func, view_args, view_kwargs):
57 try:
58 request._view_module = view_kwargs['api_name']
59 request._view_name = view_kwargs['resource_name']
60 request._start_time = time.time()
61 except (AttributeError, KeyError):
62 pass
4963
=== modified file 'django_statsd/panel.py'
--- django_statsd/panel.py 2012-09-05 01:22:39 +0000
+++ django_statsd/panel.py 2013-08-30 11:59:47 +0000
@@ -26,16 +26,23 @@
26 if not stats:26 if not stats:
27 return results27 return results
2828
29 start = stats[0][1]29 all_start = stats[0][1]
30 end = max([t[3] for t in stats])30 all_end = max([t[3] for t in stats])
31 length = end - start31 all_duration = all_end - all_start
32 for stat in stats:32 for stat, start, duration, end in stats:
33 results.append([stat[0].split('|')[0],33 start_rel = (start - all_start)
34 start_ratio = (start_rel / float(all_duration))
35 duration_ratio = (duration / float(all_duration))
36 try:
37 duration_ratio_relative = duration_ratio / (1.0 - start_ratio)
38 except ZeroDivisionError:
39 duration_ratio_relative = 0
40 results.append([stat.split('|')[0],
34 # % start from left.41 # % start from left.
35 ((stat[1] - start - stat[2]) / float(length)) * 100,42 start_ratio * 100.0,
36 # % width.43 # % width
37 max(1, (stat[2] / float(length)) * 100),44 duration_ratio_relative * 100.0,
38 stat[2],45 duration,
39 ])46 ])
40 return results47 return results
4148
4249
=== added file 'django_statsd/plugins.py'
--- django_statsd/plugins.py 1970-01-01 00:00:00 +0000
+++ django_statsd/plugins.py 2013-08-30 11:59:47 +0000
@@ -0,0 +1,81 @@
1import logging
2import os
3
4NOSE = False
5try:
6 from nose.plugins.base import Plugin
7 NOSE = True
8except ImportError:
9 class Plugin:
10 pass
11
12from django_statsd.clients import statsd
13
14log = logging.getLogger(__name__)
15
16
17class NoseStatsd(Plugin):
18 name = 'statsd'
19
20 def options(self, parse, env=os.environ):
21 super(NoseStatsd, self).options(parse, env=env)
22
23 def configure(self, options, conf):
24 super(NoseStatsd, self).configure(options, conf)
25
26 def report(self, stream):
27 def write(line):
28 stream.writeln('%s' % line)
29
30 if not hasattr(statsd, 'timings'):
31 write("Statsd timings not saved, ensure your statsd client is: "
32 "STATSD_CLIENT = 'django_statsd.clients.nose'")
33 return
34
35 timings = {}
36 longest = 0
37 for v in statsd.timings:
38 k = v[0].split('|')[0]
39 longest = max(longest, len(k))
40 timings.setdefault(k, [])
41 timings[k].append(v[2])
42
43 counts = {}
44 for k, v in statsd.cache.items():
45 k = k.split('|')[0]
46 longest = max(longest, len(k))
47 counts.setdefault(k, [])
48 [counts[k].append(_v) for _v in v]
49
50 header = '%s | Number | Avg (ms) | Total (ms)' % (
51 'Statsd Keys'.ljust(longest))
52 header_len = len(header)
53
54 write('')
55 write('=' * header_len)
56 write('%s | Number | Avg (ms) | Total (ms)' % (
57 'Statsd Keys'.ljust(longest)))
58 write('-' * header_len)
59 if not timings:
60 write('None')
61
62 for k in sorted(timings.keys()):
63 v = timings[k]
64 write('%s | %s | %s | %s' % (
65 k.ljust(longest),
66 str(len(v)).rjust(6),
67 ('%0.5f' % (sum(v) / float(len(v)))).rjust(10),
68 ('%0.3f' % sum(v)).rjust(10)))
69
70 write('=' * header_len)
71 write('%s | Number | Total' % ('Statsd Counts'.ljust(longest)))
72 write('-' * header_len)
73 if not counts:
74 write('None')
75
76 for k in sorted(counts.keys()):
77 v = counts[k]
78 write('%s | %s | %d' % (
79 k.ljust(longest),
80 str(len(v)).rjust(6),
81 sum([x * y for x, y in v])))
082
=== modified file 'django_statsd/templates/toolbar_statsd/statsd.html'
--- django_statsd/templates/toolbar_statsd/statsd.html 2012-06-10 06:48:33 +0000
+++ django_statsd/templates/toolbar_statsd/statsd.html 2013-08-30 11:59:47 +0000
@@ -17,7 +17,7 @@
17 <td class="timeline">17 <td class="timeline">
18 <div class="djDebugTimeline">18 <div class="djDebugTimeline">
19 <div class="djDebugLineChart" style="left: {{ value.1 }}%;">19 <div class="djDebugLineChart" style="left: {{ value.1 }}%;">
20 <strong style="width: {{ value.2 }}%; background: grey;">&nbsp;</strong>20 <strong style="width: {{ value.2 }}%; min-width: 1px; background: grey;">&nbsp;</strong>
21 </div>21 </div>
22 </div>22 </div>
23 </td>23 </td>
@@ -57,7 +57,7 @@
57</table>57</table>
58</section>58</section>
5959
60<div id="graphs" img="graphite?width=586&amp;height=308&amp;target=root.key&amp;target=root.key.lower&amp;target=root.key.mean&amp;target=root.key.upper_90&amp;target=scale(root.key.count,0.1)&amp;from=-24hours&amp;title=24 hours" />60<div id="graphs" img="graphite?width=586&amp;height=308&amp;target=alias(secondYAxis(summarize(root.key.count, '60sec')), 'Count per minute')&amp;target=aliasByMetric(root.key)&amp;target=aliasByMetric(root.key.lower)&amp;target=aliasByMetric(root.key.mean)&amp;target=aliasByMetric(root.key.upper_90)&amp;from=-24hours&amp;title=24 hours"></div>
6161
62<script type="text/javascript">62<script type="text/javascript">
63 // TODO: inlining is bad, this should be external.63 // TODO: inlining is bad, this should be external.
@@ -77,9 +77,9 @@
77 }77 }
78 target.html('');78 target.html('');
79 $.each(roots, function(root) {79 $.each(roots, function(root) {
80 var custom = img.replace('graphite', graphite, 'g')80 var custom = img.replace(/graphite/g, graphite)
81 .replace('root', roots[root], 'g')81 .replace(/root/g, roots[root])
82 .replace('key', that.attr('data-key'), 'g');82 .replace(/key/g, that.attr('data-key'));
83 target.append('<p><b>' + roots[root] + '.' + that.attr('data-key') + '</b></p><img src="' + custom + '">');83 target.append('<p><b>' + roots[root] + '.' + that.attr('data-key') + '</b></p><img src="' + custom + '">');
84 console.log(custom);84 console.log(custom);
85 })85 })
8686
=== modified file 'django_statsd/tests.py'
--- django_statsd/tests.py 2012-09-12 17:33:25 +0000
+++ django_statsd/tests.py 2013-08-30 11:59:47 +0000
@@ -15,6 +15,7 @@
15 },15 },
16 'ROOT_URLCONF': '',16 'ROOT_URLCONF': '',
17 'STATSD_CLIENT': 'django_statsd.clients.null',17 'STATSD_CLIENT': 'django_statsd.clients.null',
18 'STATSD_PREFIX': None,
18 'METLOG': None19 'METLOG': None
19}20}
2021
@@ -138,127 +139,139 @@
138 eq_(client.cache, {'testing|count': [[1, 1]]})139 eq_(client.cache, {'testing|count': [[1, 1]]})
139140
140141
141class TestMetlogClient(unittest.TestCase):142class TestMetlogClient(TestCase):
142143
143 def check_metlog(self):144 def check_metlog(self):
144 try:145 try:
145 from metlog.config import client_from_dict_config146 from metlog.config import client_from_dict_config
146 return client_from_dict_config147 return client_from_dict_config
147 except ImportError:148 except ImportError:
148 raise SkipTest("Metlog is not installed")149 raise SkipTest("Metlog is not installed")
149150
150 @nose_tools.raises(AttributeError)151 @nose_tools.raises(AttributeError)
151 def test_no_metlog(self):152 def test_no_metlog(self):
152 with mock.patch.object(settings, 'STATSD_CLIENT',153 with self.settings(STATSD_PREFIX='moz_metlog',
153 'django_statsd.clients.moz_metlog'):154 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
154 get_client()155 get_client()
155156
157 def _create_client(self):
158 client_from_dict_config = self.check_metlog()
159
160 # Need to load within the test in case metlog is not installed
161 from metlog.config import client_from_dict_config
162
163 METLOG_CONF = {
164 'logger': 'django-statsd',
165 'sender': {
166 'class': 'metlog.senders.DebugCaptureSender',
167 },
168 }
169
170 return client_from_dict_config(METLOG_CONF)
171
156 def test_get_client(self):172 def test_get_client(self):
157 client_from_dict_config = self.check_metlog()173 metlog = self._create_client()
158174 with self.settings(METLOG=metlog,
159 METLOG_CONF = {175 STATSD_PREFIX='moz_metlog',
160 'logger': 'django-statsd',176 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
161 'sender': {177 client = get_client()
162 'class': 'metlog.senders.DebugCaptureSender',178 eq_(client.__module__, 'django_statsd.clients.moz_metlog')
163 },
164 }
165
166 metlog = client_from_dict_config(METLOG_CONF)
167 with mock.patch.object(settings, 'METLOG', metlog):
168 with mock.patch.object(settings, 'STATSD_CLIENT',
169 'django_statsd.clients.moz_metlog'):
170
171 client = get_client()
172 eq_(client.__module__, 'django_statsd.clients.moz_metlog')
173179
174 def test_metlog_incr(self):180 def test_metlog_incr(self):
175 client_from_dict_config = self.check_metlog()181 metlog = self._create_client()
176182 with self.settings(METLOG=metlog,
177 # Need to load within the test in case metlog is not installed183 STATSD_PREFIX='moz_metlog',
178 from metlog.config import client_from_dict_config184 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
179 METLOG_CONF = {185 client = get_client()
180 'logger': 'django-statsd',186 eq_(len(client.metlog.sender.msgs), 0)
181 'sender': {187 client.incr('testing')
182 'class': 'metlog.senders.DebugCaptureSender',188 eq_(len(client.metlog.sender.msgs), 1)
183 },189
184 }190 msg = json.loads(client.metlog.sender.msgs[0])
185191 eq_(msg['severity'], 6)
186 metlog = client_from_dict_config(METLOG_CONF)192 eq_(msg['payload'], '1')
187 with mock.patch.object(settings, 'METLOG', metlog):193 eq_(msg['fields']['rate'], 1)
188 with mock.patch.object(settings, 'STATSD_CLIENT',194 eq_(msg['fields']['name'], 'moz_metlog.testing')
189 'django_statsd.clients.moz_metlog'):195 eq_(msg['type'], 'counter')
190
191 client = get_client()
192 eq_(len(client.metlog.sender.msgs), 0)
193 client.incr('testing')
194 eq_(len(client.metlog.sender.msgs), 1)
195
196 msg = json.loads(client.metlog.sender.msgs[0])
197 eq_(msg['severity'], 6)
198 eq_(msg['payload'], '1')
199 eq_(msg['fields']['rate'], 1)
200 eq_(msg['fields']['name'], 'testing')
201 eq_(msg['type'], 'counter')
202196
203 def test_metlog_decr(self):197 def test_metlog_decr(self):
204 client_from_dict_config = self.check_metlog()198 metlog = self._create_client()
205199 with self.settings(METLOG=metlog,
206 # Need to load within the test in case metlog is not installed200 STATSD_PREFIX='moz_metlog',
207 from metlog.config import client_from_dict_config201 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
208202 client = get_client()
209 METLOG_CONF = {203 eq_(len(client.metlog.sender.msgs), 0)
210 'logger': 'django-statsd',204 client.decr('testing')
211 'sender': {205 eq_(len(client.metlog.sender.msgs), 1)
212 'class': 'metlog.senders.DebugCaptureSender',206
213 },207 msg = json.loads(client.metlog.sender.msgs[0])
214 }208 eq_(msg['severity'], 6)
215209 eq_(msg['payload'], '-1')
216 metlog = client_from_dict_config(METLOG_CONF)210 eq_(msg['fields']['rate'], 1)
217 with mock.patch.object(settings, 'METLOG', metlog):211 eq_(msg['fields']['name'], 'moz_metlog.testing')
218 with mock.patch.object(settings, 'STATSD_CLIENT',212 eq_(msg['type'], 'counter')
219 'django_statsd.clients.moz_metlog'):
220
221 client = get_client()
222 eq_(len(client.metlog.sender.msgs), 0)
223 client.decr('testing')
224 eq_(len(client.metlog.sender.msgs), 1)
225
226 msg = json.loads(client.metlog.sender.msgs[0])
227 eq_(msg['severity'], 6)
228 eq_(msg['payload'], '-1')
229 eq_(msg['fields']['rate'], 1)
230 eq_(msg['fields']['name'], 'testing')
231 eq_(msg['type'], 'counter')
232213
233 def test_metlog_timing(self):214 def test_metlog_timing(self):
234 client_from_dict_config = self.check_metlog()215 metlog = self._create_client()
235216 with self.settings(METLOG=metlog,
236 # Need to load within the test in case metlog is not installed217 STATSD_PREFIX='moz_metlog',
237 from metlog.config import client_from_dict_config218 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
238219 client = get_client()
239 METLOG_CONF = {220 eq_(len(client.metlog.sender.msgs), 0)
240 'logger': 'django-statsd',221 client.timing('testing', 512, rate=2)
241 'sender': {222 eq_(len(client.metlog.sender.msgs), 1)
242 'class': 'metlog.senders.DebugCaptureSender',223
243 },224 msg = json.loads(client.metlog.sender.msgs[0])
244 }225 eq_(msg['severity'], 6)
245226 eq_(msg['payload'], '512')
246 metlog = client_from_dict_config(METLOG_CONF)227 eq_(msg['fields']['rate'], 2)
247 with mock.patch.object(settings, 'METLOG', metlog):228 eq_(msg['fields']['name'], 'moz_metlog.testing')
248 with mock.patch.object(settings, 'STATSD_CLIENT',229 eq_(msg['type'], 'timer')
249 'django_statsd.clients.moz_metlog'):230
250231 @nose_tools.raises(AttributeError)
251 client = get_client()232 def test_metlog_no_prefixes(self):
252 eq_(len(client.metlog.sender.msgs), 0)233 metlog = self._create_client()
253 client.timing('testing', 512, rate=2)234
254 eq_(len(client.metlog.sender.msgs), 1)235 with self.settings(METLOG=metlog,
255236 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
256 msg = json.loads(client.metlog.sender.msgs[0])237 client = get_client()
257 eq_(msg['severity'], 6)238 client.incr('foo', 2)
258 eq_(msg['payload'], '512')239
259 eq_(msg['fields']['rate'], 2)240 def test_metlog_prefixes(self):
260 eq_(msg['fields']['name'], 'testing')241 metlog = self._create_client()
261 eq_(msg['type'], 'timer')242
243 with self.settings(METLOG=metlog,
244 STATSD_PREFIX='some_prefix',
245 STATSD_CLIENT='django_statsd.clients.moz_metlog'):
246 client = get_client()
247 eq_(len(client.metlog.sender.msgs), 0)
248
249 client.timing('testing', 512, rate=2)
250 client.incr('foo', 2)
251 client.decr('bar', 5)
252
253 eq_(len(client.metlog.sender.msgs), 3)
254
255 msg = json.loads(client.metlog.sender.msgs[0])
256 eq_(msg['severity'], 6)
257 eq_(msg['payload'], '512')
258 eq_(msg['fields']['rate'], 2)
259 eq_(msg['fields']['name'], 'some_prefix.testing')
260 eq_(msg['type'], 'timer')
261
262 msg = json.loads(client.metlog.sender.msgs[1])
263 eq_(msg['severity'], 6)
264 eq_(msg['payload'], '2')
265 eq_(msg['fields']['rate'], 1)
266 eq_(msg['fields']['name'], 'some_prefix.foo')
267 eq_(msg['type'], 'counter')
268
269 msg = json.loads(client.metlog.sender.msgs[2])
270 eq_(msg['severity'], 6)
271 eq_(msg['payload'], '-5')
272 eq_(msg['fields']['rate'], 1)
273 eq_(msg['fields']['name'], 'some_prefix.bar')
274 eq_(msg['type'], 'counter')
262275
263276
264# This is primarily for Zamboni, which loads in the custom middleware277# This is primarily for Zamboni, which loads in the custom middleware
265278
=== modified file 'django_statsd/urls.py'
--- django_statsd/urls.py 2011-12-24 00:52:50 +0000
+++ django_statsd/urls.py 2013-08-30 11:59:47 +0000
@@ -1,4 +1,7 @@
1from django.conf.urls.defaults import patterns, url1try:
2 from django.conf.urls import patterns, url
3except ImportError: # django < 1.4
4 from django.conf.urls.defaults import patterns, url
25
3urlpatterns = patterns('',6urlpatterns = patterns('',
4 url('^record$', 'django_statsd.views.record',7 url('^record$', 'django_statsd.views.record',
58
=== modified file 'docs/index.rst'
--- docs/index.rst 2012-09-12 18:17:26 +0000
+++ docs/index.rst 2013-08-30 11:59:47 +0000
@@ -16,6 +16,36 @@
16Changes16Changes
17-------17-------
1818
190.3.9:
20
21- statsd 2.0 support
22
23- improved Django debug toolbar support
24
250.3.8.5:
26
27- don't count some 404 as 500 and fix deprecation warnings
28
290.3.8.4:
30
31- gauge support
32
330.3.8.3:
34
35- some bug fixes
36
370.3.8.1:
38
39- add in a tasty pie middleware
40
410.3.8:
42
43- add in a nose plugin
44
450.3.7:
46
47- add in metlog client
48
190.3.6:490.3.6:
2050
21- add in log handler51- add in log handler
@@ -80,6 +110,10 @@
80 using metlog will require you to bind the metlog instance to bind110 using metlog will require you to bind the metlog instance to bind
81 the metlog client instance as settings.METLOG.111 the metlog client instance as settings.METLOG.
82112
113- django_statsd.clients.nose
114
115 Route messages through to the nose plugin. This also works with the toolbar
116 client, so you don't need to change them on -dev.
83117
84Usage118Usage
85-----119-----
@@ -107,6 +141,10 @@
107 'django_statsd.middleware.GraphiteMiddleware',141 'django_statsd.middleware.GraphiteMiddleware',
108 ) + MIDDLEWARE_CLASSES142 ) + MIDDLEWARE_CLASSES
109143
144If you are using tastypie, you might enjoy::
145
146 'django_statsd.middleware.TastyPieRequestTimingMiddleware'
147
110To get timings for your database or your cache, put in some monkeypatches::148To get timings for your database or your cache, put in some monkeypatches::
111149
112 STATSD_PATCHES = [150 STATSD_PATCHES = [
@@ -266,11 +304,31 @@
266 },304 },
267 }305 }
268306
307Nose
308====
309
310There is also a nose plugin. If you use nose, then run tests, you'll get output
311in your tests. To use run tests with the following::
312
313 --with-statsd
314
269Contributors315Contributors
270~~~~~~~~~~~~316~~~~~~~~~~~~
271317
272* crankycoder: https://github.com/andymckay/django-statsd/pull/13318* streeter
273* streeter: https://github.com/andymckay/django-statsd/pull/10319* crankycoder
320* glogiotatidis
321* tominsam
322* youngbob
323* jsatt
324* youngbob
325* jsocol
326* janfabry
327* tomchristie
328
329See:
330
331https://github.com/andymckay/django-statsd/pulls?direction=desc&page=1&sort=created&state=closed
274332
275Indices and tables333Indices and tables
276==================334==================
@@ -278,4 +336,3 @@
278* :ref:`genindex`336* :ref:`genindex`
279* :ref:`modindex`337* :ref:`modindex`
280* :ref:`search`338* :ref:`search`
281
282339
=== modified file 'requirements.txt'
--- requirements.txt 2012-03-07 23:59:37 +0000
+++ requirements.txt 2013-08-30 11:59:47 +0000
@@ -1,4 +1,4 @@
1mock1mock
2nose2nose
3statsd3statsd==1.0.0
4django4django<1.5
55
=== modified file 'setup.py'
--- setup.py 2012-09-13 22:48:06 +0000
+++ setup.py 2013-08-30 11:59:47 +0000
@@ -4,13 +4,13 @@
4setup(4setup(
5 # Because django-statsd was taken, I called this django-statsd-mozilla.5 # Because django-statsd was taken, I called this django-statsd-mozilla.
6 name='django-statsd-mozilla',6 name='django-statsd-mozilla',
7 version='0.3.7',7 version='0.3.9',
8 description='Django interface with statsd',8 description='Django interface with statsd',
9 long_description=open('README.rst').read(),9 long_description=open('README.rst').read(),
10 author='Andy McKay',10 author='Andy McKay',
11 author_email='andym@mozilla.com',11 author_email='andym@mozilla.com',
12 license='BSD',12 license='BSD',
13 install_requires=['statsd'],13 install_requires=['statsd>=2.0.0'],
14 packages=['django_statsd',14 packages=['django_statsd',
15 'django_statsd/patches',15 'django_statsd/patches',
16 'django_statsd/clients',16 'django_statsd/clients',
@@ -18,6 +18,11 @@
18 'django_statsd/management',18 'django_statsd/management',
19 'django_statsd/management/commands'],19 'django_statsd/management/commands'],
20 url='https://github.com/andymckay/django-statsd',20 url='https://github.com/andymckay/django-statsd',
21 entry_points={
22 'nose.plugins.0.10': [
23 'django_statsd = django_statsd:NoseStatsd'
24 ]
25 },
21 include_package_data=True,26 include_package_data=True,
22 zip_safe=False,27 zip_safe=False,
23 classifiers=[28 classifiers=[
@@ -25,5 +30,5 @@
25 'Natural Language :: English',30 'Natural Language :: English',
26 'Operating System :: OS Independent',31 'Operating System :: OS Independent',
27 'Framework :: Django'32 'Framework :: Django'
28 ],33 ]
29 )34)

Subscribers

People subscribed via source and target branches