@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 60b755cbcb
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 14, 05:30 UTC

[Misc] Have AsyncLLM `custom_stat_loggers` extend default logger list (#20952)

Signed-off-by: Seiji Eicher <seiji@anyscale.com> Signed-off-by: Seiji Eicher <58963096+eicherseiji@users.noreply.github.com> Co-authored-by: Nick Hill <nhill@redhat.com>

Seiji Eicher committed Sep 4, 2025 at 14:25 UTC 60b755cbcb1154e519572410f60d0d258eecbce4
4 files changed +99 -9
tests/v1/engine/test_async_llm.py
+3 -2
@@ -393,7 +393,7 @@ class MockLoggingStatLogger(LoggingStatLogger):
393 async def test_customize_loggers(monkeypatch):
394 """Test that we can customize the loggers.
395 If a customized logger is provided at the init, it should
396 - be used directly.
396 + be added to the default loggers.
397 """
398
399 with monkeypatch.context() as m, ExitStack() as after:
@@ -410,7 +410,8 @@ async def test_customize_loggers(monkeypatch):
410
411 stat_loggers = engine.logger_manager.per_engine_logger_dict
412 assert len(stat_loggers) == 1
413 - assert len(stat_loggers[0]) == 1
413 + assert len(
414 + stat_loggers[0]) == 2 # LoggingStatLogger + MockLoggingStatLogger
415 stat_loggers[0][0].log.assert_called_once()
416
417
tests/v1/metrics/test_engine_logger_apis.py new
+83
@@ -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
+7 -1
@@ -98,7 +98,12 @@ class AsyncLLM(EngineClient):
98 self.model_config = vllm_config.model_config
99 self.vllm_config = vllm_config
100 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")
107
108 if self.model_config.skip_tokenizer_init:
109 self.tokenizer = None
@@ -137,6 +142,7 @@ class AsyncLLM(EngineClient):
142 vllm_config=vllm_config,
143 engine_idxs=self.engine_core.engine_ranks_managed,
144 custom_stat_loggers=stat_loggers,
145 + enable_default_loggers=log_stats,
146 )
147 self.logger_manager.log_engine_initialized()
148
vllm/v1/metrics/loggers.py
+6 -6
@@ -651,16 +651,16 @@ class StatLoggerManager:
651 vllm_config: VllmConfig,
652 engine_idxs: Optional[list[int]] = None,
653 custom_stat_loggers: Optional[list[StatLoggerFactory]] = None,
654 + enable_default_loggers: bool = True,
655 ):
656 self.engine_idxs = engine_idxs if engine_idxs else [0]
657
657 - factories: list[StatLoggerFactory]
658 + factories: list[StatLoggerFactory] = []
659 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)
664
665 # engine_idx: StatLogger
666 self.per_engine_logger_dict: dict[int, list[StatLoggerBase]] = {}