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

Proposed by Ricardo Kirkner on 2013-08-30
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 on 2013-08-30
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.
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.rst'
2--- README.rst 2012-06-20 12:58:58 +0000
3+++ README.rst 2013-08-30 11:59:47 +0000
4@@ -1,13 +1,15 @@
5+=============
6 Django Statsd
7 =============
8
9-Integration between statsd and django. It allows you to use different clients,
10-sends timings as middleware and integrates with django debug toolbar.
11-
12-Credits:
13-
14-- jbalogh and jsocol for statsd and commonware, which I just ripped parts out
15- of and put in here.
16-- robhudson for django-debug-toolbar
17-
18-For more see our docs at: http://readthedocs.org/docs/django-statsd/en/latest/
19+Documentation is on `Read the Docs <https://django-statsd.readthedocs.org/>`_.
20+
21+-------
22+License
23+-------
24+
25+BSD and MPL
26+
27+Portions of this are from commonware:
28+
29+https://github.com/jsocol/commonware/blob/master/LICENSE
30
31=== modified file 'django_statsd/__init__.py'
32--- django_statsd/__init__.py 2011-12-18 00:10:49 +0000
33+++ django_statsd/__init__.py 2013-08-30 11:59:47 +0000
34@@ -1,2 +1,4 @@
35 from django_statsd import patches
36 from django_statsd import clients
37+
38+from django_statsd.plugins import NoseStatsd
39
40=== modified file 'django_statsd/clients/log.py'
41--- django_statsd/clients/log.py 2012-07-23 16:37:50 +0000
42+++ django_statsd/clients/log.py 2013-08-30 11:59:47 +0000
43@@ -1,12 +1,12 @@
44 import logging
45
46-from statsd.client import StatsClient
47+from django_statsd.clients.null import StatsClient
48
49 log = logging.getLogger('statsd')
50
51
52 class StatsClient(StatsClient):
53- """A client that pushes things into a local cache."""
54+ """A client that sends messages to the logging framework."""
55
56 def timing(self, stat, delta, rate=1):
57 """Send new timing information. `delta` is in milliseconds."""
58@@ -19,3 +19,7 @@
59 def decr(self, stat, count=1, rate=1):
60 """Decrement a stat by `count`."""
61 log.info('Decrement: %s, %s, %s' % (stat, count, rate))
62+
63+ def gauge(self, stat, value, rate=1):
64+ """Set a gauge value."""
65+ log.info('Gauge: %s, %s, %s' % (stat, value, rate))
66
67=== modified file 'django_statsd/clients/moz_metlog.py'
68--- django_statsd/clients/moz_metlog.py 2012-09-12 17:33:25 +0000
69+++ django_statsd/clients/moz_metlog.py 2013-08-30 11:59:47 +0000
70@@ -1,26 +1,34 @@
71-from statsd.client import StatsClient
72+from django_statsd.clients.null import StatsClient
73 from django.conf import settings
74
75
76 class StatsClient(StatsClient):
77 """A client that pushes messages to metlog """
78
79- def __init__(self, *args, **kw):
80- super(StatsClient, self).__init__(*args, **kw)
81+ def __init__(self, host='localhost', port=8125, prefix=None):
82+ super(StatsClient, self).__init__(host, port, prefix)
83+ if prefix is None:
84+ raise AttributeError(
85+ "Metlog needs settings.STATSD_PREFIX to be defined")
86+
87+ self._prefix = prefix
88 if getattr(settings, 'METLOG', None) is None:
89 raise AttributeError(
90- "Metlog needs to be configured as settings.METLOG")
91+ "Metlog needs to be configured as settings.METLOG")
92
93 self.metlog = settings.METLOG
94
95 def timing(self, stat, delta, rate=1):
96 """Send new timing information. `delta` is in milliseconds."""
97+ stat = '%s.%s' % (self._prefix, stat)
98 self.metlog.timer_send(stat, delta, rate=rate)
99
100 def incr(self, stat, count=1, rate=1):
101 """Increment a stat by `count`."""
102+ stat = '%s.%s' % (self._prefix, stat)
103 self.metlog.incr(stat, count, rate=rate)
104
105 def decr(self, stat, count=1, rate=1):
106 """Decrement a stat by `count`."""
107+ stat = '%s.%s' % (self._prefix, stat)
108 self.metlog.incr(stat, -count, rate=rate)
109
110=== added file 'django_statsd/clients/nose.py'
111--- django_statsd/clients/nose.py 1970-01-01 00:00:00 +0000
112+++ django_statsd/clients/nose.py 2013-08-30 11:59:47 +0000
113@@ -0,0 +1,2 @@
114+# This is just a place holder, the toolbar works well enough for now.
115+from django_statsd.clients.toolbar import StatsClient
116
117=== modified file 'django_statsd/clients/null.py'
118--- django_statsd/clients/null.py 2011-12-14 06:33:09 +0000
119+++ django_statsd/clients/null.py 2013-08-30 11:59:47 +0000
120@@ -4,5 +4,5 @@
121 class StatsClient(StatsClient):
122 """A null client that does nothing."""
123
124- def _send(self, stat, value, rate):
125+ def _after(self, data):
126 pass
127
128=== modified file 'django_statsd/clients/toolbar.py'
129--- django_statsd/clients/toolbar.py 2012-06-10 06:48:33 +0000
130+++ django_statsd/clients/toolbar.py 2013-08-30 11:59:47 +0000
131@@ -1,6 +1,7 @@
132+from collections import defaultdict
133 from time import time
134
135-from statsd.client import StatsClient
136+from django_statsd.clients.null import StatsClient
137
138
139 class StatsClient(StatsClient):
140@@ -11,26 +12,30 @@
141 self.reset()
142
143 def reset(self):
144- self.cache = {}
145+ self.cache = defaultdict(list)
146 self.timings = []
147
148 def timing(self, stat, delta, rate=1):
149 """Send new timing information. `delta` is in milliseconds."""
150 stat = '%s|timing' % stat
151 now = time() * 1000
152- self.timings.append([stat, now, delta, now + delta])
153+ self.timings.append([stat, now - delta, delta, now])
154
155 def incr(self, stat, count=1, rate=1):
156 """Increment a stat by `count`."""
157 stat = '%s|count' % stat
158- self.cache.setdefault(stat, [])
159 self.cache[stat].append([count, rate])
160
161 def decr(self, stat, count=1, rate=1):
162 """Decrement a stat by `count`."""
163 stat = '%s|count' % stat
164- self.cache.setdefault(stat, [])
165 self.cache[stat].append([-count, rate])
166
167- def _send(self, stat, value, rate):
168- pass
169+ def gauge(self, stat, value, rate=1):
170+ """Set a gauge value."""
171+ stat = '%s|gauge' % stat
172+ self.cache[stat] = [[value, rate]]
173+
174+ def set(self, stat, value, rate=1):
175+ stat = '%s|set' % stat
176+ self.cache[stat].append([value, rate])
177
178=== modified file 'django_statsd/middleware.py'
179--- django_statsd/middleware.py 2012-06-20 12:58:58 +0000
180+++ django_statsd/middleware.py 2013-08-30 11:59:47 +0000
181@@ -1,3 +1,4 @@
182+from django.http import Http404
183 from django_statsd.clients import statsd
184 import inspect
185 import time
186@@ -12,9 +13,10 @@
187 return response
188
189 def process_exception(self, request, exception):
190- statsd.incr('response.500')
191- if hasattr(request, 'user') and request.user.is_authenticated():
192- statsd.incr('response.auth.500')
193+ if not isinstance(exception, Http404):
194+ statsd.incr('response.500')
195+ if hasattr(request, 'user') and request.user.is_authenticated():
196+ statsd.incr('response.auth.500')
197
198
199 class GraphiteRequestTimingMiddleware(object):
200@@ -46,3 +48,15 @@
201 statsd.timing('view.{module}.{name}.{method}'.format(**data), ms)
202 statsd.timing('view.{module}.{method}'.format(**data), ms)
203 statsd.timing('view.{method}'.format(**data), ms)
204+
205+
206+class TastyPieRequestTimingMiddleware(GraphiteRequestTimingMiddleware):
207+ """statd's timing specific to Tastypie."""
208+
209+ def process_view(self, request, view_func, view_args, view_kwargs):
210+ try:
211+ request._view_module = view_kwargs['api_name']
212+ request._view_name = view_kwargs['resource_name']
213+ request._start_time = time.time()
214+ except (AttributeError, KeyError):
215+ pass
216
217=== modified file 'django_statsd/panel.py'
218--- django_statsd/panel.py 2012-09-05 01:22:39 +0000
219+++ django_statsd/panel.py 2013-08-30 11:59:47 +0000
220@@ -26,16 +26,23 @@
221 if not stats:
222 return results
223
224- start = stats[0][1]
225- end = max([t[3] for t in stats])
226- length = end - start
227- for stat in stats:
228- results.append([stat[0].split('|')[0],
229+ all_start = stats[0][1]
230+ all_end = max([t[3] for t in stats])
231+ all_duration = all_end - all_start
232+ for stat, start, duration, end in stats:
233+ start_rel = (start - all_start)
234+ start_ratio = (start_rel / float(all_duration))
235+ duration_ratio = (duration / float(all_duration))
236+ try:
237+ duration_ratio_relative = duration_ratio / (1.0 - start_ratio)
238+ except ZeroDivisionError:
239+ duration_ratio_relative = 0
240+ results.append([stat.split('|')[0],
241 # % start from left.
242- ((stat[1] - start - stat[2]) / float(length)) * 100,
243- # % width.
244- max(1, (stat[2] / float(length)) * 100),
245- stat[2],
246+ start_ratio * 100.0,
247+ # % width
248+ duration_ratio_relative * 100.0,
249+ duration,
250 ])
251 return results
252
253
254=== added file 'django_statsd/plugins.py'
255--- django_statsd/plugins.py 1970-01-01 00:00:00 +0000
256+++ django_statsd/plugins.py 2013-08-30 11:59:47 +0000
257@@ -0,0 +1,81 @@
258+import logging
259+import os
260+
261+NOSE = False
262+try:
263+ from nose.plugins.base import Plugin
264+ NOSE = True
265+except ImportError:
266+ class Plugin:
267+ pass
268+
269+from django_statsd.clients import statsd
270+
271+log = logging.getLogger(__name__)
272+
273+
274+class NoseStatsd(Plugin):
275+ name = 'statsd'
276+
277+ def options(self, parse, env=os.environ):
278+ super(NoseStatsd, self).options(parse, env=env)
279+
280+ def configure(self, options, conf):
281+ super(NoseStatsd, self).configure(options, conf)
282+
283+ def report(self, stream):
284+ def write(line):
285+ stream.writeln('%s' % line)
286+
287+ if not hasattr(statsd, 'timings'):
288+ write("Statsd timings not saved, ensure your statsd client is: "
289+ "STATSD_CLIENT = 'django_statsd.clients.nose'")
290+ return
291+
292+ timings = {}
293+ longest = 0
294+ for v in statsd.timings:
295+ k = v[0].split('|')[0]
296+ longest = max(longest, len(k))
297+ timings.setdefault(k, [])
298+ timings[k].append(v[2])
299+
300+ counts = {}
301+ for k, v in statsd.cache.items():
302+ k = k.split('|')[0]
303+ longest = max(longest, len(k))
304+ counts.setdefault(k, [])
305+ [counts[k].append(_v) for _v in v]
306+
307+ header = '%s | Number | Avg (ms) | Total (ms)' % (
308+ 'Statsd Keys'.ljust(longest))
309+ header_len = len(header)
310+
311+ write('')
312+ write('=' * header_len)
313+ write('%s | Number | Avg (ms) | Total (ms)' % (
314+ 'Statsd Keys'.ljust(longest)))
315+ write('-' * header_len)
316+ if not timings:
317+ write('None')
318+
319+ for k in sorted(timings.keys()):
320+ v = timings[k]
321+ write('%s | %s | %s | %s' % (
322+ k.ljust(longest),
323+ str(len(v)).rjust(6),
324+ ('%0.5f' % (sum(v) / float(len(v)))).rjust(10),
325+ ('%0.3f' % sum(v)).rjust(10)))
326+
327+ write('=' * header_len)
328+ write('%s | Number | Total' % ('Statsd Counts'.ljust(longest)))
329+ write('-' * header_len)
330+ if not counts:
331+ write('None')
332+
333+ for k in sorted(counts.keys()):
334+ v = counts[k]
335+ write('%s | %s | %d' % (
336+ k.ljust(longest),
337+ str(len(v)).rjust(6),
338+ sum([x * y for x, y in v])))
339
340=== modified file 'django_statsd/templates/toolbar_statsd/statsd.html'
341--- django_statsd/templates/toolbar_statsd/statsd.html 2012-06-10 06:48:33 +0000
342+++ django_statsd/templates/toolbar_statsd/statsd.html 2013-08-30 11:59:47 +0000
343@@ -17,7 +17,7 @@
344 <td class="timeline">
345 <div class="djDebugTimeline">
346 <div class="djDebugLineChart" style="left: {{ value.1 }}%;">
347- <strong style="width: {{ value.2 }}%; background: grey;">&nbsp;</strong>
348+ <strong style="width: {{ value.2 }}%; min-width: 1px; background: grey;">&nbsp;</strong>
349 </div>
350 </div>
351 </td>
352@@ -57,7 +57,7 @@
353 </table>
354 </section>
355
356-<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" />
357+<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>
358
359 <script type="text/javascript">
360 // TODO: inlining is bad, this should be external.
361@@ -77,9 +77,9 @@
362 }
363 target.html('');
364 $.each(roots, function(root) {
365- var custom = img.replace('graphite', graphite, 'g')
366- .replace('root', roots[root], 'g')
367- .replace('key', that.attr('data-key'), 'g');
368+ var custom = img.replace(/graphite/g, graphite)
369+ .replace(/root/g, roots[root])
370+ .replace(/key/g, that.attr('data-key'));
371 target.append('<p><b>' + roots[root] + '.' + that.attr('data-key') + '</b></p><img src="' + custom + '">');
372 console.log(custom);
373 })
374
375=== modified file 'django_statsd/tests.py'
376--- django_statsd/tests.py 2012-09-12 17:33:25 +0000
377+++ django_statsd/tests.py 2013-08-30 11:59:47 +0000
378@@ -15,6 +15,7 @@
379 },
380 'ROOT_URLCONF': '',
381 'STATSD_CLIENT': 'django_statsd.clients.null',
382+ 'STATSD_PREFIX': None,
383 'METLOG': None
384 }
385
386@@ -138,127 +139,139 @@
387 eq_(client.cache, {'testing|count': [[1, 1]]})
388
389
390-class TestMetlogClient(unittest.TestCase):
391+class TestMetlogClient(TestCase):
392
393 def check_metlog(self):
394 try:
395- from metlog.config import client_from_dict_config
396+ from metlog.config import client_from_dict_config
397 return client_from_dict_config
398 except ImportError:
399 raise SkipTest("Metlog is not installed")
400
401 @nose_tools.raises(AttributeError)
402 def test_no_metlog(self):
403- with mock.patch.object(settings, 'STATSD_CLIENT',
404- 'django_statsd.clients.moz_metlog'):
405+ with self.settings(STATSD_PREFIX='moz_metlog',
406+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
407 get_client()
408
409+ def _create_client(self):
410+ client_from_dict_config = self.check_metlog()
411+
412+ # Need to load within the test in case metlog is not installed
413+ from metlog.config import client_from_dict_config
414+
415+ METLOG_CONF = {
416+ 'logger': 'django-statsd',
417+ 'sender': {
418+ 'class': 'metlog.senders.DebugCaptureSender',
419+ },
420+ }
421+
422+ return client_from_dict_config(METLOG_CONF)
423+
424 def test_get_client(self):
425- client_from_dict_config = self.check_metlog()
426-
427- METLOG_CONF = {
428- 'logger': 'django-statsd',
429- 'sender': {
430- 'class': 'metlog.senders.DebugCaptureSender',
431- },
432- }
433-
434- metlog = client_from_dict_config(METLOG_CONF)
435- with mock.patch.object(settings, 'METLOG', metlog):
436- with mock.patch.object(settings, 'STATSD_CLIENT',
437- 'django_statsd.clients.moz_metlog'):
438-
439- client = get_client()
440- eq_(client.__module__, 'django_statsd.clients.moz_metlog')
441+ metlog = self._create_client()
442+ with self.settings(METLOG=metlog,
443+ STATSD_PREFIX='moz_metlog',
444+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
445+ client = get_client()
446+ eq_(client.__module__, 'django_statsd.clients.moz_metlog')
447
448 def test_metlog_incr(self):
449- client_from_dict_config = self.check_metlog()
450-
451- # Need to load within the test in case metlog is not installed
452- from metlog.config import client_from_dict_config
453- METLOG_CONF = {
454- 'logger': 'django-statsd',
455- 'sender': {
456- 'class': 'metlog.senders.DebugCaptureSender',
457- },
458- }
459-
460- metlog = client_from_dict_config(METLOG_CONF)
461- with mock.patch.object(settings, 'METLOG', metlog):
462- with mock.patch.object(settings, 'STATSD_CLIENT',
463- 'django_statsd.clients.moz_metlog'):
464-
465- client = get_client()
466- eq_(len(client.metlog.sender.msgs), 0)
467- client.incr('testing')
468- eq_(len(client.metlog.sender.msgs), 1)
469-
470- msg = json.loads(client.metlog.sender.msgs[0])
471- eq_(msg['severity'], 6)
472- eq_(msg['payload'], '1')
473- eq_(msg['fields']['rate'], 1)
474- eq_(msg['fields']['name'], 'testing')
475- eq_(msg['type'], 'counter')
476+ metlog = self._create_client()
477+ with self.settings(METLOG=metlog,
478+ STATSD_PREFIX='moz_metlog',
479+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
480+ client = get_client()
481+ eq_(len(client.metlog.sender.msgs), 0)
482+ client.incr('testing')
483+ eq_(len(client.metlog.sender.msgs), 1)
484+
485+ msg = json.loads(client.metlog.sender.msgs[0])
486+ eq_(msg['severity'], 6)
487+ eq_(msg['payload'], '1')
488+ eq_(msg['fields']['rate'], 1)
489+ eq_(msg['fields']['name'], 'moz_metlog.testing')
490+ eq_(msg['type'], 'counter')
491
492 def test_metlog_decr(self):
493- client_from_dict_config = self.check_metlog()
494-
495- # Need to load within the test in case metlog is not installed
496- from metlog.config import client_from_dict_config
497-
498- METLOG_CONF = {
499- 'logger': 'django-statsd',
500- 'sender': {
501- 'class': 'metlog.senders.DebugCaptureSender',
502- },
503- }
504-
505- metlog = client_from_dict_config(METLOG_CONF)
506- with mock.patch.object(settings, 'METLOG', metlog):
507- with mock.patch.object(settings, 'STATSD_CLIENT',
508- 'django_statsd.clients.moz_metlog'):
509-
510- client = get_client()
511- eq_(len(client.metlog.sender.msgs), 0)
512- client.decr('testing')
513- eq_(len(client.metlog.sender.msgs), 1)
514-
515- msg = json.loads(client.metlog.sender.msgs[0])
516- eq_(msg['severity'], 6)
517- eq_(msg['payload'], '-1')
518- eq_(msg['fields']['rate'], 1)
519- eq_(msg['fields']['name'], 'testing')
520- eq_(msg['type'], 'counter')
521+ metlog = self._create_client()
522+ with self.settings(METLOG=metlog,
523+ STATSD_PREFIX='moz_metlog',
524+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
525+ client = get_client()
526+ eq_(len(client.metlog.sender.msgs), 0)
527+ client.decr('testing')
528+ eq_(len(client.metlog.sender.msgs), 1)
529+
530+ msg = json.loads(client.metlog.sender.msgs[0])
531+ eq_(msg['severity'], 6)
532+ eq_(msg['payload'], '-1')
533+ eq_(msg['fields']['rate'], 1)
534+ eq_(msg['fields']['name'], 'moz_metlog.testing')
535+ eq_(msg['type'], 'counter')
536
537 def test_metlog_timing(self):
538- client_from_dict_config = self.check_metlog()
539-
540- # Need to load within the test in case metlog is not installed
541- from metlog.config import client_from_dict_config
542-
543- METLOG_CONF = {
544- 'logger': 'django-statsd',
545- 'sender': {
546- 'class': 'metlog.senders.DebugCaptureSender',
547- },
548- }
549-
550- metlog = client_from_dict_config(METLOG_CONF)
551- with mock.patch.object(settings, 'METLOG', metlog):
552- with mock.patch.object(settings, 'STATSD_CLIENT',
553- 'django_statsd.clients.moz_metlog'):
554-
555- client = get_client()
556- eq_(len(client.metlog.sender.msgs), 0)
557- client.timing('testing', 512, rate=2)
558- eq_(len(client.metlog.sender.msgs), 1)
559-
560- msg = json.loads(client.metlog.sender.msgs[0])
561- eq_(msg['severity'], 6)
562- eq_(msg['payload'], '512')
563- eq_(msg['fields']['rate'], 2)
564- eq_(msg['fields']['name'], 'testing')
565- eq_(msg['type'], 'timer')
566+ metlog = self._create_client()
567+ with self.settings(METLOG=metlog,
568+ STATSD_PREFIX='moz_metlog',
569+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
570+ client = get_client()
571+ eq_(len(client.metlog.sender.msgs), 0)
572+ client.timing('testing', 512, rate=2)
573+ eq_(len(client.metlog.sender.msgs), 1)
574+
575+ msg = json.loads(client.metlog.sender.msgs[0])
576+ eq_(msg['severity'], 6)
577+ eq_(msg['payload'], '512')
578+ eq_(msg['fields']['rate'], 2)
579+ eq_(msg['fields']['name'], 'moz_metlog.testing')
580+ eq_(msg['type'], 'timer')
581+
582+ @nose_tools.raises(AttributeError)
583+ def test_metlog_no_prefixes(self):
584+ metlog = self._create_client()
585+
586+ with self.settings(METLOG=metlog,
587+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
588+ client = get_client()
589+ client.incr('foo', 2)
590+
591+ def test_metlog_prefixes(self):
592+ metlog = self._create_client()
593+
594+ with self.settings(METLOG=metlog,
595+ STATSD_PREFIX='some_prefix',
596+ STATSD_CLIENT='django_statsd.clients.moz_metlog'):
597+ client = get_client()
598+ eq_(len(client.metlog.sender.msgs), 0)
599+
600+ client.timing('testing', 512, rate=2)
601+ client.incr('foo', 2)
602+ client.decr('bar', 5)
603+
604+ eq_(len(client.metlog.sender.msgs), 3)
605+
606+ msg = json.loads(client.metlog.sender.msgs[0])
607+ eq_(msg['severity'], 6)
608+ eq_(msg['payload'], '512')
609+ eq_(msg['fields']['rate'], 2)
610+ eq_(msg['fields']['name'], 'some_prefix.testing')
611+ eq_(msg['type'], 'timer')
612+
613+ msg = json.loads(client.metlog.sender.msgs[1])
614+ eq_(msg['severity'], 6)
615+ eq_(msg['payload'], '2')
616+ eq_(msg['fields']['rate'], 1)
617+ eq_(msg['fields']['name'], 'some_prefix.foo')
618+ eq_(msg['type'], 'counter')
619+
620+ msg = json.loads(client.metlog.sender.msgs[2])
621+ eq_(msg['severity'], 6)
622+ eq_(msg['payload'], '-5')
623+ eq_(msg['fields']['rate'], 1)
624+ eq_(msg['fields']['name'], 'some_prefix.bar')
625+ eq_(msg['type'], 'counter')
626
627
628 # This is primarily for Zamboni, which loads in the custom middleware
629
630=== modified file 'django_statsd/urls.py'
631--- django_statsd/urls.py 2011-12-24 00:52:50 +0000
632+++ django_statsd/urls.py 2013-08-30 11:59:47 +0000
633@@ -1,4 +1,7 @@
634-from django.conf.urls.defaults import patterns, url
635+try:
636+ from django.conf.urls import patterns, url
637+except ImportError: # django < 1.4
638+ from django.conf.urls.defaults import patterns, url
639
640 urlpatterns = patterns('',
641 url('^record$', 'django_statsd.views.record',
642
643=== modified file 'docs/index.rst'
644--- docs/index.rst 2012-09-12 18:17:26 +0000
645+++ docs/index.rst 2013-08-30 11:59:47 +0000
646@@ -16,6 +16,36 @@
647 Changes
648 -------
649
650+0.3.9:
651+
652+- statsd 2.0 support
653+
654+- improved Django debug toolbar support
655+
656+0.3.8.5:
657+
658+- don't count some 404 as 500 and fix deprecation warnings
659+
660+0.3.8.4:
661+
662+- gauge support
663+
664+0.3.8.3:
665+
666+- some bug fixes
667+
668+0.3.8.1:
669+
670+- add in a tasty pie middleware
671+
672+0.3.8:
673+
674+- add in a nose plugin
675+
676+0.3.7:
677+
678+- add in metlog client
679+
680 0.3.6:
681
682 - add in log handler
683@@ -80,6 +110,10 @@
684 using metlog will require you to bind the metlog instance to bind
685 the metlog client instance as settings.METLOG.
686
687+- django_statsd.clients.nose
688+
689+ Route messages through to the nose plugin. This also works with the toolbar
690+ client, so you don't need to change them on -dev.
691
692 Usage
693 -----
694@@ -107,6 +141,10 @@
695 'django_statsd.middleware.GraphiteMiddleware',
696 ) + MIDDLEWARE_CLASSES
697
698+If you are using tastypie, you might enjoy::
699+
700+ 'django_statsd.middleware.TastyPieRequestTimingMiddleware'
701+
702 To get timings for your database or your cache, put in some monkeypatches::
703
704 STATSD_PATCHES = [
705@@ -266,11 +304,31 @@
706 },
707 }
708
709+Nose
710+====
711+
712+There is also a nose plugin. If you use nose, then run tests, you'll get output
713+in your tests. To use run tests with the following::
714+
715+ --with-statsd
716+
717 Contributors
718 ~~~~~~~~~~~~
719
720-* crankycoder: https://github.com/andymckay/django-statsd/pull/13
721-* streeter: https://github.com/andymckay/django-statsd/pull/10
722+* streeter
723+* crankycoder
724+* glogiotatidis
725+* tominsam
726+* youngbob
727+* jsatt
728+* youngbob
729+* jsocol
730+* janfabry
731+* tomchristie
732+
733+See:
734+
735+https://github.com/andymckay/django-statsd/pulls?direction=desc&page=1&sort=created&state=closed
736
737 Indices and tables
738 ==================
739@@ -278,4 +336,3 @@
740 * :ref:`genindex`
741 * :ref:`modindex`
742 * :ref:`search`
743-
744
745=== modified file 'requirements.txt'
746--- requirements.txt 2012-03-07 23:59:37 +0000
747+++ requirements.txt 2013-08-30 11:59:47 +0000
748@@ -1,4 +1,4 @@
749 mock
750 nose
751-statsd
752-django
753+statsd==1.0.0
754+django<1.5
755
756=== modified file 'setup.py'
757--- setup.py 2012-09-13 22:48:06 +0000
758+++ setup.py 2013-08-30 11:59:47 +0000
759@@ -4,13 +4,13 @@
760 setup(
761 # Because django-statsd was taken, I called this django-statsd-mozilla.
762 name='django-statsd-mozilla',
763- version='0.3.7',
764+ version='0.3.9',
765 description='Django interface with statsd',
766 long_description=open('README.rst').read(),
767 author='Andy McKay',
768 author_email='andym@mozilla.com',
769 license='BSD',
770- install_requires=['statsd'],
771+ install_requires=['statsd>=2.0.0'],
772 packages=['django_statsd',
773 'django_statsd/patches',
774 'django_statsd/clients',
775@@ -18,6 +18,11 @@
776 'django_statsd/management',
777 'django_statsd/management/commands'],
778 url='https://github.com/andymckay/django-statsd',
779+ entry_points={
780+ 'nose.plugins.0.10': [
781+ 'django_statsd = django_statsd:NoseStatsd'
782+ ]
783+ },
784 include_package_data=True,
785 zip_safe=False,
786 classifiers=[
787@@ -25,5 +30,5 @@
788 'Natural Language :: English',
789 'Operating System :: OS Independent',
790 'Framework :: Django'
791- ],
792- )
793+ ]
794+)

Subscribers

People subscribed via source and target branches