[Misc] Colorize logs (#29017)

Signed-off-by: Nick Hill <nhill@redhat.com>
This commit is contained in:
Nick Hill
2025-11-19 16:26:04 -08:00
committed by GitHub
parent 537cc635c7
commit 9ccef8e333
6 changed files with 152 additions and 62 deletions

View File

@@ -42,6 +42,8 @@ if TYPE_CHECKING:
VLLM_LOGGING_PREFIX: str = ""
VLLM_LOGGING_STREAM: str = "ext://sys.stdout"
VLLM_LOGGING_CONFIG_PATH: str | None = None
VLLM_LOGGING_COLOR: str = "auto"
NO_COLOR: bool = False
VLLM_LOG_STATS_INTERVAL: float = 10.0
VLLM_TRACE_FUNCTION: int = 0
VLLM_ATTENTION_BACKEND: str | None = None
@@ -616,6 +618,11 @@ environment_variables: dict[str, Callable[[], Any]] = {
"VLLM_LOGGING_STREAM": lambda: os.getenv("VLLM_LOGGING_STREAM", "ext://sys.stdout"),
# if set, VLLM_LOGGING_PREFIX will be prepended to all log messages
"VLLM_LOGGING_PREFIX": lambda: os.getenv("VLLM_LOGGING_PREFIX", ""),
# Controls colored logging output. Options: "auto" (default, colors when terminal),
# "1" (always use colors), "0" (never use colors)
"VLLM_LOGGING_COLOR": lambda: os.getenv("VLLM_LOGGING_COLOR", "auto"),
# Standard unix flag for disabling ANSI color codes
"NO_COLOR": lambda: os.getenv("NO_COLOR", "0") != "0",
# If set, vllm will log stats at this interval in seconds
# If not set, vllm will log stats every 10 seconds.
"VLLM_LOG_STATS_INTERVAL": lambda: val
@@ -1578,6 +1585,7 @@ def compile_factors() -> dict[str, object]:
"VLLM_LOGGING_PREFIX",
"VLLM_LOGGING_STREAM",
"VLLM_LOGGING_CONFIG_PATH",
"VLLM_LOGGING_COLOR",
"VLLM_LOG_STATS_INTERVAL",
"VLLM_DEBUG_LOG_API_SERVER_RESPONSE",
"VLLM_TUNED_CONFIG_FOLDER",
@@ -1608,6 +1616,7 @@ def compile_factors() -> dict[str, object]:
"VLLM_TEST_FORCE_LOAD_FORMAT",
"LOCAL_RANK",
"CUDA_VISIBLE_DEVICES",
"NO_COLOR",
}
from vllm.config.utils import normalize_value

View File

@@ -17,18 +17,25 @@ from typing import Any, Literal, cast
import vllm.envs as envs
VLLM_CONFIGURE_LOGGING = envs.VLLM_CONFIGURE_LOGGING
VLLM_LOGGING_CONFIG_PATH = envs.VLLM_LOGGING_CONFIG_PATH
VLLM_LOGGING_LEVEL = envs.VLLM_LOGGING_LEVEL
VLLM_LOGGING_PREFIX = envs.VLLM_LOGGING_PREFIX
VLLM_LOGGING_STREAM = envs.VLLM_LOGGING_STREAM
_FORMAT = (
f"{VLLM_LOGGING_PREFIX}%(levelname)s %(asctime)s "
f"{envs.VLLM_LOGGING_PREFIX}%(levelname)s %(asctime)s "
"[%(fileinfo)s:%(lineno)d] %(message)s"
)
_DATE_FORMAT = "%m-%d %H:%M:%S"
def _use_color() -> bool:
if envs.NO_COLOR or envs.VLLM_LOGGING_COLOR == "0":
return False
if envs.VLLM_LOGGING_COLOR == "1":
return True
if envs.VLLM_LOGGING_STREAM == "ext://sys.stdout": # stdout
return hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
elif envs.VLLM_LOGGING_STREAM == "ext://sys.stderr": # stderr
return hasattr(sys.stderr, "isatty") and sys.stderr.isatty()
return False
DEFAULT_LOGGING_CONFIG = {
"formatters": {
"vllm": {
@@ -36,13 +43,19 @@ DEFAULT_LOGGING_CONFIG = {
"datefmt": _DATE_FORMAT,
"format": _FORMAT,
},
"vllm_color": {
"class": "vllm.logging_utils.ColoredFormatter",
"datefmt": _DATE_FORMAT,
"format": _FORMAT,
},
},
"handlers": {
"vllm": {
"class": "logging.StreamHandler",
"formatter": "vllm",
"level": VLLM_LOGGING_LEVEL,
"stream": VLLM_LOGGING_STREAM,
# Choose formatter based on color setting.
"formatter": "vllm_color" if _use_color() else "vllm",
"level": envs.VLLM_LOGGING_LEVEL,
"stream": envs.VLLM_LOGGING_STREAM,
},
},
"loggers": {
@@ -144,7 +157,7 @@ _METHODS_TO_PATCH = {
def _configure_vllm_root_logger() -> None:
logging_config = dict[str, Any]()
if not VLLM_CONFIGURE_LOGGING and VLLM_LOGGING_CONFIG_PATH:
if not envs.VLLM_CONFIGURE_LOGGING and envs.VLLM_LOGGING_CONFIG_PATH:
raise RuntimeError(
"VLLM_CONFIGURE_LOGGING evaluated to false, but "
"VLLM_LOGGING_CONFIG_PATH was given. VLLM_LOGGING_CONFIG_PATH "
@@ -152,16 +165,22 @@ def _configure_vllm_root_logger() -> None:
"VLLM_CONFIGURE_LOGGING or unset VLLM_LOGGING_CONFIG_PATH."
)
if VLLM_CONFIGURE_LOGGING:
if envs.VLLM_CONFIGURE_LOGGING:
logging_config = DEFAULT_LOGGING_CONFIG
if VLLM_LOGGING_CONFIG_PATH:
if not path.exists(VLLM_LOGGING_CONFIG_PATH):
vllm_handler = logging_config["handlers"]["vllm"]
# Refresh these values in case env vars have changed.
vllm_handler["level"] = envs.VLLM_LOGGING_LEVEL
vllm_handler["stream"] = envs.VLLM_LOGGING_STREAM
vllm_handler["formatter"] = "vllm_color" if _use_color() else "vllm"
if envs.VLLM_LOGGING_CONFIG_PATH:
if not path.exists(envs.VLLM_LOGGING_CONFIG_PATH):
raise RuntimeError(
"Could not load logging config. File does not exist: %s",
VLLM_LOGGING_CONFIG_PATH,
envs.VLLM_LOGGING_CONFIG_PATH,
)
with open(VLLM_LOGGING_CONFIG_PATH, encoding="utf-8") as file:
with open(envs.VLLM_LOGGING_CONFIG_PATH, encoding="utf-8") as file:
custom_config = json.loads(file.read())
if not isinstance(custom_config, dict):

View File

@@ -1,12 +1,13 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from vllm.logging_utils.formatter import NewLineFormatter
from vllm.logging_utils.formatter import ColoredFormatter, NewLineFormatter
from vllm.logging_utils.lazy import lazy
from vllm.logging_utils.log_time import logtime
__all__ = [
"NewLineFormatter",
"ColoredFormatter",
"lazy",
"logtime",
]

View File

@@ -75,3 +75,53 @@ class NewLineFormatter(logging.Formatter):
parts = msg.split(record.message)
msg = msg.replace("\n", "\r\n" + parts[0])
return msg
class ColoredFormatter(NewLineFormatter):
"""Adds ANSI color codes to log levels for terminal output.
This formatter adds colors by injecting them into the format string for
static elements (timestamp, filename, line number) and modifying the
levelname attribute for dynamic color selection.
"""
# ANSI color codes
COLORS = {
"DEBUG": "\033[37m", # White
"INFO": "\033[32m", # Green
"WARNING": "\033[33m", # Yellow
"ERROR": "\033[31m", # Red
"CRITICAL": "\033[35m", # Magenta
}
GREY = "\033[90m" # Grey for timestamp and file info
RESET = "\033[0m"
def __init__(self, fmt, datefmt=None, style="%"):
# Inject grey color codes into format string for timestamp and file info
if fmt:
# Wrap %(asctime)s with grey
fmt = fmt.replace("%(asctime)s", f"{self.GREY}%(asctime)s{self.RESET}")
# Wrap [%(fileinfo)s:%(lineno)d] with grey
fmt = fmt.replace(
"[%(fileinfo)s:%(lineno)d]",
f"{self.GREY}[%(fileinfo)s:%(lineno)d]{self.RESET}",
)
# Call parent __init__ with potentially modified format string
super().__init__(fmt, datefmt, style)
def format(self, record):
# Store original levelname to restore later (in case record is reused)
orig_levelname = record.levelname
# Only modify levelname - it needs dynamic color based on severity
if (color_code := self.COLORS.get(record.levelname)) is not None:
record.levelname = f"{color_code}{record.levelname}{self.RESET}"
# Call parent format which will handle everything else
msg = super().format(record)
# Restore original levelname
record.levelname = orig_levelname
return msg

View File

@@ -22,7 +22,7 @@ from .platform_utils import cuda_is_initialized, xpu_is_initialized
logger = init_logger(__name__)
CYAN = "\033[1;36m"
CYAN = "\033[0;36m"
RESET = "\033[0;0m"
@@ -142,7 +142,10 @@ def set_process_title(
def _add_prefix(file: TextIO, worker_name: str, pid: int) -> None:
"""Add colored prefix to file output for log decoration."""
prefix = f"{CYAN}({worker_name} pid={pid}){RESET} "
if envs.NO_COLOR:
prefix = f"({worker_name} pid={pid}) "
else:
prefix = f"{CYAN}({worker_name} pid={pid}){RESET} "
file_write = file.write
def write_with_prefix(s: str):