hyperi_rustlib/metrics/
process.rs1use std::sync::Arc;
12use std::time::{SystemTime, UNIX_EPOCH};
13
14use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System};
15
16#[derive(Debug, Clone)]
18pub struct ProcessMetrics {
19 namespace: String,
20 system: Arc<std::sync::Mutex<System>>,
21 pid: sysinfo::Pid,
22 start_time: f64,
23}
24
25impl ProcessMetrics {
26 #[must_use]
28 pub fn new(namespace: &str) -> Self {
29 let pid = sysinfo::Pid::from_u32(std::process::id());
30 let system = System::new_with_specifics(
31 RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()),
32 );
33
34 let start_time = SystemTime::now()
35 .duration_since(UNIX_EPOCH)
36 .map_or(0.0, |d| d.as_secs_f64());
37
38 let this = Self {
39 namespace: namespace.to_string(),
40 system: Arc::new(std::sync::Mutex::new(system)),
41 pid,
42 start_time,
43 };
44
45 this.register_metrics();
47 this
48 }
49
50 fn register_metrics(&self) {
52 let ns = &self.namespace;
53
54 metrics::describe_gauge!(
55 format!("{ns}_process_cpu_seconds_total"),
56 "Total user and system CPU time spent in seconds".to_string()
57 );
58 metrics::describe_gauge!(
59 format!("{ns}_process_resident_memory_bytes"),
60 "Resident memory size in bytes".to_string()
61 );
62 metrics::describe_gauge!(
63 format!("{ns}_process_virtual_memory_bytes"),
64 "Virtual memory size in bytes".to_string()
65 );
66 metrics::describe_gauge!(
67 format!("{ns}_process_open_fds"),
68 "Number of open file descriptors".to_string()
69 );
70 metrics::describe_gauge!(
71 format!("{ns}_process_start_time_seconds"),
72 "Start time of the process since unix epoch in seconds".to_string()
73 );
74 }
75
76 pub fn update(&self) {
78 let mut system = self.system.lock().unwrap_or_else(|e| e.into_inner());
82 system.refresh_processes_specifics(
83 ProcessesToUpdate::Some(&[self.pid]),
84 true,
85 ProcessRefreshKind::everything(),
86 );
87
88 if let Some(process) = system.process(self.pid) {
89 let ns = &self.namespace;
90
91 let cpu_usage = f64::from(process.cpu_usage());
93 metrics::gauge!(format!("{ns}_process_cpu_seconds_total")).set(cpu_usage);
94
95 let rss = process.memory();
97 let virtual_mem = process.virtual_memory();
98 metrics::gauge!(format!("{ns}_process_resident_memory_bytes")).set(rss as f64);
99 metrics::gauge!(format!("{ns}_process_virtual_memory_bytes")).set(virtual_mem as f64);
100
101 #[cfg(target_os = "linux")]
103 {
104 if let Ok(fds) = count_open_fds() {
105 metrics::gauge!(format!("{ns}_process_open_fds")).set(fds as f64);
106 }
107 }
108
109 metrics::gauge!(format!("{ns}_process_start_time_seconds")).set(self.start_time);
111 }
112 }
113}
114
115#[cfg(target_os = "linux")]
117fn count_open_fds() -> std::io::Result<usize> {
118 let fd_dir = format!("/proc/{}/fd", std::process::id());
119 std::fs::read_dir(fd_dir).map(|entries| entries.count())
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_process_metrics_new() {
128 let pm = ProcessMetrics::new("test");
129 assert_eq!(pm.namespace, "test");
130 assert!(pm.start_time > 0.0);
131 }
132
133 #[test]
134 fn test_process_metrics_update() {
135 let pm = ProcessMetrics::new("test");
136 pm.update();
138 }
139}