From 34b56d5973a97050c6877422620adeecc58ef290 Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Fri, 12 Sep 2014 13:52:15 -0400 Subject: [PATCH 01/10] Add the statsd-prefix config option --- gunicorn/config.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gunicorn/config.py b/gunicorn/config.py index 40d8fbd5..bde18a37 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -1649,3 +1649,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 + """ From eb3bf34066f5edcb22e6fa6ed5712b2af0eb9e53 Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Fri, 12 Sep 2014 13:52:45 -0400 Subject: [PATCH 02/10] Prefix statsd metric strings with the prefix --- gunicorn/instrument/statsd.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index 8c177cab..6ee30400 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -27,6 +27,7 @@ class Statsd(Logger): Logger.__init__(self, cfg) try: host, port = cfg.statsd_host + self.prefix = cfg.statsd_prefix self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.connect((host, int(port))) except Exception: @@ -98,27 +99,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 From 88bc1884073e2f038134bd159e2228e941ba6feb Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Fri, 12 Sep 2014 14:45:29 -0400 Subject: [PATCH 03/10] Add tests for statsd prefix --- tests/test_010-statsd.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_010-statsd.py b/tests/test_010-statsd.py index 340341fc..b4d98ec3 100644 --- a/tests/test_010-statsd.py +++ b/tests/test_010-statsd.py @@ -77,3 +77,12 @@ 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") From 51bd154c99f7c0a354c41d5e2eb39c83f2c48c72 Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Sun, 14 Sep 2014 13:41:52 -0400 Subject: [PATCH 04/10] Fix whitespace --- gunicorn/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gunicorn/config.py b/gunicorn/config.py index bde18a37..1a6a33ea 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -1657,6 +1657,6 @@ class StatsdPrefix(Setting): meta = "STATSD_PREFIX" default = "" validator = validate_string - desc ="""\ + desc = """\ prefix to use when emitting statsd metrics """ From cb92b5bba7cba1d4c51539177ec5be5e5a35fb6c Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Sun, 14 Sep 2014 13:42:18 -0400 Subject: [PATCH 05/10] Move the prefix assignment out of the try block for socket creation --- gunicorn/instrument/statsd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index 6ee30400..f6a2349d 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -25,9 +25,9 @@ class Statsd(Logger): """host, port: statsD server """ Logger.__init__(self, cfg) + self.prefix = cfg.statsd_prefix try: host, port = cfg.statsd_host - self.prefix = cfg.statsd_prefix self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.connect((host, int(port))) except Exception: From a7c6a4fd205e4d4d498413cf7aea3ebcf6faeacc Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Tue, 16 Sep 2014 08:15:58 -0400 Subject: [PATCH 06/10] Update docs to indicate a . is added --- gunicorn/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gunicorn/config.py b/gunicorn/config.py index 1a6a33ea..8f3498d5 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -1658,5 +1658,5 @@ class StatsdPrefix(Setting): default = "" validator = validate_string desc = """\ - prefix to use when emitting statsd metrics + prefix to use when emitting statsd metrics (a trailing . is added, if not provided) """ From c9c543dc82136e06e1ed0de0749fe344fefb561b Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Tue, 16 Sep 2014 08:17:10 -0400 Subject: [PATCH 07/10] Update tests to demonstrate trailing . handling --- tests/test_010-statsd.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_010-statsd.py b/tests/test_010-statsd.py index b4d98ec3..56a295cd 100644 --- a/tests/test_010-statsd.py +++ b/tests/test_010-statsd.py @@ -86,3 +86,30 @@ def test_prefix(): 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") From 770c8219d239e98f212d4d1e84afdfa25d811b48 Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Tue, 16 Sep 2014 08:21:10 -0400 Subject: [PATCH 08/10] Sanitize the statsd prefix --- gunicorn/instrument/statsd.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index f6a2349d..3f6425f8 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -5,6 +5,7 @@ "Bare-bones implementation of statsD's protocol, client-side" +import re import socket import logging from gunicorn.glogging import Logger @@ -25,7 +26,8 @@ class Statsd(Logger): """host, port: statsD server """ Logger.__init__(self, cfg) - self.prefix = cfg.statsd_prefix + prefix_regex = re.compile(r"\.*$") + self.prefix = prefix_regex.sub(".", cfg.statsd_prefix) try: host, port = cfg.statsd_host self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) From 9f4861c4cfc919cffc234cc40044d3bc84e8b086 Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Tue, 16 Sep 2014 08:34:02 -0400 Subject: [PATCH 09/10] Implement statsd prefix sanitization --- gunicorn/instrument/statsd.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index 3f6425f8..fcd16bc2 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -26,8 +26,7 @@ class Statsd(Logger): """host, port: statsD server """ Logger.__init__(self, cfg) - prefix_regex = re.compile(r"\.*$") - self.prefix = prefix_regex.sub(".", cfg.statsd_prefix) + self.prefix = re.sub(r"^(.+[^.]+)\.*$", "\g<1>.", cfg.statsd_prefix) try: host, port = cfg.statsd_host self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) From ed293d30e994d76f33b8c92cd10f549b33b1dade Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Tue, 16 Sep 2014 08:38:05 -0400 Subject: [PATCH 10/10] Don't import all of re --- gunicorn/instrument/statsd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index fcd16bc2..0baf3431 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -5,9 +5,9 @@ "Bare-bones implementation of statsD's protocol, client-side" -import re import socket import logging +from re import sub from gunicorn.glogging import Logger # Instrumentation constants @@ -26,7 +26,7 @@ class Statsd(Logger): """host, port: statsD server """ Logger.__init__(self, cfg) - self.prefix = re.sub(r"^(.+[^.]+)\.*$", "\g<1>.", cfg.statsd_prefix) + 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)