use std::time::{Duration, Instant};
use tokio::time::interval;
use tracing::{debug, error, warn};
use crate::prometheus::MetricsRegistry;
pub fn start_system_metrics_collector(
registry: &'static MetricsRegistry,
collection_interval: Duration,
) -> tokio::task::JoinHandle<()> {
let start_time = Instant::now();
tokio::spawn(async move {
let mut ticker = interval(collection_interval);
debug!("System metrics collector started (interval: {:?})", collection_interval);
loop {
ticker.tick().await;
if let Err(e) = collect_and_update_metrics(registry, start_time).await {
error!("Failed to collect system metrics: {}", e);
}
}
})
}
async fn collect_and_update_metrics(
registry: &MetricsRegistry,
start_time: Instant,
) -> Result<(), Box<dyn std::error::Error>> {
let uptime_seconds = start_time.elapsed().as_secs_f64();
registry.update_uptime(uptime_seconds);
#[cfg(feature = "sysinfo")]
{
use sysinfo::System;
let mut sys = System::new_all();
sys.refresh_all();
let memory_used = sys.used_memory() as f64;
registry.update_memory_usage(memory_used);
let cpu_usage = sys.global_cpu_usage() as f64;
registry.update_cpu_usage(cpu_usage);
debug!(
"System metrics updated - Memory: {:.2} MB, CPU: {:.2}%, Uptime: {:.2}s",
memory_used / 1024.0 / 1024.0,
cpu_usage,
uptime_seconds
);
}
#[cfg(target_os = "linux")]
{
if let Ok(thread_count) = get_thread_count_linux() {
registry.update_thread_count(thread_count as f64);
}
}
#[cfg(not(target_os = "linux"))]
{
if let Ok(parallelism) = std::thread::available_parallelism() {
registry.update_thread_count(parallelism.get() as f64);
}
}
Ok(())
}
#[cfg(target_os = "linux")]
fn get_thread_count_linux() -> Result<usize, std::io::Error> {
use std::fs;
let status = fs::read_to_string("/proc/self/status")?;
for line in status.lines() {
if line.starts_with("Threads:") {
if let Some(count_str) = line.split_whitespace().nth(1) {
if let Ok(count) = count_str.parse::<usize>() {
return Ok(count);
}
}
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Thread count not found in /proc/self/status",
))
}
#[derive(Debug, Clone)]
pub struct SystemMetricsConfig {
pub enabled: bool,
pub interval_seconds: u64,
}
impl Default for SystemMetricsConfig {
fn default() -> Self {
Self {
enabled: true,
interval_seconds: 15,
}
}
}
pub fn start_with_config(
registry: &'static MetricsRegistry,
config: SystemMetricsConfig,
) -> Option<tokio::task::JoinHandle<()>> {
if config.enabled {
debug!("Starting system metrics collector with {:?}", config);
Some(start_system_metrics_collector(
registry,
Duration::from_secs(config.interval_seconds),
))
} else {
warn!("System metrics collection is disabled");
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prometheus::MetricsRegistry;
#[tokio::test]
async fn test_system_metrics_collection() {
let registry = MetricsRegistry::new();
let start_time = Instant::now();
let result = collect_and_update_metrics(®istry, start_time).await;
assert!(result.is_ok());
assert!(registry.uptime_seconds.get() > 0.0);
}
#[test]
fn test_system_metrics_config_default() {
let config = SystemMetricsConfig::default();
assert!(config.enabled);
assert_eq!(config.interval_seconds, 15);
}
#[cfg(target_os = "linux")]
#[test]
fn test_get_thread_count_linux() {
let result = get_thread_count_linux();
assert!(result.is_ok());
let count = result.unwrap();
assert!(count > 0);
}
}