[MISC] Rework logger to enable pythonic custom logging configuration to be provided (#4273)
This commit is contained in:
parent
826b82a260
commit
b8afa8b95a
178
examples/logging_configuration.md
Normal file
178
examples/logging_configuration.md
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# Logging Configuration
|
||||||
|
|
||||||
|
vLLM leverages Python's `logging.config.dictConfig` functionality to enable
|
||||||
|
robust and flexible configuration of the various loggers used by vLLM.
|
||||||
|
|
||||||
|
vLLM offers two environment variables that can be used to accommodate a range
|
||||||
|
of logging configurations that range from simple-and-inflexible to
|
||||||
|
more-complex-and-more-flexible.
|
||||||
|
|
||||||
|
- No vLLM logging (simple and inflexible)
|
||||||
|
- Set `VLLM_CONFIGURE_LOGGING=0` (leaving `VLLM_LOGGING_CONFIG_PATH` unset)
|
||||||
|
- vLLM's default logging configuration (simple and inflexible)
|
||||||
|
- Leave `VLLM_CONFIGURE_LOGGING` unset or set `VLLM_CONFIGURE_LOGGING=1`
|
||||||
|
- Fine-grained custom logging configuration (more complex, more flexible)
|
||||||
|
- Leave `VLLM_CONFIGURE_LOGGING` unset or set `VLLM_CONFIGURE_LOGGING=1` and
|
||||||
|
set `VLLM_LOGGING_CONFIG_PATH=<path-to-logging-config.json>`
|
||||||
|
|
||||||
|
|
||||||
|
## Logging Configuration Environment Variables
|
||||||
|
|
||||||
|
### `VLLM_CONFIGURE_LOGGING`
|
||||||
|
|
||||||
|
`VLLM_CONFIGURE_LOGGING` controls whether or not vLLM takes any action to
|
||||||
|
configure the loggers used by vLLM. This functionality is enabled by default,
|
||||||
|
but can be disabled by setting `VLLM_CONFIGURE_LOGGING=0` when running vLLM.
|
||||||
|
|
||||||
|
If `VLLM_CONFIGURE_LOGGING` is enabled and no value is given for
|
||||||
|
`VLLM_LOGGING_CONFIG_PATH`, vLLM will use built-in default configuration to
|
||||||
|
configure the root vLLM logger. By default, no other vLLM loggers are
|
||||||
|
configured and, as such, all vLLM loggers defer to the root vLLM logger to make
|
||||||
|
all logging decisions.
|
||||||
|
|
||||||
|
If `VLLM_CONFIGURE_LOGGING` is disabled and a value is given for
|
||||||
|
`VLLM_LOGGING_CONFIG_PATH`, an error will occur while starting vLLM.
|
||||||
|
|
||||||
|
### `VLLM_LOGGING_CONFIG_PATH`
|
||||||
|
|
||||||
|
`VLLM_LOGGING_CONFIG_PATH` allows users to specify a path to a JSON file of
|
||||||
|
alternative, custom logging configuration that will be used instead of vLLM's
|
||||||
|
built-in default logging configuration. The logging configuration should be
|
||||||
|
provided in JSON format following the schema specified by Python's [logging
|
||||||
|
configuration dictionary
|
||||||
|
schema](https://docs.python.org/3/library/logging.config.html#dictionary-schema-details).
|
||||||
|
|
||||||
|
If `VLLM_LOGGING_CONFIG_PATH` is specified, but `VLLM_CONFIGURE_LOGGING` is
|
||||||
|
disabled, an error will occur while starting vLLM.
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Customize vLLM root logger
|
||||||
|
|
||||||
|
For this example, we will customize the vLLM root logger to use
|
||||||
|
[`python-json-logger`](https://github.com/madzak/python-json-logger) to log to
|
||||||
|
STDOUT of the console in JSON format with a log level of `INFO`.
|
||||||
|
|
||||||
|
To begin, first, create an appropriate JSON logging configuration file:
|
||||||
|
|
||||||
|
**/path/to/logging_config.json:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"formatters": {
|
||||||
|
"json": {
|
||||||
|
"class": "pythonjsonlogger.jsonlogger.JsonFormatter"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class" : "logging.StreamHandler",
|
||||||
|
"formatter": "json",
|
||||||
|
"level": "INFO",
|
||||||
|
"stream": "ext://sys.stdout"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"vllm": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, install the `python-json-logger` package if it's not already installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install python-json-logger
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, run vLLM with the `VLLM_LOGGING_CONFIG_PATH` environment variable set
|
||||||
|
to the path of the custom logging configuration JSON file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VLLM_LOGGING_CONFIG_PATH=/path/to/logging_config.json \
|
||||||
|
python3 -m vllm.entrypoints.openai.api_server \
|
||||||
|
--max-model-len 2048 \
|
||||||
|
--model mistralai/Mistral-7B-v0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Example 2: Silence a particular vLLM logger
|
||||||
|
|
||||||
|
To silence a particular vLLM logger, it is necessary to provide custom logging
|
||||||
|
configuration for the target logger that configures the logger so that it won't
|
||||||
|
propagate its log messages to the root vLLM logger.
|
||||||
|
|
||||||
|
When custom configuration is provided for any logger, it is also necessary to
|
||||||
|
provide configuration for the root vLLM logger since any custom logger
|
||||||
|
configuration overrides the built-in default logging configuration used by vLLM.
|
||||||
|
|
||||||
|
First, create an appropriate JSON logging configuration file that includes
|
||||||
|
configuration for the root vLLM logger and for the logger you wish to silence:
|
||||||
|
|
||||||
|
**/path/to/logging_config.json:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"formatters": {
|
||||||
|
"vllm": {
|
||||||
|
"class": "vllm.logging.NewLineFormatter",
|
||||||
|
"datefmt": "%m-%d %H:%M:%S",
|
||||||
|
"format": "%(levelname)s %(asctime)s %(filename)s:%(lineno)d] %(message)s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"vllm": {
|
||||||
|
"class" : "logging.StreamHandler",
|
||||||
|
"formatter": "vllm",
|
||||||
|
"level": "INFO",
|
||||||
|
"stream": "ext://sys.stdout"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"vllm": {
|
||||||
|
"handlers": ["vllm"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
"propagage": false
|
||||||
|
},
|
||||||
|
"vllm.example_noisy_logger": {
|
||||||
|
"propagate": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, run vLLM with the `VLLM_LOGGING_CONFIG_PATH` environment variable set
|
||||||
|
to the path of the custom logging configuration JSON file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VLLM_LOGGING_CONFIG_PATH=/path/to/logging_config.json \
|
||||||
|
python3 -m vllm.entrypoints.openai.api_server \
|
||||||
|
--max-model-len 2048 \
|
||||||
|
--model mistralai/Mistral-7B-v0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Example 3: Disable vLLM default logging configuration
|
||||||
|
|
||||||
|
To disable vLLM's default logging configuration and silence all vLLM loggers,
|
||||||
|
simple set `VLLM_CONFIGURE_LOGGING=0` when running vLLM. This will prevent vLLM
|
||||||
|
for configuring the root vLLM logger, which in turn, silences all other vLLM
|
||||||
|
loggers.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VLLM_CONFIGURE_LOGGING=0 \
|
||||||
|
python3 -m vllm.entrypoints.openai.api_server \
|
||||||
|
--max-model-len 2048 \
|
||||||
|
--model mistralai/Mistral-7B-v0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Additional resources
|
||||||
|
|
||||||
|
- [`logging.config` Dictionary Schema Details](https://docs.python.org/3/library/logging.config.html#dictionary-schema-details)
|
@ -1,8 +1,19 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from json.decoder import JSONDecodeError
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import patch
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from vllm.logger import enable_trace_function_call
|
import pytest
|
||||||
|
|
||||||
|
from vllm.logger import (_DATE_FORMAT, _FORMAT, _configure_vllm_root_logger,
|
||||||
|
enable_trace_function_call, init_logger)
|
||||||
|
from vllm.logging import NewLineFormatter
|
||||||
|
|
||||||
|
|
||||||
def f1(x):
|
def f1(x):
|
||||||
@ -25,3 +36,179 @@ def test_trace_function_call():
|
|||||||
assert "f2" in content
|
assert "f2" in content
|
||||||
sys.settrace(None)
|
sys.settrace(None)
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_vllm_root_logger_configuration():
|
||||||
|
"""This test presumes that VLLM_CONFIGURE_LOGGING (default: True) and
|
||||||
|
VLLM_LOGGING_CONFIG_PATH (default: None) are not configured and default
|
||||||
|
behavior is activated."""
|
||||||
|
logger = logging.getLogger("vllm")
|
||||||
|
assert logger.level == logging.DEBUG
|
||||||
|
assert not logger.propagate
|
||||||
|
|
||||||
|
handler = logger.handlers[0]
|
||||||
|
assert handler.stream == sys.stdout
|
||||||
|
assert handler.level == logging.INFO
|
||||||
|
|
||||||
|
formatter = handler.formatter
|
||||||
|
assert formatter is not None
|
||||||
|
assert isinstance(formatter, NewLineFormatter)
|
||||||
|
assert formatter._fmt == _FORMAT
|
||||||
|
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():
|
||||||
|
"""This test presumes that VLLM_CONFIGURE_LOGGING (default: True) and
|
||||||
|
VLLM_LOGGING_CONFIG_PATH (default: None) are not configured and default
|
||||||
|
behavior is activated."""
|
||||||
|
root_logger = logging.getLogger("vllm")
|
||||||
|
root_handler = root_logger.handlers[0]
|
||||||
|
|
||||||
|
unique_name = f"vllm.{uuid4()}"
|
||||||
|
logger = init_logger(unique_name)
|
||||||
|
assert logger.name == unique_name
|
||||||
|
assert logger.level == logging.NOTSET
|
||||||
|
assert not logger.handlers
|
||||||
|
assert logger.propagate
|
||||||
|
|
||||||
|
message = "Hello, world!"
|
||||||
|
with patch.object(root_handler, "emit") as root_handle_mock:
|
||||||
|
logger.info(message)
|
||||||
|
|
||||||
|
root_handle_mock.assert_called_once()
|
||||||
|
_, call_args, _ = root_handle_mock.mock_calls[0]
|
||||||
|
log_record = call_args[0]
|
||||||
|
assert unique_name == log_record.name
|
||||||
|
assert message == log_record.msg
|
||||||
|
assert message == log_record.msg
|
||||||
|
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():
|
||||||
|
"""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."""
|
||||||
|
|
||||||
|
with patch("logging.config.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():
|
||||||
|
"""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."""
|
||||||
|
with pytest.raises(RuntimeError) as ex_info:
|
||||||
|
_configure_vllm_root_logger()
|
||||||
|
assert ex_info.type == RuntimeError
|
||||||
|
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():
|
||||||
|
"""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."""
|
||||||
|
with NamedTemporaryFile(encoding="utf-8", mode="w") as logging_config_file:
|
||||||
|
logging_config_file.write("---\nloggers: []\nversion: 1")
|
||||||
|
logging_config_file.flush()
|
||||||
|
with patch("vllm.logger.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", (
|
||||||
|
"Invalid string",
|
||||||
|
[{
|
||||||
|
"version": 1,
|
||||||
|
"loggers": []
|
||||||
|
}],
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
def test_an_error_is_raised_when_custom_logging_config_is_unexpected_json(
|
||||||
|
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."""
|
||||||
|
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):
|
||||||
|
with pytest.raises(ValueError) as ex_info:
|
||||||
|
_configure_vllm_root_logger()
|
||||||
|
assert ex_info.type == ValueError
|
||||||
|
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():
|
||||||
|
"""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."""
|
||||||
|
valid_logging_config = {
|
||||||
|
"loggers": {
|
||||||
|
"vllm.test_logger.logger": {
|
||||||
|
"handlers": [],
|
||||||
|
"propagate": False,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
"logging.config.dictConfig") as dict_config_mock:
|
||||||
|
_configure_vllm_root_logger()
|
||||||
|
assert dict_config_mock.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():
|
||||||
|
"""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."""
|
||||||
|
valid_logging_config = {
|
||||||
|
"loggers": {
|
||||||
|
"vllm.test_logger.logger": {
|
||||||
|
"handlers": [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
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):
|
||||||
|
with pytest.raises(RuntimeError) as ex_info:
|
||||||
|
_configure_vllm_root_logger()
|
||||||
|
assert ex_info.type is RuntimeError
|
||||||
|
expected_message_snippet = (
|
||||||
|
"VLLM_CONFIGURE_LOGGING evaluated to false, but "
|
||||||
|
"VLLM_LOGGING_CONFIG_PATH was given.")
|
||||||
|
assert expected_message_snippet in str(ex_info)
|
||||||
|
|
||||||
|
# Remember! The root logger is assumed to have been configured as
|
||||||
|
# though VLLM_CONFIGURE_LOGGING=1 and VLLM_LOGGING_CONFIG_PATH=None.
|
||||||
|
root_logger = logging.getLogger("vllm")
|
||||||
|
other_logger_name = f"vllm.test_logger.{uuid4()}"
|
||||||
|
other_logger = init_logger(other_logger_name)
|
||||||
|
assert other_logger.handlers != root_logger.handlers
|
||||||
|
assert other_logger.level != root_logger.level
|
||||||
|
assert other_logger.propagate
|
||||||
|
122
vllm/logger.py
122
vllm/logger.py
@ -1,73 +1,91 @@
|
|||||||
# Adapted from
|
|
||||||
# https://github.com/skypilot-org/skypilot/blob/86dc0f6283a335e4aa37b3c10716f90999f48ab6/sky/sky_logging.py
|
|
||||||
"""Logging configuration for vLLM."""
|
"""Logging configuration for vLLM."""
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Optional
|
from logging import Logger
|
||||||
|
from logging.config import dictConfig
|
||||||
|
from os import path
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
VLLM_CONFIGURE_LOGGING = int(os.getenv("VLLM_CONFIGURE_LOGGING", "1"))
|
VLLM_CONFIGURE_LOGGING = int(os.getenv("VLLM_CONFIGURE_LOGGING", "1"))
|
||||||
|
VLLM_LOGGING_CONFIG_PATH = os.getenv("VLLM_LOGGING_CONFIG_PATH")
|
||||||
|
|
||||||
_FORMAT = "%(levelname)s %(asctime)s %(filename)s:%(lineno)d] %(message)s"
|
_FORMAT = "%(levelname)s %(asctime)s %(filename)s:%(lineno)d] %(message)s"
|
||||||
_DATE_FORMAT = "%m-%d %H:%M:%S"
|
_DATE_FORMAT = "%m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
DEFAULT_LOGGING_CONFIG = {
|
||||||
class NewLineFormatter(logging.Formatter):
|
"formatters": {
|
||||||
"""Adds logging prefix to newlines to align multi-line messages."""
|
"vllm": {
|
||||||
|
"class": "vllm.logging.NewLineFormatter",
|
||||||
def __init__(self, fmt, datefmt=None):
|
"datefmt": _DATE_FORMAT,
|
||||||
logging.Formatter.__init__(self, fmt, datefmt)
|
"format": _FORMAT,
|
||||||
|
},
|
||||||
def format(self, record):
|
},
|
||||||
msg = logging.Formatter.format(self, record)
|
"handlers": {
|
||||||
if record.message != "":
|
"vllm": {
|
||||||
parts = msg.split(record.message)
|
"class": "logging.StreamHandler",
|
||||||
msg = msg.replace("\n", "\r\n" + parts[0])
|
"formatter": "vllm",
|
||||||
return msg
|
"level": "INFO",
|
||||||
|
"stream": "ext://sys.stdout",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"vllm": {
|
||||||
|
"handlers": ["vllm"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"version": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_root_logger = logging.getLogger("vllm")
|
def _configure_vllm_root_logger() -> None:
|
||||||
_default_handler: Optional[logging.Handler] = None
|
logging_config: Optional[Dict] = None
|
||||||
|
|
||||||
|
if not VLLM_CONFIGURE_LOGGING and VLLM_LOGGING_CONFIG_PATH:
|
||||||
def _setup_logger():
|
raise RuntimeError(
|
||||||
_root_logger.setLevel(logging.DEBUG)
|
"VLLM_CONFIGURE_LOGGING evaluated to false, but "
|
||||||
global _default_handler
|
"VLLM_LOGGING_CONFIG_PATH was given. VLLM_LOGGING_CONFIG_PATH "
|
||||||
if _default_handler is None:
|
"implies VLLM_CONFIGURE_LOGGING. Please enable "
|
||||||
_default_handler = logging.StreamHandler(sys.stdout)
|
"VLLM_CONFIGURE_LOGGING or unset VLLM_LOGGING_CONFIG_PATH.")
|
||||||
_default_handler.flush = sys.stdout.flush # type: ignore
|
|
||||||
_default_handler.setLevel(logging.INFO)
|
|
||||||
_root_logger.addHandler(_default_handler)
|
|
||||||
fmt = NewLineFormatter(_FORMAT, datefmt=_DATE_FORMAT)
|
|
||||||
_default_handler.setFormatter(fmt)
|
|
||||||
# Setting this will avoid the message
|
|
||||||
# being propagated to the parent logger.
|
|
||||||
_root_logger.propagate = False
|
|
||||||
|
|
||||||
|
|
||||||
# The logger is initialized when the module is imported.
|
|
||||||
# This is thread-safe as the module is only imported once,
|
|
||||||
# guaranteed by the Python GIL.
|
|
||||||
if VLLM_CONFIGURE_LOGGING:
|
|
||||||
_setup_logger()
|
|
||||||
|
|
||||||
|
|
||||||
def init_logger(name: str):
|
|
||||||
# Use the same settings as above for root logger
|
|
||||||
logger = logging.getLogger(name)
|
|
||||||
logger.setLevel(os.getenv("LOG_LEVEL", "DEBUG"))
|
|
||||||
|
|
||||||
if VLLM_CONFIGURE_LOGGING:
|
if VLLM_CONFIGURE_LOGGING:
|
||||||
if _default_handler is None:
|
logging_config = DEFAULT_LOGGING_CONFIG
|
||||||
raise ValueError(
|
|
||||||
"_default_handler is not set up. This should never happen!"
|
|
||||||
" Please open an issue on Github.")
|
|
||||||
logger.addHandler(_default_handler)
|
|
||||||
logger.propagate = False
|
|
||||||
return logger
|
|
||||||
|
|
||||||
|
if VLLM_LOGGING_CONFIG_PATH:
|
||||||
|
if not path.exists(VLLM_LOGGING_CONFIG_PATH):
|
||||||
|
raise RuntimeError(
|
||||||
|
"Could not load logging config. File does not exist: %s",
|
||||||
|
VLLM_LOGGING_CONFIG_PATH)
|
||||||
|
with open(VLLM_LOGGING_CONFIG_PATH, encoding="utf-8",
|
||||||
|
mode="r") as file:
|
||||||
|
custom_config = json.loads(file.read())
|
||||||
|
|
||||||
|
if not isinstance(custom_config, dict):
|
||||||
|
raise ValueError("Invalid logging config. Expected Dict, got %s.",
|
||||||
|
type(custom_config).__name__)
|
||||||
|
logging_config = custom_config
|
||||||
|
|
||||||
|
if logging_config:
|
||||||
|
dictConfig(logging_config)
|
||||||
|
|
||||||
|
|
||||||
|
def init_logger(name: str) -> Logger:
|
||||||
|
"""The main purpose of this function is to ensure that loggers are
|
||||||
|
retrieved in such a way that we can be sure the root vllm logger has
|
||||||
|
already been configured."""
|
||||||
|
|
||||||
|
return logging.getLogger(name)
|
||||||
|
|
||||||
|
|
||||||
|
# The root logger is initialized when the module is imported.
|
||||||
|
# This is thread-safe as the module is only imported once,
|
||||||
|
# guaranteed by the Python GIL.
|
||||||
|
_configure_vllm_root_logger()
|
||||||
|
|
||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
|
5
vllm/logging/__init__.py
Normal file
5
vllm/logging/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from vllm.logging.formatter import NewLineFormatter
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"NewLineFormatter",
|
||||||
|
]
|
15
vllm/logging/formatter.py
Normal file
15
vllm/logging/formatter.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class NewLineFormatter(logging.Formatter):
|
||||||
|
"""Adds logging prefix to newlines to align multi-line messages."""
|
||||||
|
|
||||||
|
def __init__(self, fmt, datefmt=None, style="%"):
|
||||||
|
logging.Formatter.__init__(self, fmt, datefmt, style)
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
msg = logging.Formatter.format(self, record)
|
||||||
|
if record.message != "":
|
||||||
|
parts = msg.split(record.message)
|
||||||
|
msg = msg.replace("\n", "\r\n" + parts[0])
|
||||||
|
return msg
|
Loading…
x
Reference in New Issue
Block a user