[Bugfix][Logging] Preserve application log record factories (#58747)
Signed-off-by: mgoin <mgoin64@gmail.com> Co-authored-by: OpenAI Codex <noreply@openai.com>
Misha Goin committed
Sep 28, 2026 at 09:20 UTC
d7f5722d7b487dd6794f638043375a95ebecfef7
2 files changed
+60
-13
tests/test_logger.py
+41
@@ -165,6 +165,45 @@ def test_json_logging(monkeypatch, tmp_path):
165
assert log["process"] == os.getpid()
166
167
168
+@pytest.mark.parametrize("factory_order", ["before", "after", "replacement"])
169
+def test_configure_logging_preserves_application_record_factory(
170
+ monkeypatch, factory_order
171
+):
172
+ """Application fields survive initial configuration and reconfiguration."""
173
+ original_factory = logging.getLogRecordFactory()
174
+ monkeypatch.setattr(vllm_logger, "dictConfig", lambda _: None)
175
+ monkeypatch.setattr(vllm_logger, "_last_configured_logging_config", None)
176
+ monkeypatch.setattr(vllm_logger, "_vllm_process_info", None)
177
+ config = LoggingConfig()
178
+ formatter = logging.Formatter("%(request_id)s %(vllm_process_name)s %(message)s")
179
+
180
+ try:
181
+ logging.setLogRecordFactory(logging.LogRecord)
182
+ if factory_order != "before":
183
+ configure_logging(config)
184
+ base_factory = (
185
+ logging.LogRecord
186
+ if factory_order == "replacement"
187
+ else logging.getLogRecordFactory()
188
+ )
189
+
190
+ def application_factory(*args, **kwargs):
191
+ record = base_factory(*args, **kwargs)
192
+ record.request_id = "request-123"
193
+ return record
194
+
195
+ logging.setLogRecordFactory(application_factory)
196
+ decorate_logs("Worker_DP0")
197
+ for _ in range(2):
198
+ configure_logging(config)
199
+ record = logging.getLogger("application").makeRecord(
200
+ "application", logging.INFO, __file__, 1, "probe", (), None
201
+ )
202
+ assert formatter.format(record) == "request-123 Worker_DP0 probe"
203
+ finally:
204
+ logging.setLogRecordFactory(original_factory)
205
+
206
+
207
def test_use_color_force_color(monkeypatch):
208
"""FORCE_COLOR forces colored logs without a TTY, while NO_COLOR and an
209
explicit VLLM_LOGGING_COLOR=0 take precedence over it."""
@@ -223,9 +262,11 @@ def test_logger_configuring_can_be_disabled(monkeypatch):
262
monkeypatch.setenv("VLLM_CONFIGURE_LOGGING", "0")
263
monkeypatch.delenv("VLLM_LOGGING_CONFIG_PATH", raising=False)
264
265
+ original_factory = logging.getLogRecordFactory()
266
with patch("vllm.logger.dictConfig") as dict_config_mock:
267
configure_logging(LoggingConfig(configure_logging=False))
268
dict_config_mock.assert_not_called()
269
+ assert logging.getLogRecordFactory() is original_factory
270
271
272
def test_an_error_is_raised_when_custom_logging_config_file_does_not_exist(monkeypatch):
vllm/logger.py
+19
-13
@@ -7,7 +7,7 @@ import json
7
import logging
8
import os
9
import sys
10
-from collections.abc import Generator, Hashable
10
+from collections.abc import Callable, Generator, Hashable
11
from contextlib import contextmanager
12
from copy import deepcopy
13
from dataclasses import replace
@@ -31,20 +31,24 @@ _FORMAT = (
31
)
32
_DATE_FORMAT = "%m-%d %H:%M:%S"
33
34
-_base_log_record_factory = logging.getLogRecordFactory()
34
_vllm_process_info: tuple[str, int] | None = None
35
36
38
-def _vllm_log_record_factory(*args: Any, **kwargs: Any) -> logging.LogRecord:
39
- """Add vLLM process metadata to each log record."""
40
- record = _base_log_record_factory(*args, **kwargs)
41
- process_info = _vllm_process_info
42
- pid = os.getpid()
43
- if process_info is None or process_info[1] != pid:
44
- record.vllm_process_name = record.processName
45
- else:
46
- record.vllm_process_name = process_info[0]
47
- return record
37
+class _VllmLogRecordFactory:
38
+ """Add vLLM process metadata while preserving the wrapped record factory."""
39
+
40
+ def __init__(self, factory: Callable[..., logging.LogRecord]) -> None:
41
+ self._factory = factory
42
+
43
+ def __call__(self, *args: Any, **kwargs: Any) -> logging.LogRecord:
44
+ record = self._factory(*args, **kwargs)
45
+ process_info = _vllm_process_info
46
+ pid = os.getpid()
47
+ if process_info is None or process_info[1] != pid:
48
+ record.vllm_process_name = record.processName
49
+ else:
50
+ record.vllm_process_name = process_info[0]
51
+ return record
52
53
54
def set_vllm_process_name(process_name: str, *, skip_if_set: bool = False) -> None:
@@ -220,7 +224,9 @@ def _configure_vllm_root_logger(config: "LoggingConfig | None" = None) -> None:
224
)
225
226
if configure_logging:
223
- logging.setLogRecordFactory(_vllm_log_record_factory)
227
+ factory = logging.getLogRecordFactory()
228
+ if not isinstance(factory, _VllmLogRecordFactory):
229
+ logging.setLogRecordFactory(_VllmLogRecordFactory(factory))
230
logging_config = deepcopy(DEFAULT_LOGGING_CONFIG)
231
232
vllm_handler = logging_config["handlers"]["vllm"]