diff --git a/docs/source/settings.rst b/docs/source/settings.rst index e759ded7..a3cb12dd 100644 --- a/docs/source/settings.rst +++ b/docs/source/settings.rst @@ -359,21 +359,15 @@ statsd_host * ``--statsd-host STATSD_ADDR`` * ``None`` -``host:port`` of the statsd server to log to. +The address of the StatsD server to log to. + +Address is a string of the form: + +* ``unix://PATH`` : for a unix domain socket. +* ``HOST:PORT`` : for a network address .. versionadded:: 19.1 -.. _statsd-socket: - -statsd_socket -~~~~~~~~~~~~~ - -* ``--statsd-socket STATSD_SOCKET`` -* ``None`` - -Unix domain socket of the statsd server to log to. -Supersedes ``statsd_host`` if provided. - .. _dogstatsd-tags: dogstatsd_tags diff --git a/gunicorn/config.py b/gunicorn/config.py index 95b615d4..99b4ba31 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -144,11 +144,7 @@ class Config(object): # if default logger is in use, and statsd is on, automagically switch # to the statsd logger if uri == LoggerClass.default: - statsd_address = 'statsd_socket' in self.settings and \ - self.settings['statsd_socket'].value or \ - 'statsd_host' in self.settings and \ - self.settings['statsd_host'].value - if statsd_address is not None: + if 'statsd_host' in self.settings and self.settings['statsd_host'].value is not None: uri = "gunicorn.instrument.statsd.Statsd" logger_class = util.load_class( @@ -499,15 +495,17 @@ def validate_chdir(val): return path -def validate_hostport(val): +def validate_address(val): val = validate_string(val) if val is None: return None - elements = val.split(":") - if len(elements) == 2: - return (elements[0], int(elements[1])) - else: - raise TypeError("Value must consist of: hostname:port") + + try: + address = util.parse_address(val, default_port='8125') + except RuntimeError: + raise TypeError("Value must be one of ('host:port', 'unix://PATH')") + + return address def validate_reload_engine(val): @@ -1475,25 +1473,18 @@ class StatsdHost(Setting): cli = ["--statsd-host"] meta = "STATSD_ADDR" default = None - validator = validate_hostport + validator = validate_address desc = """\ - ``host:port`` of the statsd server to log to. + The address of the StatsD server to log to. + + Address is a string of the form: + + * ``unix://PATH`` : for a unix domain socket. + * ``HOST:PORT`` : for a network address .. versionadded:: 19.1 """ -class StatsdSocket(Setting): - name = "statsd_socket" - section = "Logging" - cli = ["--statsd-socket"] - meta = "STATSD_SOCKET" - default = None - validator = validate_string - desc = """\ - Unix domain socket of the statsd server to log to. - Supersedes ``statsd_host`` if provided. - """ - # Datadog Statsd (dogstatsd) tags. https://docs.datadoghq.com/developers/dogstatsd/ class DogstatsdTags(Setting): name = "dogstatsd_tags" diff --git a/gunicorn/instrument/statsd.py b/gunicorn/instrument/statsd.py index ee6d5308..143d0bbc 100644 --- a/gunicorn/instrument/statsd.py +++ b/gunicorn/instrument/statsd.py @@ -23,22 +23,17 @@ class Statsd(Logger): """statsD-based instrumentation, that passes as a logger """ def __init__(self, cfg): - """host, port: statsD server - """ Logger.__init__(self, cfg) self.prefix = sub(r"^(.+[^.]+)\.*$", "\\g<1>.", cfg.statsd_prefix) - if cfg.statsd_socket: + if isinstance(cfg.statsd_host, str): address_family = socket.AF_UNIX - address = cfg.statsd_socket - elif cfg.statsd_host: + else: address_family = socket.AF_INET - host, port = cfg.statsd_host - address = (host, int(port)) try: self.sock = socket.socket(address_family, socket.SOCK_DGRAM) - self.sock.connect(address) + self.sock.connect(cfg.statsd_host) except Exception: self.sock = None diff --git a/tests/test_config.py b/tests/test_config.py index db108b9b..db33cb3f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -306,20 +306,26 @@ def test_nworkers_changed(): assert c.nworkers_changed(1, 2, 3) == 3 -def test_statsd_host_changes_logger(): +def test_statsd_host(): + c = config.Config() + assert c.statsd_host is None + c.set("statsd_host", "localhost") + assert c.statsd_host == ("localhost", 8125) + c.set("statsd_host", "statsd:7777") + assert c.statsd_host == ("statsd", 7777) + c.set("statsd_host", "unix:/path/to.sock") + assert c.statsd_host == "/path/to.sock" + pytest.raises(TypeError, c.set, "statsd_host", 666) + pytest.raises(TypeError, c.set, "statsd_host", "host:string") + + +def test_statsd_changes_logger(): c = config.Config() assert c.logger_class == glogging.Logger c.set('statsd_host', 'localhost:12345') assert c.logger_class == statsd.Statsd -def test_statsd_socket_changes_logger(): - c = config.Config() - assert c.logger_class == glogging.Logger - c.set('statsd_socket', '/var/run/sock') - assert c.logger_class == statsd.Statsd - - class MyLogger(glogging.Logger): # dummy custom logger class for testing pass diff --git a/tests/test_statsd.py b/tests/test_statsd.py index 06c1d964..6f7bf426 100644 --- a/tests/test_statsd.py +++ b/tests/test_statsd.py @@ -59,6 +59,18 @@ def test_statsd_fail(): logger.exception("No impact on logging") +def test_statsd_host_initialization(): + c = Config() + c.set('statsd_host', 'unix:test.sock') + logger = Statsd(c) + logger.info("Can be initialized and used with a UDS socket") + + # Can be initialized and used with a UDP address + c.set('statsd_host', 'host:8080') + logger = Statsd(c) + logger.info("Can be initialized and used with a UDP socket") + + def test_dogstatsd_tags(): c = Config() tags = 'yucatan,libertine:rhubarb'