feat(telemetry): report process CPU and FDs plus host memory and load
This commit is contained in:
7 files changed
+301
-8
No files matched your search
@@ -1,6 +1,6 @@
|
||||
//! Linux procfs reader for core's OS-independent process/host instruments.
|
||||
use procfs::Current;
|
||||
use wakey_core::telemetry::ProcessMetricsSource;
|
||||
use wakey_core::telemetry::{HostLoad, HostMemory, ProcessMetricsSource};
|
||||
|
||||
/// Reads only this process and the visible host clock; never scans other PIDs.
|
||||
/// Constructing the reader does no I/O, so disabled OTLP remains local-only.
|
||||
@@ -16,6 +16,39 @@ impl ProcessMetricsSource for ProcfsProcessMetrics {
|
||||
fn host_uptime_seconds(&self) -> Option<f64> {
|
||||
host_uptime()
|
||||
}
|
||||
fn cpu_time_seconds(&self) -> Option<f64> {
|
||||
let stat = procfs::process::Process::myself().ok()?.stat().ok()?;
|
||||
let ticks = procfs::ticks_per_second();
|
||||
if ticks == 0 {
|
||||
return None;
|
||||
}
|
||||
// utime/stime account for all process threads, not child processes.
|
||||
Some(stat.utime.checked_add(stat.stime)? as f64 / ticks as f64)
|
||||
}
|
||||
fn open_file_descriptors(&self) -> Option<u64> {
|
||||
let process = procfs::process::Process::myself().ok()?;
|
||||
// procfs uses the kernel's count when available, otherwise enumerates
|
||||
// directory entries without reading every FD's target. Sampling itself
|
||||
// temporarily opens procfs descriptors, which can contribute to this count.
|
||||
u64::try_from(process.fd_count().ok()?).ok()
|
||||
}
|
||||
fn host_memory(&self) -> Option<HostMemory> {
|
||||
let memory = procfs::Meminfo::current().ok()?;
|
||||
// Unlike Status::vmrss, Meminfo has already converted its fields to bytes.
|
||||
// Do not substitute MemFree when an older kernel omits MemAvailable.
|
||||
Some(HostMemory {
|
||||
total_bytes: memory.mem_total,
|
||||
available_bytes: memory.mem_available,
|
||||
})
|
||||
}
|
||||
fn host_load(&self) -> Option<HostLoad> {
|
||||
let load = procfs::LoadAverage::current().ok()?;
|
||||
Some(HostLoad {
|
||||
one_minute: f64::from(load.one),
|
||||
five_minutes: f64::from(load.five),
|
||||
fifteen_minutes: f64::from(load.fifteen),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn host_uptime() -> Option<f64> {
|
||||
@@ -56,5 +89,19 @@ mod tests {
|
||||
assert!(age.is_finite() && age >= 0.0);
|
||||
assert!(host.is_finite() && host >= age);
|
||||
assert!(source.resident_memory_bytes().expect("VmRSS available") > 0);
|
||||
let cpu = source
|
||||
.cpu_time_seconds()
|
||||
.expect("process CPU ticks available");
|
||||
assert!(cpu.is_finite() && cpu >= 0.0);
|
||||
assert!(source.open_file_descriptors().expect("FD count available") > 0);
|
||||
let memory = source.host_memory().expect("host memory available");
|
||||
assert!(memory.total_bytes > 0);
|
||||
if let Some(available) = memory.available_bytes {
|
||||
assert!(available <= memory.total_bytes);
|
||||
}
|
||||
let load = source.host_load().expect("host load available");
|
||||
for average in [load.one_minute, load.five_minutes, load.fifteen_minutes] {
|
||||
assert!(average.is_finite() && average >= 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user