From 5dfeac845dc1d3130b2d4ad8147b706c1f5dd2fe Mon Sep 17 00:00:00 2001 From: GeoffreyWang1117 <173976389+GeoffreyWang1117@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:34:30 -0800 Subject: [PATCH] [Frontend] Add --uvicorn-access-log-exclude-paths option Add a new CLI option to selectively exclude specific paths from uvicorn access logs. This is useful for filtering out noisy endpoints like /health and /metrics that are frequently polled by monitoring systems. Usage example: vllm serve model --uvicorn-access-log-exclude-paths /metrics \ --uvicorn-access-log-exclude-paths /health Fixes #29023 Signed-off-by: GeoffreyWang1117 <173976389+GeoffreyWang1117@users.noreply.github.com> --- vllm/entrypoints/launcher.py | 47 +++++++++++++++++++++++++++ vllm/entrypoints/openai/api_server.py | 1 + vllm/entrypoints/openai/cli_args.py | 4 +++ 3 files changed, 52 insertions(+) diff --git a/vllm/entrypoints/launcher.py b/vllm/entrypoints/launcher.py index cabf95e8d214..16103d79cec1 100644 --- a/vllm/entrypoints/launcher.py +++ b/vllm/entrypoints/launcher.py @@ -2,6 +2,7 @@ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project import asyncio +import logging import signal import socket from http import HTTPStatus @@ -24,6 +25,36 @@ logger = init_logger(__name__) +class UvicornAccessLogFilter(logging.Filter): + """Filter to exclude specific paths from uvicorn access logs. + + This is useful for filtering out noisy endpoints like /health and /metrics + that are frequently polled by monitoring systems. + """ + + def __init__(self, exclude_paths: list[str]) -> None: + super().__init__() + self.exclude_paths = set(exclude_paths) + + def filter(self, record: logging.LogRecord) -> bool: + # Uvicorn access log format: '%(client_addr)s - "%(request_line)s" %(status_code)s' # noqa: E501 + # The request line contains the method, path, and protocol. + # Example: '127.0.0.1:12345 - "GET /metrics HTTP/1.1" 200' + if hasattr(record, "scope"): + path = record.scope.get("path", "") # type: ignore[union-attr] + if path in self.exclude_paths: + return False + # Fallback: check the message for the path + # The path appears after the HTTP method (GET, POST, etc.) + # Pattern: 'METHOD /path ' or 'METHOD /path"' + message = record.getMessage() + for path in self.exclude_paths: + # Match patterns like: "GET /metrics " or "POST /health HTTP" + if f" {path} " in message or f' {path}"' in message: + return False + return True + + async def serve_http( app: FastAPI, sock: socket.socket | None, @@ -51,12 +82,28 @@ async def serve_http( ) h11_max_header_count = uvicorn_kwargs.pop("h11_max_header_count", None) + # Extract access log exclude paths if present + access_log_exclude_paths: list[str] = uvicorn_kwargs.pop( + "access_log_exclude_paths", [] + ) + # Set safe defaults if not provided if h11_max_incomplete_event_size is None: h11_max_incomplete_event_size = H11_MAX_INCOMPLETE_EVENT_SIZE_DEFAULT if h11_max_header_count is None: h11_max_header_count = H11_MAX_HEADER_COUNT_DEFAULT + # Apply access log filter if paths are specified + if access_log_exclude_paths: + uvicorn_access_logger = logging.getLogger("uvicorn.access") + uvicorn_access_logger.addFilter( + UvicornAccessLogFilter(access_log_exclude_paths) + ) + logger.info( + "Uvicorn access log will exclude paths: %s", + ", ".join(access_log_exclude_paths), + ) + config = uvicorn.Config(app, **uvicorn_kwargs) # Set header limits config.h11_max_incomplete_event_size = h11_max_incomplete_event_size diff --git a/vllm/entrypoints/openai/api_server.py b/vllm/entrypoints/openai/api_server.py index a7a2733913b0..8a529b0a7cb9 100644 --- a/vllm/entrypoints/openai/api_server.py +++ b/vllm/entrypoints/openai/api_server.py @@ -2142,6 +2142,7 @@ async def run_server_worker( # NOTE: When the 'disable_uvicorn_access_log' value is True, # no access log will be output. access_log=not args.disable_uvicorn_access_log, + access_log_exclude_paths=args.uvicorn_access_log_exclude_paths, timeout_keep_alive=envs.VLLM_HTTP_TIMEOUT_KEEP_ALIVE, ssl_keyfile=args.ssl_keyfile, ssl_certfile=args.ssl_certfile, diff --git a/vllm/entrypoints/openai/cli_args.py b/vllm/entrypoints/openai/cli_args.py index 946362ce2ef0..edcc3c16a0bf 100644 --- a/vllm/entrypoints/openai/cli_args.py +++ b/vllm/entrypoints/openai/cli_args.py @@ -85,6 +85,10 @@ class FrontendArgs: """Log level for uvicorn.""" disable_uvicorn_access_log: bool = False """Disable uvicorn access log.""" + uvicorn_access_log_exclude_paths: list[str] = field(default_factory=lambda: []) + """Paths to exclude from uvicorn access log. For example, to exclude + /health and /metrics endpoints: --uvicorn-access-log-exclude-paths /health + --uvicorn-access-log-exclude-paths /metrics""" allow_credentials: bool = False """Allow credentials.""" allowed_origins: list[str] = field(default_factory=lambda: ["*"])