From 0440d26d7272b8efc0d54ac01bc7314980e45d67 Mon Sep 17 00:00:00 2001 From: Harshkamdar67 Date: Sat, 27 Jun 2026 15:58:16 +0530 Subject: [PATCH] feat(proxy): add disable_health_check_logs to silence health-check access logs Liveness/readiness probes (/health/liveliness, /health/liveness, /health/readiness) are polled constantly by orchestrators, so their uvicorn access logs drown out real traffic. Setting general_settings.disable_health_check_logs to true installs a uvicorn.access logging filter at proxy startup that drops records for those probe paths while keeping every other request (including /health and /health/readiness/details) logged. The filter lives on the uvicorn.access logger, so it applies in both default and JSON logging modes, and is idempotent across workers and reloads. --- litellm/_logging.py | 43 ++++++++++++++++ litellm/proxy/proxy_server.py | 5 ++ tests/test_litellm/test_logging.py | 81 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/litellm/_logging.py b/litellm/_logging.py index bb743c32878..18b7dc4c455 100644 --- a/litellm/_logging.py +++ b/litellm/_logging.py @@ -429,3 +429,46 @@ def _is_debugging_on() -> bool: Returns True if debugging is on """ return verbose_logger.isEnabledFor(logging.DEBUG) or set_verbose is True + + +_HEALTH_CHECK_PROBE_PATHS = frozenset( + { + "/health/liveliness", + "/health/liveness", + "/health/readiness", + } +) + + +class HealthCheckAccessLogFilter(logging.Filter): + """Drop uvicorn access-log records for health-check probe endpoints. + + Liveness/readiness probes are polled constantly by orchestrators (k8s, load + balancers), drowning out real access logs. uvicorn emits each access record + with args ``(client_addr, method, full_path, http_version, status_code)``, + so the request path is matched to decide what to silence. + """ + + def filter(self, record: logging.LogRecord) -> bool: + args = record.args + if not isinstance(args, tuple) or len(args) < 3: + return True + path = args[2] + if not isinstance(path, str): + return True + normalized = path.split("?", 1)[0].rstrip("/") + return normalized not in _HEALTH_CHECK_PROBE_PATHS + + +def disable_health_check_access_logs() -> None: + """Silence uvicorn access logs for health-check probe endpoints. + + Idempotent so repeated startup hooks (workers, reloads) don't stack filters. + """ + access_logger = logging.getLogger("uvicorn.access") + if any( + isinstance(existing, HealthCheckAccessLogFilter) + for existing in access_logger.filters + ): + return + access_logger.addFilter(HealthCheckAccessLogFilter()) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b49d8ad7965..b769835b792 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -919,6 +919,11 @@ async def proxy_startup_event(app: FastAPI): ) ) + if general_settings.get("disable_health_check_logs") is True: + from litellm._logging import disable_health_check_access_logs + + disable_health_check_access_logs() + ProxyStartupEvent._initialize_startup_logging( llm_router=llm_router, proxy_logging_obj=proxy_logging_obj, diff --git a/tests/test_litellm/test_logging.py b/tests/test_litellm/test_logging.py index fbed044445b..52792d826fe 100644 --- a/tests/test_litellm/test_logging.py +++ b/tests/test_litellm/test_logging.py @@ -328,3 +328,84 @@ async def test_cache_hit_includes_custom_llm_provider(): # Clean up litellm.callbacks = original_callbacks litellm.cache = None + + +def _uvicorn_access_record(path: str) -> logging.LogRecord: + return logging.LogRecord( + name="uvicorn.access", + level=logging.INFO, + pathname=__file__, + lineno=1, + msg='%s - "%s %s HTTP/%s" %d', + args=("127.0.0.1:1234", "GET", path, "1.1", 200), + exc_info=None, + ) + + +@pytest.mark.parametrize( + "path", + [ + "/health/liveliness", + "/health/liveness", + "/health/readiness", + "/health/readiness/", + "/health/readiness?foo=bar", + ], +) +def test_health_check_filter_drops_probe_paths(path): + from litellm._logging import HealthCheckAccessLogFilter + + assert HealthCheckAccessLogFilter().filter(_uvicorn_access_record(path)) is False + + +@pytest.mark.parametrize( + "path", + [ + "/chat/completions", + "/health", + "/health/readiness/details", + "/v1/models", + ], +) +def test_health_check_filter_keeps_other_paths(path): + from litellm._logging import HealthCheckAccessLogFilter + + assert HealthCheckAccessLogFilter().filter(_uvicorn_access_record(path)) is True + + +def test_health_check_filter_ignores_non_access_records(): + from litellm._logging import HealthCheckAccessLogFilter + + record = logging.LogRecord( + name="uvicorn.access", + level=logging.INFO, + pathname=__file__, + lineno=1, + msg="server started", + args=None, + exc_info=None, + ) + assert HealthCheckAccessLogFilter().filter(record) is True + + +def test_disable_health_check_access_logs_is_idempotent_and_active(): + from litellm._logging import ( + HealthCheckAccessLogFilter, + disable_health_check_access_logs, + ) + + access_logger = logging.getLogger("uvicorn.access") + original_filters = list(access_logger.filters) + try: + disable_health_check_access_logs() + disable_health_check_access_logs() + installed = [ + f + for f in access_logger.filters + if isinstance(f, HealthCheckAccessLogFilter) + ] + assert len(installed) == 1 + assert access_logger.filter(_uvicorn_access_record("/health/liveliness")) is False + assert access_logger.filter(_uvicorn_access_record("/chat/completions")) is True + finally: + access_logger.filters = original_filters