use std::time::Duration;
use tokio::runtime::Handle;
const RUNTIME_METRICS_ENV: &str = "VTCODE_RUNTIME_METRICS";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RuntimeSnapshot {
pub workers_len: usize,
pub alive_tasks_len: usize,
pub global_queue_depth_len: usize,
pub total_busy: Duration,
}
#[must_use]
pub fn runtime_diagnostics_enabled() -> bool {
env_flag_enabled(RUNTIME_METRICS_ENV) || startup_trace_enabled()
}
fn env_flag_enabled(var_name: &str) -> bool {
std::env::var(var_name).ok().is_some_and(|value| {
matches!(value.trim().to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on" | "debug")
})
}
fn startup_trace_enabled() -> bool {
std::env::var("VTCODE_STARTUP_TRACE").is_ok_and(|value| value == "1")
}
const RUNTIME_WORKERS_ENV: &str = "VTCODE_RUNTIME_WORKERS";
#[must_use]
pub fn configured_worker_threads() -> Option<usize> {
std::env::var(RUNTIME_WORKERS_ENV)
.ok()
.and_then(|value| value.trim().parse::<usize>().ok())
.filter(|workers| *workers > 0)
}
#[must_use]
pub fn snapshot(handle: &Handle) -> RuntimeSnapshot {
let metrics = handle.metrics();
RuntimeSnapshot {
workers_len: metrics.num_workers(),
alive_tasks_len: metrics.num_alive_tasks(),
global_queue_depth_len: metrics.global_queue_depth(),
total_busy: total_busy(&metrics),
}
}
#[cfg(target_has_atomic = "64")]
fn total_busy(metrics: &tokio::runtime::RuntimeMetrics) -> Duration {
let mut busy = Duration::ZERO;
for worker in 0..metrics.num_workers() {
busy = busy.saturating_add(metrics.worker_total_busy_duration(worker));
}
busy
}
#[cfg(not(target_has_atomic = "64"))]
fn total_busy(_metrics: &tokio::runtime::RuntimeMetrics) -> Duration {
Duration::ZERO
}
pub fn log_snapshot(handle: &Handle, context: &str) {
if !runtime_diagnostics_enabled() {
return;
}
let snapshot = snapshot(handle);
let worker_busy_ms = u64::try_from(snapshot.total_busy.as_millis()).unwrap_or(u64::MAX);
tracing::debug!(
target = "vtcode.runtime",
context,
workers = snapshot.workers_len,
alive_tasks = snapshot.alive_tasks_len,
global_queue_depth = snapshot.global_queue_depth_len,
worker_busy_ms,
"tokio runtime snapshot"
);
}
const REPORT_INTERVAL: Duration = Duration::from_secs(60);
pub fn spawn_periodic_reporter(handle: &Handle) -> Option<tokio::task::JoinHandle<()>> {
if !runtime_diagnostics_enabled() {
return None;
}
Some(spawn_reporter(handle, REPORT_INTERVAL))
}
fn spawn_reporter(handle: &Handle, interval: Duration) -> tokio::task::JoinHandle<()> {
let metrics_handle = handle.clone();
handle.spawn(async move {
let mut ticker = tokio::time::interval(interval);
ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let _ = ticker.tick().await;
loop {
let _ = ticker.tick().await;
log_snapshot(&metrics_handle, "periodic");
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn absent_env_flag_is_disabled() {
assert!(!env_flag_enabled("VTCODE_RUNTIME_METRICS_TEST_ABSENT_XYZ"));
}
#[test]
fn absent_worker_override_uses_default() {
if std::env::var("VTCODE_RUNTIME_WORKERS").is_err() {
assert_eq!(configured_worker_threads(), None);
}
}
#[tokio::test]
async fn snapshot_reflects_current_runtime() {
let snapshot = snapshot(&Handle::current());
assert!(snapshot.workers_len >= 1);
}
#[tokio::test]
async fn log_snapshot_is_quiet_when_disabled() {
log_snapshot(&Handle::current(), "test");
}
#[test]
fn reporter_spawns_outside_ambient_runtime_context() {
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.expect("current-thread runtime");
let reporter = spawn_reporter(runtime.handle(), Duration::from_secs(3600));
reporter.abort();
}
}