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 1, 22:30 UTC
[Logs Refactor] Optimize shutdown logs, easier to follow and consistent (#43707)
Signed-off-by: yewentao256 <zhyanwentao@126.com>
Wentao Ye committed
Jun 4, 2026 at 14:36 UTC
41a4829f2202f726711349994526afd4b24be868
7 files changed
+139
-23
vllm/distributed/parallel_state.py
+6
@@ -2055,6 +2055,10 @@ def destroy_distributed_environment():
2055
2056
2057
def cleanup_dist_env_and_memory(shutdown_ray: bool = False):
2058
+ logger.debug(
2059
+ "[shutdown] Distributed: cleanup start shutdown_ray=%s",
2060
+ shutdown_ray,
2061
+ )
2062
# Reset environment variable cache
2063
envs.disable_envs_cache()
2064
@@ -2089,6 +2093,8 @@ def cleanup_dist_env_and_memory(shutdown_ray: bool = False):
2093
"torch._C._host_emptyCache() only available in Pytorch >=2.5"
2094
)
2095
2096
+ logger.debug_once("[shutdown] Distributed: cleanup complete")
2097
+
2098
2099
def in_the_same_node_as(
2100
pg: ProcessGroup | StatelessProcessGroup, source_rank: int = 0
vllm/entrypoints/launcher.py
+13
-1
@@ -95,6 +95,9 @@ async def serve_http(
95
shutdown_event = asyncio.Event()
96
97
def signal_handler() -> None:
98
+ if shutdown_event.is_set():
99
+ return
100
+ logger.info_once("[shutdown] API server: shutdown triggered")
101
shutdown_event.set()
102
103
async def dummy_shutdown() -> None:
@@ -108,12 +111,21 @@ async def serve_http(
111
112
engine_client = app.state.engine_client
113
timeout = engine_client.vllm_config.shutdown_timeout
114
+ mode = "abort" if timeout == 0 else "drain"
115
+
116
+ logger.info(
117
+ "[shutdown] API server: stopping engine client mode=%s timeout=%ss",
118
+ mode,
119
+ timeout,
120
+ )
121
122
await loop.run_in_executor(
123
None, partial(engine_client.shutdown, timeout=timeout)
124
)
125
+ logger.info_once("[shutdown] API server: engine client stopped")
126
127
server.should_exit = True
128
+ logger.info_once("[shutdown] API server: signalling HTTP server shutdown")
129
server_task.cancel()
130
watchdog_task.cancel()
131
if ssl_cert_refresher:
@@ -134,7 +146,7 @@ async def serve_http(
146
process,
147
" ".join(process.cmdline()),
148
)
137
- logger.info("Shutting down FastAPI HTTP server.")
149
+ logger.info_once("[shutdown] API server: shutting down FastAPI HTTP server")
150
return server.shutdown()
151
finally:
152
shutdown_task.cancel()
vllm/v1/core/sched/scheduler.py
+4
@@ -2056,13 +2056,17 @@ class Scheduler(SchedulerInterface):
2056
return spec_decoding_stats
2057
2058
def shutdown(self) -> None:
2059
+ logger.debug_once("[shutdown] Scheduler: start")
2060
if self.kv_event_publisher:
2061
self.kv_event_publisher.shutdown()
2062
if self.connector is not None:
2063
self.connector.shutdown()
2064
+
2065
if self.ec_connector is not None:
2066
self.ec_connector.shutdown()
2067
2068
+ logger.debug_once("[shutdown] Scheduler: complete")
2069
+
2070
########################################################################
2071
# KV Connector Related Methods
2072
########################################################################
vllm/v1/engine/core.py
+32
-7
@@ -608,6 +608,7 @@ class EngineCore:
608
self.abort_requests(request_ids)
609
610
def shutdown(self):
611
+ logger.debug_once("[shutdown] EngineCore: tearing down local resources")
612
self.structured_output_manager.clear_backend()
613
if self.model_executor:
614
self.model_executor.shutdown()
@@ -622,6 +623,7 @@ class EngineCore:
623
# Tear down distributed state initialized in this EngineCore process
624
# before it exits and release cached memory.
625
cleanup_dist_env_and_memory()
626
+ logger.debug_once("[shutdown] EngineCore: local resource teardown complete")
627
628
def profile(self, is_start: bool = True, profile_prefix: str | None = None):
629
self.model_executor.profile(is_start, profile_prefix)
@@ -1172,6 +1174,11 @@ class EngineCoreProc(EngineCore):
1174
signal_callback = SignalCallback(wakeup_engine)
1175
1176
def signal_handler(signum, frame):
1177
+ signal_name = signal.Signals(signum).name
1178
+ logger.info(
1179
+ "[shutdown] EngineCore: trigger received signal=%s",
1180
+ signal_name,
1181
+ )
1182
engine_core.shutdown_state = EngineShutdownState.REQUESTED
1183
signal_callback.trigger()
1184
@@ -1181,7 +1188,7 @@ class EngineCoreProc(EngineCore):
1188
engine_core.run_busy_loop()
1189
1190
except SystemExit:
1184
- logger.debug("EngineCore exiting.")
1191
+ logger.info_once("[shutdown] EngineCore: exiting busy loop")
1192
raise
1193
except Exception as e:
1194
if engine_core is None:
@@ -1285,13 +1292,21 @@ class EngineCoreProc(EngineCore):
1292
1293
if self.shutdown_state == EngineShutdownState.REQUESTED:
1294
shutdown_timeout = self.vllm_config.shutdown_timeout
1295
+ mode = "abort" if shutdown_timeout == 0 else "drain"
1296
1289
- logger.info("Shutdown initiated (timeout=%d)", shutdown_timeout)
1297
+ logger.info(
1298
+ "[shutdown] EngineCore: start mode=%s timeout=%ds",
1299
+ mode,
1300
+ shutdown_timeout,
1301
+ )
1302
1303
if shutdown_timeout == 0:
1304
num_requests = self.scheduler.get_num_unfinished_requests()
1305
if num_requests > 0:
1294
- logger.info("Aborting %d requests", num_requests)
1306
+ logger.info(
1307
+ "[shutdown] EngineCore: aborting in-flight requests count=%d",
1308
+ num_requests,
1309
+ )
1310
aborted_reqs = self.scheduler.finish_requests(
1311
None, RequestStatus.FINISHED_ABORTED
1312
)
@@ -1300,7 +1315,8 @@ class EngineCoreProc(EngineCore):
1315
num_requests = self.scheduler.get_num_unfinished_requests()
1316
if num_requests > 0:
1317
logger.info(
1303
- "Draining %d in-flight requests (timeout=%ds)",
1318
+ "[shutdown] EngineCore: draining in-flight requests "
1319
+ "count=%d timeout=%ds",
1320
num_requests,
1321
shutdown_timeout,
1322
)
@@ -1309,7 +1325,10 @@ class EngineCoreProc(EngineCore):
1325
1326
# Exit when no work remaining
1327
if not self.has_work():
1312
- logger.info("Shutdown complete")
1328
+ logger.info(
1329
+ "[shutdown] EngineCore: request processing complete; "
1330
+ "starting resource teardown"
1331
+ )
1332
return False
1333
1334
return True
@@ -1353,7 +1372,10 @@ class EngineCoreProc(EngineCore):
1372
if self.shutdown_state == EngineShutdownState.RUNNING:
1373
return False
1374
1356
- logger.info("Rejecting request %s (server shutting down)", request.request_id)
1375
+ logger.debug(
1376
+ "[shutdown] EngineCore: rejecting new request request_id=%s",
1377
+ request.request_id,
1378
+ )
1379
self._send_abort_outputs_to_client([request.request_id], request.client_index)
1380
return True
1381
@@ -1363,7 +1385,10 @@ class EngineCoreProc(EngineCore):
1385
if self.shutdown_state == EngineShutdownState.RUNNING:
1386
return False
1387
1366
- logger.warning("Rejecting utility call %s (server shutting down)", method_name)
1388
+ logger.warning(
1389
+ "[shutdown] EngineCore: rejecting utility call method=%s",
1390
+ method_name,
1391
+ )
1392
output = UtilityOutput(call_id, failure_message="Server shutting down")
1393
self.output_queue.put_nowait(
1394
(client_idx, EngineCoreOutputs(utility_output=output))
vllm/v1/engine/core_client.py
+12
@@ -391,6 +391,7 @@ class BackgroundResources:
391
def __call__(self):
392
"""Clean up background resources."""
393
394
+ logger.debug_once("[shutdown] MPClient: background resource cleanup start")
395
self.engine_dead = True
396
if self.engine_manager is not None:
397
self.engine_manager.shutdown()
@@ -445,6 +446,8 @@ class BackgroundResources:
446
# Send shutdown signal.
447
shutdown_sender.send(b"")
448
449
+ logger.debug_once("[shutdown] MPClient: background resource cleanup complete")
450
+
451
def validate_alive(self, frames: Sequence[zmq.Frame]):
452
if len(frames) == 1 and (frames[0].buffer == EngineCoreProc.ENGINE_CORE_DEAD):
453
self.engine_dead = True
@@ -645,9 +648,15 @@ class MPClient(EngineCoreClient):
648
def shutdown(self, timeout: float | None = None) -> None:
649
"""Shutdown engine manager under timeout and clean up resources."""
650
if self._finalizer.detach() is not None:
651
+ timeout_str = "default" if timeout is None else f"{timeout}s"
652
+ logger.info("[shutdown] MPClient: start timeout=%s", timeout_str)
653
if self.resources.engine_manager is not None:
654
+ logger.info_once("[shutdown] MPClient: stopping engine manager")
655
self.resources.engine_manager.shutdown(timeout=timeout)
656
+ logger.info_once("[shutdown] MPClient: engine manager stopped")
657
+ logger.info_once("[shutdown] MPClient: cleaning up background resources")
658
self.resources()
659
+ logger.info_once("[shutdown] MPClient: complete")
660
661
def _format_exception(self, e: Exception) -> Exception:
662
"""If errored, use EngineDeadError so root cause is clear."""
@@ -687,6 +696,9 @@ class MPClient(EngineCoreClient):
696
if not _self or not _self._finalizer.alive or _self.resources.engine_dead:
697
return
698
_self.resources.engine_dead = True
699
+ logger.warning_once(
700
+ "[shutdown] MPClient: engine core exited unexpectedly; starting cleanup"
701
+ )
702
_self.shutdown()
703
# Note: For MPClient, we don't have a failure callback mechanism
704
# like MultiprocExecutor, but we set engine_dead flag which will
vllm/v1/executor/multiproc_executor.py
+36
-9
@@ -422,27 +422,45 @@ class MultiprocExecutor(Executor):
422
return False
423
424
active_procs = lambda: [proc for proc in worker_procs if proc.is_alive()]
425
+ initial_count = len(active_procs())
426
+
427
# Give processes time to clean themselves up properly first
426
- logger.debug("Worker Termination: allow workers to gracefully shutdown")
428
+ logger.info(
429
+ "[shutdown] Executor: waiting for worker exit count=%d",
430
+ initial_count,
431
+ )
432
if wait_for_termination(active_procs(), 4):
433
+ logger.info_once("[shutdown] Executor: all workers exited gracefully")
434
return
435
436
# Send SIGTERM if still running
431
- logger.debug("Worker Termination: workers still running sending SIGTERM")
432
- for p in active_procs():
437
+ remaining = active_procs()
438
+ logger.warning(
439
+ "[shutdown] Executor: workers still running after grace period; "
440
+ "sending SIGTERM count=%d",
441
+ len(remaining),
442
+ )
443
+ for p in remaining:
444
p.terminate()
445
if not wait_for_termination(active_procs(), 4):
446
# Send SIGKILL if still running
436
- logger.debug(
437
- "Worker Termination: resorting to SIGKILL to take down workers"
447
+ remaining = active_procs()
448
+ logger.warning(
449
+ "[shutdown] Executor: workers still running after SIGTERM; "
450
+ "sending SIGKILL count=%d",
451
+ len(remaining),
452
)
439
- for p in active_procs():
453
+ for p in remaining:
454
p.kill()
455
456
def shutdown(self):
457
"""Properly shut down the executor and its workers"""
458
if not getattr(self, "shutting_down", False):
445
- logger.debug("Triggering shutdown of workers")
459
+ worker_count = len(getattr(self, "workers", None) or [])
460
+ logger.debug(
461
+ "[shutdown] Executor: start worker_count=%d",
462
+ worker_count,
463
+ )
464
self.shutting_down = True
465
466
# Make sure all the worker processes are terminated first.
@@ -468,6 +486,8 @@ class MultiprocExecutor(Executor):
486
mq.shutdown()
487
self.response_mqs = []
488
489
+ logger.debug_once("[shutdown] Executor: complete")
490
+
491
def check_health(self) -> None:
492
self.collective_rpc("check_health", timeout=10)
493
return
@@ -867,7 +887,9 @@ class WorkerProc:
887
if ready_writer is not None:
888
logger.exception("WorkerProc failed to start.")
889
elif shutdown_requested.is_set():
870
- logger.info("WorkerProc shutting down.")
890
+ logger.debug_once(
891
+ "[shutdown] WorkerProc: exiting after shutdown request"
892
+ )
893
else:
894
logger.exception("WorkerProc failed.")
895
@@ -879,7 +901,12 @@ class WorkerProc:
901
except SystemExit as e:
902
# SystemExit is raised on SIGTERM or SIGKILL, which usually indicates that
903
# the graceful shutdown process did not succeed
882
- logger.warning("WorkerProc was terminated")
904
+ if shutdown_requested.is_set():
905
+ logger.debug_once(
906
+ "[shutdown] WorkerProc: terminated by shutdown signal"
907
+ )
908
+ else:
909
+ logger.warning("WorkerProc was terminated")
910
# SystemExit must never be ignored
911
raise e
912
vllm/v1/utils.py
+36
-6
@@ -444,6 +444,12 @@ def _shutdown_subprocesses(
444
timeout = 0.0
445
timeout = max(timeout, 5.0)
446
447
+ logger.debug(
448
+ "[shutdown] Subprocess manager: start process_count=%d timeout=%ss",
449
+ len(procs),
450
+ timeout,
451
+ )
452
+
453
for proc in procs:
454
if proc.is_alive():
455
proc.terminate()
@@ -456,9 +462,18 @@ def _shutdown_subprocesses(
462
if proc.is_alive():
463
proc.join(remaining)
464
459
- for proc in procs:
460
- if proc.is_alive() and (pid := proc.pid) is not None:
461
- kill_process_tree(pid)
465
+ remaining_pids = [
466
+ proc.pid for proc in procs if proc.is_alive() and proc.pid is not None
467
+ ]
468
+ if remaining_pids:
469
+ logger.warning(
470
+ "[shutdown] Subprocess manager: force killing remaining processes count=%d",
471
+ len(remaining_pids),
472
+ )
473
+ for pid in remaining_pids:
474
+ kill_process_tree(pid)
475
+
476
+ logger.debug_once("[shutdown] Subprocess manager: complete")
477
478
479
def run_api_server_worker_proc(
@@ -565,6 +580,12 @@ def shutdown(procs: list[BaseProcess], timeout: float | None = None) -> None:
580
# have a user-configured shutdown timeout.
581
timeout = 5.0
582
583
+ logger.debug(
584
+ "[shutdown] Process manager: start process_count=%d timeout=%ss",
585
+ len(procs),
586
+ timeout,
587
+ )
588
+
589
# Shutdown the process.
590
for proc in procs:
591
if proc.is_alive():
@@ -579,9 +600,18 @@ def shutdown(procs: list[BaseProcess], timeout: float | None = None) -> None:
600
if proc.is_alive():
601
proc.join(remaining)
602
582
- for proc in procs:
583
- if proc.is_alive() and (pid := proc.pid) is not None:
584
- kill_process_tree(pid)
603
+ remaining_pids = [
604
+ proc.pid for proc in procs if proc.is_alive() and proc.pid is not None
605
+ ]
606
+ if remaining_pids:
607
+ logger.warning(
608
+ "[shutdown] Process manager: force killing remaining processes count=%d",
609
+ len(remaining_pids),
610
+ )
611
+ for pid in remaining_pids:
612
+ kill_process_tree(pid)
613
+
614
+ logger.debug_once("[shutdown] Process manager: complete")
615
616
617
def copy_slice(