mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-24 05:45:11 +00:00
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.
This commit is contained in:
parent
80d3b69d9c
commit
0440d26d72
3 changed files with 129 additions and 0 deletions
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue