Skip to main content

hashiverse_server_lib/server/stats/
system_stats.rs

1use sysinfo::{Disks, LoadAvg, System};
2
3/// Build the `system` subtree: process- and host-level metrics that don't
4/// belong to any single subsystem. Numbers only; never asserts a specific
5/// value, since these vary per host.
6///
7/// Disk fields sum across all disks the OS exposes. Load average is reported
8/// as three floats; on platforms that don't compute load averages (notably
9/// Windows, where sysinfo returns `Err(Unsupported)`) the fields are present but zero.
10/// Likewise, any metric sysinfo fails to read is reported as zero rather than
11/// failing the whole stats response.
12pub fn system_stats_subtree() -> serde_json::Value {
13    let (memory_total_bytes, memory_free_bytes) = match System::new() {
14        Ok(mut system) => {
15            system.refresh_memory();
16            (system.total_memory(), system.available_memory())
17        }
18        Err(_) => (0, 0),
19    };
20
21    let mut disk_total_bytes: u64 = 0;
22    let mut disk_free_bytes: u64 = 0;
23    if let Ok(disks) = Disks::new_with_refreshed_list() {
24        for disk in &disks {
25            disk_total_bytes = disk_total_bytes.saturating_add(disk.total_space());
26            disk_free_bytes = disk_free_bytes.saturating_add(disk.available_space());
27        }
28    }
29
30    let load = System::load_average().unwrap_or(LoadAvg { one: 0.0, five: 0.0, fifteen: 0.0 });
31
32    serde_json::json!({
33        "memory_total_bytes": memory_total_bytes,
34        "memory_free_bytes": memory_free_bytes,
35        "disk_total_bytes": disk_total_bytes,
36        "disk_free_bytes": disk_free_bytes,
37        "load_1m": load.one,
38        "load_5m": load.five,
39        "load_15m": load.fifteen,
40    })
41}
42
43#[cfg(test)]
44mod tests {
45    use super::system_stats_subtree;
46
47    #[test]
48    fn system_stats_subtree_has_all_numeric_fields() {
49        let system_stats = system_stats_subtree();
50        for field in ["memory_total_bytes", "memory_free_bytes", "disk_total_bytes", "disk_free_bytes", "load_1m", "load_5m", "load_15m"] {
51            assert!(system_stats[field].is_number(), "field {} missing or not numeric: {}", field, system_stats);
52        }
53        // Totals may legitimately be 0 (the documented fallback when sysinfo can't read a metric, e.g. containers
54        // without listed disks), so only check invariants that hold for both real values and the 0/0 fallback.
55        assert!(system_stats["memory_free_bytes"].as_u64().unwrap() <= system_stats["memory_total_bytes"].as_u64().unwrap(), "free memory exceeds total: {}", system_stats);
56        assert!(system_stats["disk_free_bytes"].as_u64().unwrap() <= system_stats["disk_total_bytes"].as_u64().unwrap(), "free disk exceeds total: {}", system_stats);
57    }
58}