use crate::telemetry::metrics::{Gauge, GaugeExt, Register, raw};
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,
#[cfg(all(
feature = "mimalloc",
not(any(
commonware_stability_BETA,
commonware_stability_GAMMA,
commonware_stability_DELTA,
commonware_stability_EPSILON,
commonware_stability_RESERVED
))
))]
allocator: Option<super::allocator::mimalloc::Metrics>,
}
impl Metrics {
pub fn init(registry: &mut impl Register) -> Self {
Self {
pid: sysinfo::Pid::from_u32(std::process::id()),
rss: registry.register(
"process_rss",
"Resident set size of the current process",
raw::Gauge::default(),
),
virtual_memory: registry.register(
"process_virtual_memory",
"Virtual memory size of the current process",
raw::Gauge::default(),
),
system: System::new(),
#[cfg(all(
feature = "mimalloc",
not(any(
commonware_stability_BETA,
commonware_stability_GAMMA,
commonware_stability_DELTA,
commonware_stability_EPSILON,
commonware_stability_RESERVED
))
))]
allocator: super::allocator::mimalloc::Metrics::init(registry),
}
}
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());
}
#[cfg(all(
feature = "mimalloc",
not(any(
commonware_stability_BETA,
commonware_stability_GAMMA,
commonware_stability_DELTA,
commonware_stability_EPSILON,
commonware_stability_RESERVED
))
))]
if let Some(allocator) = &mut self.allocator {
allocator.update();
}
}
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)]
mod tests {
use super::*;
#[test]
fn test_process_metrics_init() {
let mut registry = crate::telemetry::metrics::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})"
);
}
#[cfg(all(
feature = "mimalloc",
not(any(
commonware_stability_BETA,
commonware_stability_GAMMA,
commonware_stability_DELTA,
commonware_stability_EPSILON,
commonware_stability_RESERVED
))
))]
#[test]
fn test_allocator_metrics_skipped_without_mimalloc() {
let mut registry = crate::telemetry::metrics::Registry::default();
let mut metrics = Metrics::init(&mut registry);
assert!(metrics.allocator.is_none());
metrics.update();
assert!(!registry.encode().contains("allocator_committed_bytes"));
}
}