diff --git a/gunicorn/config.py b/gunicorn/config.py index bd68aae2..c41add5c 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -1655,3 +1655,14 @@ class StatsdHost(Setting): desc = """\ host:port of the statsd server to log to """ + +class StatsdPrefix(Setting): + name = "statsd_prefix" + section = "Logging" + cli = ["--statsd-prefix"] + meta = "STATSD_PREFIX" + default = "" + validator = validate_string + desc = """\ + prefix to use when emitting statsd metrics (a trailing . is added, if not provided) + """ diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index 8c177cab..0baf3431 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -7,6 +7,7 @@ import socket import logging +from re import sub from gunicorn.glogging import Logger # Instrumentation constants @@ -25,6 +26,7 @@ class Statsd(Logger): """host, port: statsD server """ Logger.__init__(self, cfg) + self.prefix = sub(r"^(.+[^.]+)\.*$", "\g<1>.", cfg.statsd_prefix) try: host, port = cfg.statsd_host self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -98,27 +100,27 @@ class Statsd(Logger): def gauge(self, name, value): try: if self.sock: - self.sock.send("{0}:{1}|g".format(name, value)) + self.sock.send("{0}{1}:{2}|g".format(self.prefix, name, value)) except Exception: pass def increment(self, name, value, sampling_rate=1.0): try: if self.sock: - self.sock.send("{0}:{1}|c|@{2}".format(name, value, sampling_rate)) + self.sock.send("{0}{1}:{2}|c|@{3}".format(self.prefix, name, value, sampling_rate)) except Exception: pass def decrement(self, name, value, sampling_rate=1.0): try: if self.sock: - self.sock.send("{0}:-{1}|c|@{2}".format(name, value, sampling_rate)) + self.sock.send("{0){1}:-{2}|c|@{3}".format(self.prefix, name, value, sampling_rate)) except Exception: pass def histogram(self, name, value): try: if self.sock: - self.sock.send("{0}:{1}|ms".format(name, value)) + self.sock.send("{0}{1}:{2}|ms".format(self.prefix, name, value)) except Exception: pass diff --git a/tests/test_010-statsd.py b/tests/test_010-statsd.py index 340341fc..56a295cd 100644 --- a/tests/test_010-statsd.py +++ b/tests/test_010-statsd.py @@ -77,3 +77,39 @@ def test_instrument(): t.eq(logger.sock.msgs[0], "gunicorn.request.duration:7000.0|ms") t.eq(logger.sock.msgs[1], "gunicorn.requests:1|c|@1.0") t.eq(logger.sock.msgs[2], "gunicorn.request.status.200:1|c|@1.0") + +def test_prefix(): + c = Config() + c.set("statsd_prefix", "test.") + logger = Statsd(c) + logger.sock = MockSocket(False) + + logger.info("Blah", extra={"mtype": "gauge", "metric": "gunicorn.test", "value": 666}) + t.eq(logger.sock.msgs[0], "test.gunicorn.test:666|g") + +def test_prefix_no_dot(): + c = Config() + c.set("statsd_prefix", "test") + logger = Statsd(c) + logger.sock = MockSocket(False) + + logger.info("Blah", extra={"mtype": "gauge", "metric": "gunicorn.test", "value": 666}) + t.eq(logger.sock.msgs[0], "test.gunicorn.test:666|g") + +def test_prefix_multiple_dots(): + c = Config() + c.set("statsd_prefix", "test...") + logger = Statsd(c) + logger.sock = MockSocket(False) + + logger.info("Blah", extra={"mtype": "gauge", "metric": "gunicorn.test", "value": 666}) + t.eq(logger.sock.msgs[0], "test.gunicorn.test:666|g") + +def test_prefix_nested(): + c = Config() + c.set("statsd_prefix", "test.asdf.") + logger = Statsd(c) + logger.sock = MockSocket(False) + + logger.info("Blah", extra={"mtype": "gauge", "metric": "gunicorn.test", "value": 666}) + t.eq(logger.sock.msgs[0], "test.asdf.gunicorn.test:666|g")