Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
vllm_cscc
Commits
9ccef8e3
Unverified
Commit
9ccef8e3
authored
Nov 19, 2025
by
Nick Hill
Committed by
GitHub
Nov 19, 2025
Browse files
[Misc] Colorize logs (#29017)
Signed-off-by:
Nick Hill
<
nhill@redhat.com
>
parent
537cc635
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
152 additions
and
62 deletions
+152
-62
tests/test_logger.py
tests/test_logger.py
+51
-43
vllm/envs.py
vllm/envs.py
+9
-0
vllm/logger.py
vllm/logger.py
+35
-16
vllm/logging_utils/__init__.py
vllm/logging_utils/__init__.py
+2
-1
vllm/logging_utils/formatter.py
vllm/logging_utils/formatter.py
+50
-0
vllm/utils/system_utils.py
vllm/utils/system_utils.py
+5
-2
No files found.
tests/test_logger.py
View file @
9ccef8e3
...
...
@@ -49,10 +49,13 @@ def test_trace_function_call():
os
.
remove
(
path
)
def
test_default_vllm_root_logger_configuration
():
def
test_default_vllm_root_logger_configuration
(
monkeypatch
):
"""This test presumes that VLLM_CONFIGURE_LOGGING (default: True) and
VLLM_LOGGING_CONFIG_PATH (default: None) are not configured and default
behavior is activated."""
monkeypatch
.
setenv
(
"VLLM_LOGGING_COLOR"
,
"0"
)
_configure_vllm_root_logger
()
logger
=
logging
.
getLogger
(
"vllm"
)
assert
logger
.
level
==
logging
.
DEBUG
assert
not
logger
.
propagate
...
...
@@ -70,12 +73,13 @@ def test_default_vllm_root_logger_configuration():
assert
formatter
.
datefmt
==
_DATE_FORMAT
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
1
)
@
patch
(
"vllm.logger.VLLM_LOGGING_CONFIG_PATH"
,
None
)
def
test_descendent_loggers_depend_on_and_propagate_logs_to_root_logger
():
def
test_descendent_loggers_depend_on_and_propagate_logs_to_root_logger
(
monkeypatch
):
"""This test presumes that VLLM_CONFIGURE_LOGGING (default: True) and
VLLM_LOGGING_CONFIG_PATH (default: None) are not configured and default
behavior is activated."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"1"
)
monkeypatch
.
delenv
(
"VLLM_LOGGING_CONFIG_PATH"
,
raising
=
False
)
root_logger
=
logging
.
getLogger
(
"vllm"
)
root_handler
=
root_logger
.
handlers
[
0
]
...
...
@@ -99,49 +103,50 @@ def test_descendent_loggers_depend_on_and_propagate_logs_to_root_logger():
assert
log_record
.
levelno
==
logging
.
INFO
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
0
)
@
patch
(
"vllm.logger.VLLM_LOGGING_CONFIG_PATH"
,
None
)
def
test_logger_configuring_can_be_disabled
():
def
test_logger_configuring_can_be_disabled
(
monkeypatch
):
"""This test calls _configure_vllm_root_logger again to test custom logging
config behavior, however mocks are used to ensure no changes in behavior or
configuration occur."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"0"
)
monkeypatch
.
delenv
(
"VLLM_LOGGING_CONFIG_PATH"
,
raising
=
False
)
with
patch
(
"vllm.logger.dictConfig"
)
as
dict_config_mock
:
_configure_vllm_root_logger
()
dict_config_mock
.
assert_not_called
()
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
1
)
@
patch
(
"vllm.logger.VLLM_LOGGING_CONFIG_PATH"
,
"/if/there/is/a/file/here/then/you/did/this/to/yourself.json"
,
)
def
test_an_error_is_raised_when_custom_logging_config_file_does_not_exist
():
def
test_an_error_is_raised_when_custom_logging_config_file_does_not_exist
(
monkeypatch
):
"""This test calls _configure_vllm_root_logger again to test custom logging
config behavior, however it fails before any change in behavior or
configuration occurs."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"1"
)
monkeypatch
.
setenv
(
"VLLM_LOGGING_CONFIG_PATH"
,
"/if/there/is/a/file/here/then/you/did/this/to/yourself.json"
,
)
with
pytest
.
raises
(
RuntimeError
)
as
ex_info
:
_configure_vllm_root_logger
()
assert
ex_info
.
type
==
RuntimeError
# noqa: E721
assert
"File does not exist"
in
str
(
ex_info
)
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
1
)
def
test_an_error_is_raised_when_custom_logging_config_is_invalid_json
():
def
test_an_error_is_raised_when_custom_logging_config_is_invalid_json
(
monkeypatch
):
"""This test calls _configure_vllm_root_logger again to test custom logging
config behavior, however it fails before any change in behavior or
configuration occurs."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"1"
)
with
NamedTemporaryFile
(
encoding
=
"utf-8"
,
mode
=
"w"
)
as
logging_config_file
:
logging_config_file
.
write
(
"---
\n
loggers: []
\n
version: 1"
)
logging_config_file
.
flush
()
with
patch
(
"vllm.logger.
VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
:
monkeypatch
.
setenv
(
"
VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
with
pytest
.
raises
(
JSONDecodeError
)
as
ex_info
:
_configure_vllm_root_logger
()
assert
ex_info
.
type
==
JSONDecodeError
assert
"Expecting value"
in
str
(
ex_info
)
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
1
)
@
pytest
.
mark
.
parametrize
(
"unexpected_config"
,
(
...
...
@@ -151,26 +156,30 @@ def test_an_error_is_raised_when_custom_logging_config_is_invalid_json():
),
)
def
test_an_error_is_raised_when_custom_logging_config_is_unexpected_json
(
monkeypatch
,
unexpected_config
:
Any
,
):
"""This test calls _configure_vllm_root_logger again to test custom logging
config behavior, however it fails before any change in behavior or
configuration occurs."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"1"
)
with
NamedTemporaryFile
(
encoding
=
"utf-8"
,
mode
=
"w"
)
as
logging_config_file
:
logging_config_file
.
write
(
json
.
dumps
(
unexpected_config
))
logging_config_file
.
flush
()
with
patch
(
"vllm.logger.
VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
:
monkeypatch
.
setenv
(
"
VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
with
pytest
.
raises
(
ValueError
)
as
ex_info
:
_configure_vllm_root_logger
()
assert
ex_info
.
type
==
ValueError
# noqa: E721
assert
"Invalid logging config. Expected dict, got"
in
str
(
ex_info
)
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
1
)
def
test_custom_logging_config_is_parsed_and_used_when_provided
():
def
test_custom_logging_config_is_parsed_and_used_when_provided
(
monkeypatch
):
"""This test calls _configure_vllm_root_logger again to test custom logging
config behavior, however mocks are used to ensure no changes in behavior or
configuration occur."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"1"
)
valid_logging_config
=
{
"loggers"
:
{
"vllm.test_logger.logger"
:
{
...
...
@@ -183,19 +192,18 @@ def test_custom_logging_config_is_parsed_and_used_when_provided():
with
NamedTemporaryFile
(
encoding
=
"utf-8"
,
mode
=
"w"
)
as
logging_config_file
:
logging_config_file
.
write
(
json
.
dumps
(
valid_logging_config
))
logging_config_file
.
flush
()
with
(
patch
(
"vllm.logger.VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
),
patch
(
"vllm.logger.dictConfig"
)
as
dict_config_mock
,
):
monkeypatch
.
setenv
(
"VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
with
patch
(
"vllm.logger.dictConfig"
)
as
dict_config_mock
:
_configure_vllm_root_logger
()
dict_config_mock
.
assert_called_with
(
valid_logging_config
)
@
patch
(
"vllm.logger.VLLM_CONFIGURE_LOGGING"
,
0
)
def
test_custom_logging_config_causes_an_error_if_configure_logging_is_off
():
def
test_custom_logging_config_causes_an_error_if_configure_logging_is_off
(
monkeypatch
):
"""This test calls _configure_vllm_root_logger again to test custom logging
config behavior, however mocks are used to ensure no changes in behavior or
configuration occur."""
monkeypatch
.
setenv
(
"VLLM_CONFIGURE_LOGGING"
,
"0"
)
valid_logging_config
=
{
"loggers"
:
{
"vllm.test_logger.logger"
:
{
...
...
@@ -207,7 +215,7 @@ def test_custom_logging_config_causes_an_error_if_configure_logging_is_off():
with
NamedTemporaryFile
(
encoding
=
"utf-8"
,
mode
=
"w"
)
as
logging_config_file
:
logging_config_file
.
write
(
json
.
dumps
(
valid_logging_config
))
logging_config_file
.
flush
()
with
patch
(
"vllm.logger.
VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
:
monkeypatch
.
setenv
(
"
VLLM_LOGGING_CONFIG_PATH"
,
logging_config_file
.
name
)
with
pytest
.
raises
(
RuntimeError
)
as
ex_info
:
_configure_vllm_root_logger
()
assert
ex_info
.
type
is
RuntimeError
...
...
vllm/envs.py
View file @
9ccef8e3
...
...
@@ -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
...
...
vllm/logger.py
View file @
9ccef8e3
...
...
@@ -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
):
...
...
vllm/logging_utils/__init__.py
View file @
9ccef8e3
# 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"
,
]
vllm/logging_utils/formatter.py
View file @
9ccef8e3
...
...
@@ -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
vllm/utils/system_utils.py
View file @
9ccef8e3
...
...
@@ -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,6 +142,9 @@ def set_process_title(
def
_add_prefix
(
file
:
TextIO
,
worker_name
:
str
,
pid
:
int
)
->
None
:
"""Add colored prefix to file output for log decoration."""
if
envs
.
NO_COLOR
:
prefix
=
f
"(
{
worker_name
}
pid=
{
pid
}
) "
else
:
prefix
=
f
"
{
CYAN
}
(
{
worker_name
}
pid=
{
pid
}
)
{
RESET
}
"
file_write
=
file
.
write
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment