use crate::telemetry::metrics::status::GaugeExt;
use prometheus_client::{metrics::gauge::Gauge, registry::Registry};
use std::{future::Future, time::Duration};
use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, System};
const TICK_INTERVAL: Duration = Duration::from_secs(10);
pub struct Metrics {
pub rss: Gauge,
pub virtual_memory: Gauge,
pid: sysinfo::Pid,
system: System,
}
impl Metrics {
pub fn init(registry: &mut Registry) -> Self {
let metrics = Self {
pid: sysinfo::Pid::from_u32(std::process::id()),
rss: Gauge::default(),
virtual_memory: Gauge::default(),
system: System::new(),
};
registry.register(
"process_rss",
"Resident set size of the current process",
metrics.rss.clone(),
);
registry.register(
"process_virtual_memory",
"Virtual memory size of the current process",
metrics.virtual_memory.clone(),
);
metrics
}
fn update(&mut self) {
self.system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[self.pid]),
false,
ProcessRefreshKind::nothing().with_memory(),
);
if let Some(process) = self.system.process(self.pid) {
let _ = self.rss.try_set(process.memory());
let _ = self.virtual_memory.try_set(process.virtual_memory());
}
}
pub async fn collect<F, Fut>(mut self, sleep_fn: F)
where
F: Fn(Duration) -> Fut,
Fut: Future<Output = ()>,
{
loop {
self.update();
sleep_fn(TICK_INTERVAL).await;
}
}
}
#[cfg(test)]
#[cfg(not(target_os = "windows"))]
mod tests {
use super::*;
#[test]
fn test_process_metrics_init() {
let mut registry = Registry::default();
let mut metrics = Metrics::init(&mut registry);
metrics.update();
let rss = metrics.rss.get();
assert!(rss > 1024 * 1024); let virt = metrics.virtual_memory.get();
assert!(
virt >= rss,
"Expected virtual memory ({virt}) to be >= RSS ({rss})"
);
metrics.update();
let new_rss = metrics.rss.get();
assert!(new_rss > 1024 * 1024); let new_virt = metrics.virtual_memory.get();
assert!(
new_virt >= new_rss,
"Expected virtual memory ({new_virt}) to be >= RSS ({new_rss})"
);
}
}