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