yggdrasil/src/sysinfo_sampler.rs
xfy 5426458f4a
Some checks failed
CI / check (push) Failing after 5m18s
CI / build (push) Has been skipped
fix(docs): resolve rustdoc broken links and dead_code warnings
doc 构建报 8 个 broken_intra_doc_links + dx 构建报 7 个 dead_code 警告。
两者根源相同:被引用的常量/类型/模块要么是 server-only(消费方都在
#[cfg(feature="server")] 块里),要么是跨 cfg 引用(host doc 构建看不到
wasm32-only 类型)。

dead_code:给 sql_console 的 MAX_ROWS/ABSOLUTELY_FORBIDDEN/GuardResult 与
backup 的 BACKUP_DIR/FILENAME_RE/BACKUP_SIGNATURE 加 #[cfg(feature="server")]
gate——这些都是安全护栏常量,消费方全在 server fn 体里,WASM 构建剥掉 fn 体后
常量即成死代码。sysinfo_sampler 的 WASM 桩 read_snapshot 加 allow(dead_code)
(与 codemirror_bridge 既有处理一致)。

rustdoc 链接:跨模块引用改全路径(crate::xxx),跨 cfg 引用(EditorHandle 在
wasm32 mod 内、host doc 构建不可见)转义为纯文本。

验证:cargo doc(0 warning)+ cargo check(default)+ wasm32 check 全部干净。
2026-06-30 17:42:52 +08:00

108 lines
3.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! sysinfo 主机指标后台采样。
//!
//! sysinfo 的 CPU% **不是即时值**,需要两次采样间的 delta。因此用后台任务周期
//! 刷新server function 只读 [`tokio::sync::RwLock`] 快照(毫秒级返回,零采样成本)。
//!
//! 采样间隔由环境变量 `SYSINFO_SAMPLE_SECS` 配置(默认 0.5 秒,支持小数如 0.1)。
// LazyLock / Duration 仅 server 构建的采样任务用到WASM 端只序列化 SystemSnapshot。
#[cfg(feature = "server")]
use std::sync::LazyLock;
#[cfg(feature = "server")]
use std::time::Duration;
use serde::{Deserialize, Serialize};
/// 主机指标快照(由后台采样任务周期更新)。
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct SystemSnapshot {
/// 总体 CPU 使用率(百分比)。
pub cpu_usage: f32,
/// 系统 1 分钟平均负载。
pub load_avg_1: f64,
/// 总物理内存(字节)。
pub total_memory: u64,
/// 已用物理内存(字节)。
pub used_memory: u64,
/// 主磁盘总空间(字节,取根分区或最大盘)。
pub disk_total: u64,
/// 主磁盘可用空间(字节)。
pub disk_available: u64,
/// 操作系统版本(如 "macOS 15.5")。
pub os_name: String,
/// 内核版本。
pub kernel_version: String,
/// 系统启动后秒数。
pub uptime_secs: u64,
}
#[cfg(feature = "server")]
static SNAPSHOT: LazyLock<tokio::sync::RwLock<SystemSnapshot>> =
LazyLock::new(|| tokio::sync::RwLock::new(SystemSnapshot::default()));
/// 采样间隔,由环境变量 `SYSINFO_SAMPLE_SECS` 配置,默认 0.5 秒,下限 0.05 秒。
#[cfg(feature = "server")]
fn sample_interval() -> Duration {
let secs = std::env::var("SYSINFO_SAMPLE_SECS")
.ok()
.and_then(|s| s.parse::<f64>().ok())
.unwrap_or(0.5);
Duration::from_secs_f64(secs.max(0.05))
}
/// 启动后台采样任务。应在 main 启动流程migrate 之后、serve 之前)调用一次。
///
/// CPU% 需两次采样 delta故循环里先 refresh 再 sleep 再 refresh 才有有效值。
#[cfg(feature = "server")]
pub fn spawn_sampler() {
tokio::spawn(async move {
use sysinfo::{Disks, System};
let mut sys = System::new();
let interval = sample_interval();
// 首次 refresh 建立基线CPU% 在下一轮才有意义。
sys.refresh_cpu_usage();
let disks = Disks::new_with_refreshed_list();
loop {
tokio::time::sleep(interval).await;
sys.refresh_cpu_usage();
sys.refresh_memory();
let load = System::load_average();
// 主磁盘:取空间最大的盘(通常是数据盘)。
let (disk_total, disk_available) = disks
.list()
.iter()
.max_by_key(|d| d.total_space())
.map(|d| (d.total_space(), d.available_space()))
.unwrap_or((0, 0));
let snap = SystemSnapshot {
cpu_usage: sys.global_cpu_usage(),
load_avg_1: load.one,
total_memory: sys.total_memory(),
used_memory: sys.used_memory(),
disk_total,
disk_available,
os_name: System::long_os_version().unwrap_or_default(),
kernel_version: System::kernel_version().unwrap_or_default(),
uptime_secs: System::uptime(),
};
*SNAPSHOT.write().await = snap;
}
});
}
/// 读取最新快照(只读,毫秒级返回,不触发采样)。
#[cfg(feature = "server")]
pub async fn read_snapshot() -> SystemSnapshot {
SNAPSHOT.read().await.clone()
}
#[cfg(not(feature = "server"))]
#[allow(dead_code)]
pub async fn read_snapshot() -> SystemSnapshot {
SystemSnapshot::default()
}