feat(monitor): 顶部栏增加磁盘占用,格式与内存一致
采集根分区(/)容量,按 DISK x% · x/x GB · Free x GB 拼入展示文本, Stats 新增 disk_used_pct / disk_total_bytes / disk_available_bytes 字段。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f576b03315
commit
a3075133d1
@ -6,7 +6,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sysinfo::System;
|
use sysinfo::{Disks, System};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
@ -26,6 +26,12 @@ pub struct Stats {
|
|||||||
pub mem_total_bytes: u64,
|
pub mem_total_bytes: u64,
|
||||||
/// 可用内存(字节)
|
/// 可用内存(字节)
|
||||||
pub mem_available_bytes: u64,
|
pub mem_available_bytes: u64,
|
||||||
|
/// 磁盘使用率(百分比,0–100)
|
||||||
|
pub disk_used_pct: f32,
|
||||||
|
/// 磁盘总量(字节)
|
||||||
|
pub disk_total_bytes: u64,
|
||||||
|
/// 可用磁盘(字节)
|
||||||
|
pub disk_available_bytes: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stats {
|
impl Default for Stats {
|
||||||
@ -36,6 +42,9 @@ impl Default for Stats {
|
|||||||
mem_used_pct: 0.0,
|
mem_used_pct: 0.0,
|
||||||
mem_total_bytes: 0,
|
mem_total_bytes: 0,
|
||||||
mem_available_bytes: 0,
|
mem_available_bytes: 0,
|
||||||
|
disk_used_pct: 0.0,
|
||||||
|
disk_total_bytes: 0,
|
||||||
|
disk_available_bytes: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,9 +53,22 @@ fn bytes_to_gb(bytes: u64) -> f64 {
|
|||||||
bytes as f64 / 1024.0 / 1024.0 / 1024.0
|
bytes as f64 / 1024.0 / 1024.0 / 1024.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 选出要展示的磁盘容量:优先根分区 `/`(Linux 线上环境),否则取容量最大的那块
|
||||||
|
/// (兜底 Windows/本地等无 `/` 的情况)。返回 `(总字节, 可用字节)`;无磁盘时 `(0, 0)`。
|
||||||
|
fn root_disk(disks: &Disks) -> (u64, u64) {
|
||||||
|
let chosen = disks
|
||||||
|
.iter()
|
||||||
|
.find(|d| d.mount_point() == std::path::Path::new("/"))
|
||||||
|
.or_else(|| disks.iter().max_by_key(|d| d.total_space()));
|
||||||
|
match chosen {
|
||||||
|
Some(d) => (d.total_space(), d.available_space()),
|
||||||
|
None => (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
/// 从已刷新的 `System` 读出指标并格式化。
|
/// 从已刷新的 `System` + `Disks` 读出指标并格式化。
|
||||||
fn from_system(sys: &System) -> Self {
|
fn from_system(sys: &System, disks: &Disks) -> Self {
|
||||||
let cpu = sys.global_cpu_usage();
|
let cpu = sys.global_cpu_usage();
|
||||||
let total = sys.total_memory();
|
let total = sys.total_memory();
|
||||||
let available = sys.available_memory();
|
let available = sys.available_memory();
|
||||||
@ -57,13 +79,25 @@ impl Stats {
|
|||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (disk_total, disk_available) = root_disk(disks);
|
||||||
|
let disk_used = disk_total.saturating_sub(disk_available);
|
||||||
|
let disk_used_pct = if disk_total > 0 {
|
||||||
|
disk_used as f32 / disk_total as f32 * 100.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"CPU {:.1}% · MEM {:.1}% · {:.1}/{:.1} GB · Free {:.1} GB",
|
"CPU {:.1}% · MEM {:.1}% · {:.1}/{:.1} GB · Free {:.1} GB · DISK {:.1}% · {:.1}/{:.1} GB · Free {:.1} GB",
|
||||||
cpu,
|
cpu,
|
||||||
mem_used_pct,
|
mem_used_pct,
|
||||||
bytes_to_gb(used),
|
bytes_to_gb(used),
|
||||||
bytes_to_gb(total),
|
bytes_to_gb(total),
|
||||||
bytes_to_gb(available),
|
bytes_to_gb(available),
|
||||||
|
disk_used_pct,
|
||||||
|
bytes_to_gb(disk_used),
|
||||||
|
bytes_to_gb(disk_total),
|
||||||
|
bytes_to_gb(disk_available),
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -72,6 +106,9 @@ impl Stats {
|
|||||||
mem_used_pct,
|
mem_used_pct,
|
||||||
mem_total_bytes: total,
|
mem_total_bytes: total,
|
||||||
mem_available_bytes: available,
|
mem_available_bytes: available,
|
||||||
|
disk_used_pct,
|
||||||
|
disk_total_bytes: disk_total,
|
||||||
|
disk_available_bytes: disk_available,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,6 +119,8 @@ impl Stats {
|
|||||||
/// `MINIMUM_CPU_UPDATE_INTERVAL`),之后循环里的读数才准确。
|
/// `MINIMUM_CPU_UPDATE_INTERVAL`),之后循环里的读数才准确。
|
||||||
pub async fn run_sampler(state: AppState) {
|
pub async fn run_sampler(state: AppState) {
|
||||||
let mut sys = System::new();
|
let mut sys = System::new();
|
||||||
|
// 磁盘容量变化很慢,但每轮 refresh 一下也很廉价,省得单独计时。
|
||||||
|
let mut disks = Disks::new_with_refreshed_list();
|
||||||
|
|
||||||
sys.refresh_cpu_usage();
|
sys.refresh_cpu_usage();
|
||||||
sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
|
sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
|
||||||
@ -89,8 +128,9 @@ pub async fn run_sampler(state: AppState) {
|
|||||||
loop {
|
loop {
|
||||||
sys.refresh_cpu_usage();
|
sys.refresh_cpu_usage();
|
||||||
sys.refresh_memory();
|
sys.refresh_memory();
|
||||||
|
disks.refresh(true);
|
||||||
|
|
||||||
let stats = Stats::from_system(&sys);
|
let stats = Stats::from_system(&sys, &disks);
|
||||||
*state.snapshot.write().await = stats;
|
*state.snapshot.write().await = stats;
|
||||||
|
|
||||||
sleep(Duration::from_secs(1)).await;
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user