fn live_chart_system_payload(state: &App) -> Value { let view = state.diagnostics.view(); json!({ "sampled_at_ms": view.sampled_at_ms, "cpu_percent": view.cpu_percent, "process_cpu_percent": view.process_cpu_percent, "process_rss_bytes": view.process_rss_bytes, "network_rx_bytes_per_second": view.network_rx_bytes_per_second, "network_tx_bytes_per_second": view.network_tx_bytes_per_second, "network_interfaces": view.network_interfaces }) } fn web_server_metrics_payload(snapshot: &crate::config::Snapshot) -> Value { let mut services = serde_json::Map::new(); for service in &snapshot.envelope.config.web_services { let Some(compiled) = snapshot.web_php_services.get(&service.id) else { continue; }; let php = compiled.php.as_ref().map(|handler| { let available = handler.permits.available_permits(); json!({"metrics": handler.metrics.view(), "capacity_limit": handler.max_concurrent, "capacity_available": available, "capacity_used": handler.max_concurrent.saturating_sub(available)}) }); services.insert( service.id.clone(), json!({"web": compiled.web_metrics.view(), "php": php}), ); } Value::Object(services) } fn live_status_payload(state: &App) -> Value { let snapshot = state.config.load_full(); let stream_metrics = state .active_streams .iter() .filter_map(|stream| { state .stream_metrics .get(&stream.id) .map(|metrics| (stream.id.clone(), metrics.view())) }) .collect::>(); json!({ "version": env!("CARGO_PKG_VERSION"), "uptime_seconds": state.started.elapsed().as_secs(), "revision": snapshot.envelope.revision, "listeners": { "active_endpoints": state.active_listeners, "desired_endpoints": snapshot.listeners, "restart_required": crate::http3::listeners_require_restart(&state.active_listeners, &snapshot.listeners) }, "metrics": state.metrics.view(), "web_server_metrics": web_server_metrics_payload(&snapshot), "live_system": live_chart_system_payload(state), "cache": state.cache.view(), "transform_metrics": state.transform_metrics.total(), "logs_dropped": state.logs.dropped.load(Ordering::Relaxed), "backends": snapshot.envelope.config.vhosts.iter().map(|v| v.backends.len()).sum::(), "backend_pools": snapshot.pools.iter().map(|p| p.view()).collect::>(), "vhost_metrics": snapshot.vhost_metrics.iter().map(|(id, metrics)| (id.clone(), metrics.view())).collect::>(), "streams": {"metrics": stream_metrics}, "logging": {"sinks": state.logs.stats(), "error_file": crate::logging::error_log_stats()} }) } #[derive(Default, Deserialize)] #[serde(default, deny_unknown_fields)] struct StatusQuery { view: Option, } async fn status( State(state): State>, Query(query): Query, ) -> Result, ApiError> { match query.view.as_deref() { None => Ok(Json(status_payload(&state))), Some("live") => Ok(Json(live_status_payload(&state))), _ => Err(ApiError( StatusCode::BAD_REQUEST, "invalid status view".into(), )), } } async fn system_info(State(state): State>) -> Json { Json(system_info_payload(&state)) }