-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implements vanilla StatsD backend #20
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
from __future__ import absolute_import | ||
|
||
import logging | ||
|
||
from statsd import StatsClient | ||
from markus.backends import BackendBase | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class StatsdMetrics(BackendBase): | ||
"""Uses pystatsd client for statsd pings. | ||
|
||
This requires the pystatsd module and requirements to be installed. | ||
To install those bits, do:: | ||
|
||
$ pip install markus[statsd] | ||
|
||
|
||
To use, add this to your backends list:: | ||
|
||
{ | ||
'class': 'markus.backends.statsd.StatsdMetrics', | ||
'options': { | ||
'statsd_host': 'statsd.example.com', | ||
'statsd_port': 8125, | ||
'statsd_prefix': None, | ||
'statsd_maxudpsize': 512, | ||
} | ||
} | ||
|
||
|
||
Options: | ||
|
||
* statsd_host: the hostname for the statsd daemon to connect to | ||
|
||
Defaults to ``'localhost'``. | ||
|
||
* statsd_port: the port for the statsd daemon to connect to | ||
|
||
Defaults to ``8125``. | ||
|
||
* statsd_prefix: the prefix to use for statsd data | ||
|
||
Defaults to ``None``. | ||
|
||
* statsd_maxudpsize: the maximum data to send per packet | ||
|
||
Defaults to ``512``. | ||
|
||
|
||
.. seealso:: | ||
|
||
http://docs.datadoghq.com/guides/metrics/ | ||
|
||
""" | ||
def __init__(self, options): | ||
self.host = options.get('statsd_host', 'localhost') | ||
self.port = options.get('statsd_port', 8125) | ||
self.prefix = options.get('statsd_prefix') | ||
self.maxudpsize = options.get('statsd_maxudpsize', 512) | ||
|
||
self.client = self.get_client( | ||
self.host, self.port, self.prefix, self.maxudpsize) | ||
|
||
logger.info( | ||
'%s configured: %s:%s %s', | ||
self.__class__.__name__, | ||
self.host, | ||
self.port, | ||
self.prefix, | ||
) | ||
|
||
def get_client(self, host, port, prefix, maxudpsize): | ||
return StatsClient( | ||
host=host, port=port, prefix=prefix, maxudpsize=maxudpsize) | ||
|
||
def incr(self, stat, value=1, tags=None): | ||
"""Increment a counter""" | ||
self.client.incr(stat=stat, count=value) | ||
|
||
def gauge(self, stat, value, tags=None): | ||
"""Set a gauge""" | ||
self.client.gauge(stat=stat, value=value) | ||
|
||
def timing(self, stat, value, tags=None): | ||
"""Measure a timing for statistical distribution""" | ||
self.client.timing(stat=stat, delta=value) | ||
|
||
def histogram(self, stat, value, tags=None): | ||
"""Measure a value for statistical distribution""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should mention that it's turning around and using the timing, too. Kind of like how I did this: |
||
self.client.timing(stat=stat, delta=value) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
-e . | ||
|
||
datadog==0.15.0 | ||
statsd==3.2.1 | ||
|
||
freezegun==0.3.8 | ||
pytest==3.0.7 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,7 +54,8 @@ def get_file(fn): | |
'six', | ||
], | ||
extra_requires={ | ||
'datadog': ['datadog'] | ||
'datadog': ['datadog'], | ||
'statsd': ['statd'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be;
|
||
}, | ||
tests_requires=['pytest'], | ||
packages=[ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
import pytest | ||
|
||
from markus.backends import statsd | ||
|
||
|
||
class MockStatsd(object): | ||
def __init__(self, *args, **kwargs): | ||
self.initargs = args | ||
self.initkwargs = kwargs | ||
self.calls = [] | ||
|
||
def incr(self, *args, **kwargs): | ||
self.calls.append( | ||
('incr', args, kwargs), | ||
) | ||
|
||
def gauge(self, *args, **kwargs): | ||
self.calls.append( | ||
('gauge', args, kwargs) | ||
) | ||
|
||
def timing(self, *args, **kwargs): | ||
self.calls.append( | ||
('timing', args, kwargs) | ||
) | ||
|
||
|
||
@pytest.yield_fixture | ||
def mockstatsd(): | ||
"""Mocks Statsd class to capture method call data""" | ||
_old_statsd = statsd.StatsClient | ||
mock = MockStatsd | ||
statsd.StatsClient = mock | ||
yield | ||
statsd.StatsClient = _old_statsd | ||
|
||
|
||
def test_default_options(mockstatsd): | ||
ddm = statsd.StatsdMetrics({}) | ||
|
||
assert ddm.host == 'localhost' | ||
assert ddm.port == 8125 | ||
assert ddm.prefix == None | ||
assert ddm.maxudpsize == 512 | ||
|
||
# NOTE: ddm.client is the mock instance | ||
assert ddm.client.initargs == () | ||
assert ddm.client.initkwargs == {'host': 'localhost', 'port': 8125, 'prefix': None, 'maxudpsize': 512} | ||
|
||
|
||
def test_options(mockstatsd): | ||
ddm = statsd.StatsdMetrics({ | ||
'statsd_host': 'example.com', | ||
'statsd_port': 5000, | ||
'statsd_prefix': 'joe', | ||
'statsd_maxudpsize': 256, | ||
}) | ||
|
||
assert ddm.host == 'example.com' | ||
assert ddm.port == 5000 | ||
assert ddm.prefix == 'joe' | ||
assert ddm.maxudpsize == 256 | ||
|
||
# NOTE: ddm.client is the mock instance | ||
assert ddm.client.initargs == () | ||
assert ddm.client.initkwargs == {'host': 'example.com', 'port': 5000, 'prefix': 'joe', 'maxudpsize': 256} | ||
|
||
|
||
def test_incr(mockstatsd): | ||
ddm = statsd.StatsdMetrics({}) | ||
|
||
ddm.incr('foo', value=10, tags=['key1:val']) | ||
|
||
assert ( | ||
ddm.client.calls == | ||
[('incr', (), {'stat': 'foo', 'count': 10})] | ||
) | ||
|
||
|
||
def test_gauge(mockstatsd): | ||
ddm = statsd.StatsdMetrics({}) | ||
|
||
ddm.gauge('foo', value=100, tags=['key1:val']) | ||
|
||
assert ( | ||
ddm.client.calls == | ||
[('gauge', (), {'stat': 'foo', 'value': 100})] | ||
) | ||
|
||
|
||
def test_timing(mockstatsd): | ||
ddm = statsd.StatsdMetrics({}) | ||
|
||
ddm.timing('foo', value=1234, tags=['key1:val']) | ||
|
||
assert ( | ||
ddm.client.calls == | ||
[('timing', (), {'stat': 'foo', 'delta': 1234})] | ||
) | ||
|
||
|
||
def test_histogram(mockstatsd): | ||
ddm = statsd.StatsdMetrics({}) | ||
|
||
ddm.histogram('foo', value=4321, tags=['key1:val']) | ||
|
||
assert ( | ||
ddm.client.calls == | ||
[('timing', (), {'stat': 'foo', 'delta': 4321})] | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests are great! Having said that, if you have better ideas for how to test backends in Markus, I'm all ears. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seealso should probably be a different url so people who follow it don't get confused by Datadog statsd enhancements.
Maybe this url?: http://statsd.readthedocs.io/en/v3.2.1/configure.html