[Rust Frontend] Improve startup failure and readiness logs (#50406)

Signed-off-by: Bugen Zhao <i@bugenzhao.com> Co-authored-by: OpenAI Codex <codex@openai.com>

Bugen Zhao committed Jul 31, 2026 at 05:18 UTC e9096fcb123181467a6fff5326fe8387d578760c
2 files changed +39 -12
rust/src/cmd/src/main.rs
+15 -6
@@ -5,11 +5,12 @@ mod cli;
5
6 use std::env;
7 use std::ffi::OsStr;
8 -use std::process::ExitStatus;
8 +use std::process::{ExitCode, ExitStatus};
9
10 use anyhow::{Context, Result, anyhow, bail};
11 +use thiserror_ext::AsReport as _;
12 use tokio_util::sync::CancellationToken;
12 -use tracing::{info, warn};
13 +use tracing::{error, info, warn};
14 use vllm_managed_engine::ManagedEngineHandle;
15
16 use crate::cli::{BenchCommand, Cli, Command};
@@ -81,7 +82,7 @@ fn shutdown_signal() -> CancellationToken {
82 token
83 }
84
84 -fn main() -> Result<()> {
85 +fn main() -> ExitCode {
86 let process_label =
87 match env::args_os().nth(1).as_deref().and_then(OsStr::to_str).unwrap_or_default() {
88 "bench" => "Bench",
@@ -98,10 +99,18 @@ fn main() -> Result<()> {
99 runtime.worker_threads(worker_threads);
100 }
101
101 - runtime
102 + let result = runtime
103 .build()
103 - .context("failed to build Tokio runtime")?
104 - .block_on(async_main(cli))
104 + .context("failed to build Tokio runtime")
105 + .and_then(|runtime| runtime.block_on(async_main(cli)));
106 +
107 + match result {
108 + Ok(()) => ExitCode::SUCCESS,
109 + Err(error) => {
110 + error!("process failed with error: {:#?}", error.as_report());
111 + ExitCode::FAILURE
112 + }
113 + }
114 }
115
116 async fn async_main(cli: Cli) -> Result<()> {
rust/src/server/src/lib.rs
+24 -6
@@ -181,12 +181,15 @@ where
181 result = build_state(&config) => result?,
182 _ = shutdown.cancelled() => return Ok(()),
183 };
184 + let model = state.primary_model_name().to_owned();
185 + let app = extend_router(build_router(state.clone()));
186 +
187 + info!(model, "starting vLLM server");
188 +
189 let listener = Listener::bind(&config.listener_mode)
190 .await
191 .context("failed to bind listener for OpenAI server")?;
192 let bind_address = listener.local_addr_display()?;
188 - let model = state.primary_model_name().to_owned();
189 - let app = extend_router(build_router(state.clone()));
193
194 // Optionally bind the gRPC Inference server on a separate port. Bind
195 // synchronously here so bind errors (port in use, permission denied, ...)
@@ -220,8 +223,14 @@ where
223 .add_service(health_service)
224 .add_service(control_service)
225 .add_service(inference_service);
223 - info!(%addr, tls = grpc_tls.is_some(), "starting gRPC server");
224 - Some((grpc_listener, svc, grpc_tls, health_reporter, engine_health))
226 + Some((
227 + addr,
228 + grpc_listener,
229 + svc,
230 + grpc_tls,
231 + health_reporter,
232 + engine_health,
233 + ))
234 } else {
235 None
236 };
@@ -231,7 +240,7 @@ where
240 } else {
241 "http"
242 };
234 - info!(%bind_address, %scheme, %model, "starting OpenAI server");
243 + let model = model.as_str();
244
245 // Run HTTP and gRPC concurrently under a child token of the caller's shutdown
246 // token. Caller cancellation propagates into both protocols; if either
@@ -285,6 +294,11 @@ where
294 };
295 let server = serve_connections(listener, app, shutdown.cancelled_owned(), timeouts);
296
297 + info!(
298 + bind_address,
299 + scheme, model, "OpenAI server is ready to accept requests"
300 + );
301 +
302 let result = tokio::select! {
303 result = server => {
304 result.context("HTTP server failed")
@@ -305,13 +319,15 @@ where
319 let server_shutdown = server_shutdown.clone();
320 let force_shutdown = force_shutdown.clone();
321 async move {
308 - let Some((grpc_listener, svc, grpc_tls, health_reporter, engine_health)) = grpc_setup
322 + let Some((addr, grpc_listener, svc, grpc_tls, health_reporter, engine_health)) =
323 + grpc_setup
324 else {
325 // No gRPC configured: just wait for shutdown so we do not race the
326 // join! by resolving early and tripping the cancellation token.
327 shutdown.cancelled().await;
328 return Ok(());
329 };
330 + let tls = grpc_tls.is_some();
331 let incoming = match grpc_tls {
332 Some(context) => MaybeTlsListener::tls(grpc_listener, context),
333 None => MaybeTlsListener::plain(grpc_listener),
@@ -320,6 +336,8 @@ where
336 svc.serve_with_incoming_shutdown(incoming, shutdown.clone().cancelled_owned());
337 let health_monitor = grpc::monitor_health(health_reporter, engine_health, shutdown);
338
339 + info!(%addr, tls, model, "gRPC server is ready to accept requests");
340 +
341 let server = async move {
342 let result = tokio::select! {
343 result = server => {