Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:jsocol/pystatsd

  • Loading branch information...
commit eb7ad475b42440144449ede1861ebba0059c0158 2 parents d87ede8 + 55e495b
James Socol authored
View
4 docs/conf.py
@@ -48,9 +48,9 @@
# built documents.
#
# The short X.Y version.
-version = '0.5.0'
+version = '1.0'
# The full version, including alpha/beta/rc tags.
-release = '0.5.0'
+release = '1.0.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
View
46 docs/index.rst
@@ -6,7 +6,45 @@
Welcome to Python StatsD's documentation!
=========================================
-Contents:
+statsd_ is a friendly front-end to Graphite_. This is a Python client for the
+statsd daemon.
+
+Quickly, to use::
+
+ >>> import statsd
+ >>> c = statsd.StatsClient('localhost', 8125)
+ >>> c.incr('foo') # Increment the 'foo' counter.
+ >>> c.timing('stats.timed', 320) # Record a 320ms 'stats.timed'.
+
+You can also add a prefix to all your stats::
+
+ >>> import statsd
+ >>> c = statsd.StatsClient('localhost', 8125, prefix='foo')
+ >>> c.incr('bar') # Will be 'foo.bar' in statsd/graphite.
+
+
+Installing
+----------
+
+The easiest way to install statsd is with pip!
+
+You can install from PyPI::
+
+ $ pip install statsd
+
+Or GitHub::
+
+ $ pip install -e git+https://github.com/jsocol/pystatsd#egg=statsd
+
+Or from source::
+
+ $ git clone https://github.com/jsocol/pystatsd
+ $ cd statsd
+ $ python setup.py install
+
+
+Contents
+--------
.. toctree::
:maxdepth: 2
@@ -18,9 +56,9 @@ Contents:
Indices and tables
-==================
+------------------
-* :ref:`genindex`
-* :ref:`modindex`
* :ref:`search`
+.. _statsd: https://github.com/etsy/statsd
+.. _Graphite: http://graphite.wikidot.com/
View
4 setup.py
@@ -2,7 +2,7 @@
setup(
name='statsd',
- version='0.5.1',
+ version='1.0.0',
description='A simple statsd client.',
long_description=open('README.rst').read(),
author='James Socol',
@@ -13,7 +13,7 @@
include_package_data=True,
package_data={'': ['README.rst']},
classifiers=[
- 'Development Status :: 4 - Beta',
+ 'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
View
2  statsd/__init__.py
@@ -12,7 +12,7 @@
__all__ = ['StatsClient', 'statsd']
-VERSION = (0, 5, 1)
+VERSION = (1, 0, 0)
__version__ = '.'.join(map(str, VERSION))
View
25 statsd/client.py
@@ -34,11 +34,13 @@ def __exit__(self, typ, value, tb):
class StatsClient(object):
"""A client for statsd."""
- def __init__(self, host='localhost', port=8125, prefix=None):
+ def __init__(self, host='localhost', port=8125, prefix=None, batch_len=1):
"""Create a new client."""
self._addr = (socket.gethostbyname(host), port)
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._prefix = prefix
+ self._batch_len = batch_len
+ self._stats = []
def timer(self, stat, rate=1):
return _Timer(self, stat, rate)
@@ -59,6 +61,17 @@ def gauge(self, stat, value, rate=1):
"""Set a gauge value."""
self._send(stat, '%s|g' % value, rate)
+ def flush(self):
+ """Flush the stats batching buffer."""
+ if (0 < len(self._stats)):
+ data = '\n'.join(self._stats)
+ self._stats = []
+ try:
+ self._sock.sendto(data.encode('ascii'), self._addr)
+ except socket.error:
+ # No time for love, Dr. Jones!
+ pass
+
def _send(self, stat, value, rate=1):
"""Send data to statsd."""
if rate < 1:
@@ -70,9 +83,7 @@ def _send(self, stat, value, rate=1):
if self._prefix:
stat = '%s.%s' % (self._prefix, stat)
- try:
- txt = '%s:%s' % (stat, value)
- self._sock.sendto(txt.encode('ascii'), self._addr)
- except socket.error:
- # No time for love, Dr. Jones!
- pass
+ txt = '%s:%s' % (stat, value)
+ self._stats.append(txt)
+ if self._batch_len <= len(self._stats):
+ self.flush()
View
35 statsd/tests.py
@@ -12,17 +12,19 @@
ADDR = (socket.gethostbyname('localhost'), 8125)
-def _client(prefix=None):
- sc = StatsClient(host=ADDR[0], port=ADDR[1], prefix=prefix)
+def _client(prefix=None, batch_len=1):
+ sc = StatsClient(host=ADDR[0], port=ADDR[1], prefix=prefix, batch_len=batch_len)
sc._sock = mock.Mock()
return sc
def _sock_check(cl, count, val):
- val = val.encode('ascii')
eq_(cl._sock.sendto.call_count, count)
- eq_(cl._sock.sendto.call_args, ((val, ADDR), {}))
-
+ if val:
+ val = val.encode('ascii')
+ eq_(cl._sock.sendto.call_args, ((val, ADDR), {}))
+ else:
+ eq_(cl._sock.sendto.call_args, None)
@mock.patch.object(random, 'random', lambda: -1)
def test_incr():
@@ -85,6 +87,29 @@ def test_timing():
_sock_check(sc, 3, 'foo:100|ms|@0.5')
+@mock.patch.object(random, 'random', lambda: -1)
+def test_batch():
+ sc = _client(None, 2)
+
+ sc.incr('foo')
+ _sock_check(sc, 0, '')
+
+ sc.incr('bar')
+ _sock_check(sc, 1, 'foo:1|c\nbar:1|c')
+
+@mock.patch.object(random, 'random', lambda: -1)
+def test_batch_flush():
+ sc = _client(None, 10)
+
+ sc.incr('foo')
+ _sock_check(sc, 0, '')
+
+ sc.incr('bar')
+ _sock_check(sc, 0, '')
+
+ sc.flush()
+ _sock_check(sc, 1, 'foo:1|c\nbar:1|c')
+
def test_prefix():
sc = _client('foo')
Please sign in to comment.
Something went wrong with that request. Please try again.