Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions examples/features/logging_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ For `vllm serve`, configure logging with CLI arguments:
## CLI logging configuration

`--logging-config` accepts a JSON object. Its fields are `log_level`,
`configure_logging`, and `pylogging_config_file`. For example:
`log_stream`, `log_color`, `configure_logging`, and `pylogging_config_file`.
For example:

```bash
vllm serve mistralai/Mistral-7B-v0.1 \
Expand All @@ -33,6 +34,16 @@ precedence if both are supplied. It sets the level of vLLM's built-in logging
configuration. `--log-config-file` is deprecated and will be removed in
v0.33.0; use `--logging-config.pylogging_config_file` in new commands.

`log_stream` selects the stream of the built-in handler, for example
`ext://sys.stderr`. `log_color` is one of `auto`, `always`, or `never`; `auto`
colors output only when `log_stream` is a terminal:

```bash
vllm serve mistralai/Mistral-7B-v0.1 \
--logging-config.log_stream ext://sys.stderr \
--logging-config.log_color never
```

If `configure_logging` is `false`, vLLM does not apply a logging
configuration. It cannot be combined with `pylogging_config_file`. This has
the same effect as the legacy `VLLM_CONFIGURE_LOGGING=0` setting.
Expand All @@ -44,18 +55,21 @@ schema](https://docs.python.org/3/library/logging.config.html#dictionary-schema-
!!! note "Custom configurations override `--log-level`"
When `pylogging_config_file` is set, vLLM loads that JSON file and replaces
its built-in `dictConfig`; it does not merge the two. Therefore,
`--log-level` applies only when no custom configuration file is provided.
`--log-level`, `log_stream`, and `log_color` apply only when no custom
configuration file is provided.
Set logger and handler levels in the custom file itself. vLLM applies the
resolved configuration in its child processes as well.

## Environment variables

The CLI configuration is recommended for `vllm serve`. The legacy
`VLLM_CONFIGURE_LOGGING`, `VLLM_LOGGING_LEVEL`, and
`VLLM_LOGGING_CONFIG_PATH` variables remain supported as defaults. The default
handler's stream, prefix, and color are currently controlled only through
environment variables. Values from a YAML `--config` file or the command line
override those defaults. See [Environment Variables](https://docs.vllm.ai/en/latest/configuration/env_vars/)
`VLLM_CONFIGURE_LOGGING`, `VLLM_LOGGING_LEVEL`, `VLLM_LOGGING_CONFIG_PATH`, and
`VLLM_LOGGING_STREAM` variables remain supported as defaults. `log_color`
defaults to `never` if `NO_COLOR` is set or `VLLM_LOGGING_COLOR=0`, and to
`always` if `VLLM_LOGGING_COLOR=1` or `FORCE_COLOR` is set. The default
handler's prefix is currently controlled only through `VLLM_LOGGING_PREFIX`.
Values from a YAML `--config` file or the command line override those
defaults. See [Environment Variables](https://docs.vllm.ai/en/latest/configuration/env_vars/)
for those settings.

## Examples
Expand Down
15 changes: 15 additions & 0 deletions tests/entrypoints/launchers/test_cli_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ def test_logging_config_cli_args(serve_parser):
assert config.pylogging_config_file == "/tmp/flat.json"


def test_logging_config_color_and_stream_cli_args(serve_parser):
args = serve_parser.parse_args(
[
"--logging-config.log_color",
"never",
"--logging-config.log_stream",
"ext://sys.stderr",
]
)

config = AsyncEngineArgs.from_cli_args(args).create_logging_config()
assert config.log_color == "never"
assert config.log_stream == "ext://sys.stderr"


### Tests for LoRA module parsing
def test_valid_key_value_format(serve_parser):
# Test old format: name=path
Expand Down
78 changes: 65 additions & 13 deletions tests/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
enable_trace_function_call,
init_logger,
)
from vllm.logging_utils import NewLineFormatter
from vllm.logging_utils import ColoredFormatter, NewLineFormatter
from vllm.logging_utils.dump_input import prepare_object_to_dump


Expand Down Expand Up @@ -110,25 +110,77 @@ def log_args(_):
llm_module.LLM(model="facebook/opt-125m")


def test_use_color_force_color(monkeypatch):
"""FORCE_COLOR forces colored logs without a TTY, while NO_COLOR and an
explicit VLLM_LOGGING_COLOR=0 take precedence over it."""
monkeypatch.setattr(sys, "stdout", io.StringIO())
monkeypatch.setattr(sys, "stderr", io.StringIO())
@pytest.mark.parametrize(
("env", "expected"),
[
({}, "auto"),
({"FORCE_COLOR": "1"}, "always"),
({"VLLM_LOGGING_COLOR": "1"}, "always"),
({"FORCE_COLOR": "1", "VLLM_LOGGING_COLOR": "0"}, "never"),
({"FORCE_COLOR": "1", "NO_COLOR": "1"}, "never"),
],
)
def test_log_color_defaults_from_env(monkeypatch, env, expected):
"""NO_COLOR and VLLM_LOGGING_COLOR=0 take precedence over FORCE_COLOR."""
for var in ("NO_COLOR", "FORCE_COLOR", "VLLM_LOGGING_COLOR"):
monkeypatch.delenv(var, raising=False)
for var, value in env.items():
monkeypatch.setenv(var, value)

assert not _use_color()
assert LoggingConfig().log_color == expected

monkeypatch.setenv("FORCE_COLOR", "1")
assert _use_color()

monkeypatch.setenv("VLLM_LOGGING_COLOR", "0")
assert not _use_color()
monkeypatch.delenv("VLLM_LOGGING_COLOR")
def test_log_stream_defaults_from_env(monkeypatch):
monkeypatch.setenv("VLLM_LOGGING_STREAM", "ext://sys.stderr")

assert LoggingConfig().log_stream == "ext://sys.stderr"


@pytest.mark.parametrize(
("log_color", "log_stream", "tty_stream", "expected"),
[
("auto", "ext://sys.stdout", None, False),
("auto", "ext://sys.stdout", "stdout", True),
("auto", "ext://sys.stderr", "stdout", False),
("auto", "ext://sys.stderr", "stderr", True),
("always", "ext://sys.stdout", None, True),
("never", "ext://sys.stdout", "stdout", False),
],
)
def test_use_color(monkeypatch, log_color, log_stream, tty_stream, expected):
for name in ("stdout", "stderr"):
stream = io.StringIO()
monkeypatch.setattr(stream, "isatty", lambda tty=name == tty_stream: tty)
monkeypatch.setattr(sys, name, stream)

assert _use_color(log_color, log_stream) == expected


def test_log_color_and_stream_config_override_env(monkeypatch):
"""Explicit config wins over NO_COLOR, FORCE_COLOR and VLLM_LOGGING_STREAM."""
monkeypatch.setenv("VLLM_LOGGING_STREAM", "ext://sys.stdout")
monkeypatch.setenv("NO_COLOR", "1")
assert not _use_color()
monkeypatch.delenv("FORCE_COLOR", raising=False)
monkeypatch.delenv("VLLM_LOGGING_COLOR", raising=False)
logger = logging.getLogger("vllm")

try:
configure_logging(
LoggingConfig(log_color="always", log_stream="ext://sys.stderr")
)
handler = logger.handlers[0]
assert isinstance(handler, logging.StreamHandler)
assert handler.stream is sys.stderr
assert isinstance(handler.formatter, ColoredFormatter)

monkeypatch.setenv("FORCE_COLOR", "1")
configure_logging(LoggingConfig(log_color="never"))
handler = logger.handlers[0]
assert isinstance(handler, logging.StreamHandler)
assert handler.stream is sys.stdout
assert not isinstance(handler.formatter, ColoredFormatter)
finally:
_configure_vllm_root_logger()


def test_descendent_loggers_depend_on_and_propagate_logs_to_root_logger(monkeypatch):
Expand Down
13 changes: 13 additions & 0 deletions vllm/config/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

import vllm.envs as envs
from vllm.config.utils import config
from vllm.logger import log_color_from_env

LogLevel = Literal["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
LogColor = Literal["auto", "always", "never"]


@config
Expand All @@ -27,6 +29,17 @@ class LoggingConfig:
)
"""Log level used when no custom logging configuration is provided."""

log_stream: str = Field(default_factory=lambda: envs.VLLM_LOGGING_STREAM)
"""Stream used by the default handler, e.g. ``ext://sys.stdout`` or
``ext://sys.stderr``. Ignored when a custom logging configuration is
provided."""

log_color: LogColor = Field(default_factory=log_color_from_env)
"""Whether the default handler colors its output. ``auto`` colors only when
``log_stream`` is a terminal. Defaults from ``NO_COLOR``,
``VLLM_LOGGING_COLOR`` and ``FORCE_COLOR``. Ignored when a custom logging
configuration is provided."""

configure_logging: bool = Field(default_factory=lambda: envs.VLLM_CONFIGURE_LOGGING)
"""Whether to apply a Python logging configuration.

Expand Down
37 changes: 27 additions & 10 deletions vllm/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from vllm.logging_utils import ColoredFormatter, NewLineFormatter

if TYPE_CHECKING:
from vllm.config.logging import LoggingConfig
from vllm.config.logging import LogColor, LoggingConfig

_FORMAT = (
f"{envs.VLLM_LOGGING_PREFIX}%(levelname)s %(asctime)s "
Expand All @@ -31,14 +31,22 @@
_DATE_FORMAT = "%m-%d %H:%M:%S"


def _use_color() -> bool:
def log_color_from_env() -> "LogColor":
"""Resolve the default log color from ``NO_COLOR``, ``VLLM_LOGGING_COLOR``
and ``FORCE_COLOR``."""
if envs.NO_COLOR or envs.VLLM_LOGGING_COLOR == "0":
return False
return "never"
if envs.VLLM_LOGGING_COLOR == "1" or envs.FORCE_COLOR:
return True
if envs.VLLM_LOGGING_STREAM == "ext://sys.stdout": # stdout
return "always"
return "auto"


def _use_color(log_color: "LogColor", log_stream: str) -> bool:
if log_color != "auto":
return log_color == "always"
if log_stream == "ext://sys.stdout":
return hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
elif envs.VLLM_LOGGING_STREAM == "ext://sys.stderr": # stderr
elif log_stream == "ext://sys.stderr":
return hasattr(sys.stderr, "isatty") and sys.stderr.isatty()
return False

Expand All @@ -63,7 +71,11 @@ def _use_color() -> bool:
"vllm": {
"class": "logging.StreamHandler",
# Choose formatter based on color setting.
"formatter": "vllm_color" if _use_color() else "vllm",
"formatter": (
"vllm_color"
if _use_color(log_color_from_env(), envs.VLLM_LOGGING_STREAM)
else "vllm"
),
"level": envs.VLLM_LOGGING_LEVEL,
"stream": envs.VLLM_LOGGING_STREAM,
},
Expand Down Expand Up @@ -182,10 +194,14 @@ def _configure_vllm_root_logger(config: "LoggingConfig | None" = None) -> None:
configure_logging = envs.VLLM_CONFIGURE_LOGGING
log_level = envs.VLLM_LOGGING_LEVEL
log_config_file = envs.VLLM_LOGGING_CONFIG_PATH
log_color = log_color_from_env()
log_stream = envs.VLLM_LOGGING_STREAM
else:
configure_logging = config.configure_logging
log_level = config.log_level
log_config_file = config.pylogging_config_file
log_color = config.log_color
log_stream = config.log_stream

if not configure_logging and log_config_file:
raise RuntimeError(
Expand All @@ -198,10 +214,11 @@ def _configure_vllm_root_logger(config: "LoggingConfig | None" = None) -> None:
logging_config = deepcopy(DEFAULT_LOGGING_CONFIG)

vllm_handler = logging_config["handlers"]["vllm"]
# Refresh these values in case env vars have changed.
vllm_handler["level"] = log_level
vllm_handler["stream"] = envs.VLLM_LOGGING_STREAM
vllm_handler["formatter"] = "vllm_color" if _use_color() else "vllm"
vllm_handler["stream"] = log_stream
vllm_handler["formatter"] = (
"vllm_color" if _use_color(log_color, log_stream) else "vllm"
)

vllm_loggers = logging_config["loggers"]["vllm"]
vllm_loggers["level"] = log_level
Expand Down
Loading