Skip to content

Commit 60b755c

Browse files
eicherseijinjhill
andauthored
[Misc] Have AsyncLLM custom_stat_loggers extend default logger list (#20952)
Signed-off-by: Seiji Eicher <[email protected]> Signed-off-by: Seiji Eicher <[email protected]> Co-authored-by: Nick Hill <[email protected]>
1 parent 482e52f commit 60b755c

File tree

4 files changed

+99
-9
lines changed

4 files changed

+99
-9
lines changed

tests/v1/engine/test_async_llm.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ def __init__(self, vllm_config: VllmConfig, engine_index: int = 0):
393393
async def test_customize_loggers(monkeypatch):
394394
"""Test that we can customize the loggers.
395395
If a customized logger is provided at the init, it should
396-
be used directly.
396+
be added to the default loggers.
397397
"""
398398

399399
with monkeypatch.context() as m, ExitStack() as after:
@@ -410,7 +410,8 @@ async def test_customize_loggers(monkeypatch):
410410

411411
stat_loggers = engine.logger_manager.per_engine_logger_dict
412412
assert len(stat_loggers) == 1
413-
assert len(stat_loggers[0]) == 1
413+
assert len(
414+
stat_loggers[0]) == 2 # LoggingStatLogger + MockLoggingStatLogger
414415
stat_loggers[0][0].log.assert_called_once()
415416

416417

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
import copy
4+
5+
import pytest
6+
7+
from vllm.v1.engine.async_llm import AsyncEngineArgs, AsyncLLM
8+
from vllm.v1.metrics.ray_wrappers import RayPrometheusStatLogger
9+
10+
11+
class DummyStatLogger:
12+
"""
13+
A dummy stat logger for testing purposes.
14+
Implements the minimal interface expected by StatLoggerManager.
15+
"""
16+
17+
def __init__(self, vllm_config, engine_idx):
18+
self.vllm_config = vllm_config
19+
self.engine_idx = engine_idx
20+
self.recorded = []
21+
self.logged = False
22+
self.engine_initialized = False
23+
24+
def record(self, scheduler_stats, iteration_stats, engine_idx):
25+
self.recorded.append((scheduler_stats, iteration_stats, engine_idx))
26+
27+
def log(self):
28+
self.logged = True
29+
30+
def log_engine_initialized(self):
31+
self.engine_initialized = True
32+
33+
34+
@pytest.fixture
35+
def log_stats_enabled_engine_args():
36+
"""
37+
Shared fixture providing common AsyncEngineArgs configuration
38+
used across multiple tests.
39+
"""
40+
return AsyncEngineArgs(
41+
model="distilbert/distilgpt2",
42+
dtype="half",
43+
disable_log_stats=False,
44+
enforce_eager=True,
45+
)
46+
47+
48+
@pytest.mark.asyncio
49+
async def test_async_llm_replace_default_loggers(
50+
log_stats_enabled_engine_args):
51+
"""
52+
RayPrometheusStatLogger should replace the default PrometheusStatLogger
53+
"""
54+
55+
engine = AsyncLLM.from_engine_args(log_stats_enabled_engine_args,
56+
stat_loggers=[RayPrometheusStatLogger])
57+
assert isinstance(engine.logger_manager.prometheus_logger,
58+
RayPrometheusStatLogger)
59+
engine.shutdown()
60+
61+
62+
@pytest.mark.asyncio
63+
async def test_async_llm_add_to_default_loggers(log_stats_enabled_engine_args):
64+
"""
65+
It's still possible to use custom stat loggers exclusively by passing
66+
disable_log_stats=True in addition to a list of custom stat loggers.
67+
"""
68+
# Create engine_args with disable_log_stats=True for this test
69+
disabled_log_engine_args = copy.deepcopy(log_stats_enabled_engine_args)
70+
disabled_log_engine_args.disable_log_stats = True
71+
72+
# Disable default loggers; pass custom stat logger to the constructor
73+
engine = AsyncLLM.from_engine_args(disabled_log_engine_args,
74+
stat_loggers=[DummyStatLogger])
75+
76+
assert len(engine.logger_manager.per_engine_logger_dict[0]) == 1
77+
assert isinstance(engine.logger_manager.per_engine_logger_dict[0][0],
78+
DummyStatLogger)
79+
80+
# log_stats is still True, since custom stat loggers are used
81+
assert engine.log_stats
82+
83+
engine.shutdown()

vllm/v1/engine/async_llm.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,12 @@ def __init__(
9898
self.model_config = vllm_config.model_config
9999
self.vllm_config = vllm_config
100100
self.log_requests = log_requests
101-
self.log_stats = log_stats
101+
102+
self.log_stats = log_stats or (stat_loggers is not None)
103+
if not log_stats and stat_loggers is not None:
104+
logger.info(
105+
"AsyncLLM created with log_stats=False and non-empty custom "
106+
"logger list; enabling logging without default stat loggers")
102107

103108
if self.model_config.skip_tokenizer_init:
104109
self.tokenizer = None
@@ -137,6 +142,7 @@ def __init__(
137142
vllm_config=vllm_config,
138143
engine_idxs=self.engine_core.engine_ranks_managed,
139144
custom_stat_loggers=stat_loggers,
145+
enable_default_loggers=log_stats,
140146
)
141147
self.logger_manager.log_engine_initialized()
142148

vllm/v1/metrics/loggers.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -651,16 +651,16 @@ def __init__(
651651
vllm_config: VllmConfig,
652652
engine_idxs: Optional[list[int]] = None,
653653
custom_stat_loggers: Optional[list[StatLoggerFactory]] = None,
654+
enable_default_loggers: bool = True,
654655
):
655656
self.engine_idxs = engine_idxs if engine_idxs else [0]
656657

657-
factories: list[StatLoggerFactory]
658+
factories: list[StatLoggerFactory] = []
658659
if custom_stat_loggers is not None:
659-
factories = custom_stat_loggers
660-
else:
661-
factories = []
662-
if logger.isEnabledFor(logging.INFO):
663-
factories.append(LoggingStatLogger)
660+
factories.extend(custom_stat_loggers)
661+
662+
if enable_default_loggers and logger.isEnabledFor(logging.INFO):
663+
factories.append(LoggingStatLogger)
664664

665665
# engine_idx: StatLogger
666666
self.per_engine_logger_dict: dict[int, list[StatLoggerBase]] = {}

0 commit comments

Comments
 (0)