diff --git a/docs/source/settings.rst b/docs/source/settings.rst index e1e91fa76..b6741109d 100644 --- a/docs/source/settings.rst +++ b/docs/source/settings.rst @@ -461,6 +461,19 @@ if not provided). .. versionadded:: 19.2 +.. _enable-backlog-metric: + +``enable_backlog_metric`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Command line:** ``--enable-backlog-metric`` + +**Default:** ``False`` + +Enable socket backlog metric (only supported on Linux). + +.. versionadded:: 23.1 + Process Naming -------------- diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index 8537f1e85..373d1b534 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -583,16 +583,15 @@ def manage_workers(self): "value": active_worker_count, "mtype": "gauge"}) - backlog = sum( - sock.get_backlog() or 0 - for sock in self.LISTENERS - ) - - if backlog: - self.log.debug("socket backlog: {0}".format(backlog), - extra={"metric": "gunicorn.backlog", - "value": backlog, - "mtype": "histogram"}) + if self.cfg.enable_backlog_metric: + backlog = sum(sock.get_backlog() or 0 + for sock in self.LISTENERS) + + if backlog >= 0: + self.log.debug("socket backlog: {0}".format(backlog), + extra={"metric": "gunicorn.backlog", + "value": backlog, + "mtype": "histogram"}) def spawn_worker(self): self.worker_age += 1 diff --git a/gunicorn/config.py b/gunicorn/config.py index 402a26b68..df0f3a36c 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -1693,6 +1693,20 @@ class StatsdPrefix(Setting): """ +class BacklogMetric(Setting): + name = "enable_backlog_metric" + section = "Logging" + cli = ["--enable-backlog-metric"] + validator = validate_bool + default = False + action = "store_true" + desc = """\ + Enable socket backlog metric (only supported on Linux). + + .. versionadded:: 23.1 + """ + + class Procname(Setting): name = "proc_name" section = "Process Naming" diff --git a/gunicorn/sock.py b/gunicorn/sock.py index 13eeece71..b7e64deb7 100644 --- a/gunicorn/sock.py +++ b/gunicorn/sock.py @@ -73,7 +73,7 @@ def close(self): self.sock = None def get_backlog(self): - return None + return -1 class TCPSocket(BaseSocket): @@ -93,19 +93,22 @@ def set_options(self, sock, bound=False): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) return super().set_options(sock, bound=bound) - def get_backlog(self): - if self.sock and PLATFORM == "linux": - # tcp_info struct from include/uapi/linux/tcp.h - fmt = 'B' * 8 + 'I' * 24 - try: - tcp_info_struct = self.sock.getsockopt(socket.IPPROTO_TCP, - socket.TCP_INFO, 104) - # 12 is tcpi_unacked - return struct.unpack(fmt, tcp_info_struct)[12] - except AttributeError: - pass - - return None + if PLATFORM == "linux": + def get_backlog(self): + if self.sock: + # tcp_info struct from include/uapi/linux/tcp.h + fmt = 'B' * 8 + 'I' * 24 + try: + tcp_info_struct = self.sock.getsockopt(socket.IPPROTO_TCP, + socket.TCP_INFO, 104) + # 12 is tcpi_unacked + return struct.unpack(fmt, tcp_info_struct)[12] + except AttributeError: + pass + return 0 + else: + def get_backlog(self): + return -1 class TCP6Socket(TCPSocket):