1use std::time::Duration;
2
3use async_channel::Sender;
4use ferron_common::observability::{Metric, MetricAttributeValue, MetricType, MetricValue};
5
6pub async fn background_metrics(metrics_channels: Vec<Sender<Metric>>, parallelism: usize) {
8 let mut previous_instant = std::time::Instant::now();
9 let mut previous_cpu_user_time = 0.0;
10 let mut previous_cpu_system_time = 0.0;
11 let mut previous_rss = 0;
12 let mut previous_vms = 0;
13 loop {
14 tokio::time::sleep(Duration::from_secs(1)).await;
16
17 if let Ok(Ok(stat)) =
18 tokio::task::spawn_blocking(|| procfs::process::Process::myself().and_then(|p| p.stat())).await
19 {
20 let cpu_user_time = stat.utime as f64 / procfs::ticks_per_second() as f64;
21 let cpu_system_time = stat.stime as f64 / procfs::ticks_per_second() as f64;
22 let cpu_user_time_increase = cpu_user_time - previous_cpu_user_time;
23 let cpu_system_time_increase = cpu_system_time - previous_cpu_system_time;
24 previous_cpu_user_time = cpu_user_time;
25 previous_cpu_system_time = cpu_system_time;
26
27 let rss = stat.rss * procfs::page_size();
28 let rss_diff = rss as i64 - previous_rss as i64;
29 let vms_diff = stat.vsize as i64 - previous_vms as i64;
30 previous_rss = rss;
31 previous_vms = stat.vsize;
32
33 let elapsed = previous_instant.elapsed().as_secs_f64();
34 previous_instant = std::time::Instant::now();
35
36 let cpu_user_utilization = cpu_user_time_increase / (elapsed * parallelism as f64);
37 let cpu_system_utilization = cpu_system_time_increase / (elapsed * parallelism as f64);
38
39 for metrics_sender in &metrics_channels {
40 metrics_sender
41 .send(Metric::new(
42 "process.cpu.time",
43 vec![("cpu.mode", MetricAttributeValue::String("user".to_string()))],
44 MetricType::Counter,
45 MetricValue::F64(cpu_user_time_increase),
46 Some("s"),
47 Some("Total CPU seconds broken down by different states."),
48 ))
49 .await
50 .unwrap_or_default();
51
52 metrics_sender
53 .send(Metric::new(
54 "process.cpu.time",
55 vec![("cpu.mode", MetricAttributeValue::String("system".to_string()))],
56 MetricType::Counter,
57 MetricValue::F64(cpu_system_time_increase),
58 Some("s"),
59 Some("Total CPU seconds broken down by different states."),
60 ))
61 .await
62 .unwrap_or_default();
63
64 metrics_sender
65 .send(Metric::new(
66 "process.cpu.utilization",
67 vec![("cpu.mode", MetricAttributeValue::String("user".to_string()))],
68 MetricType::Gauge,
69 MetricValue::F64(cpu_user_utilization),
70 Some("1"),
71 Some(
72 "Difference in process.cpu.time since the last measurement, \
73 divided by the elapsed time and number of CPUs available to the process.",
74 ),
75 ))
76 .await
77 .unwrap_or_default();
78
79 metrics_sender
80 .send(Metric::new(
81 "process.cpu.utilization",
82 vec![("cpu.mode", MetricAttributeValue::String("system".to_string()))],
83 MetricType::Gauge,
84 MetricValue::F64(cpu_system_utilization),
85 Some("1"),
86 Some(
87 "Difference in process.cpu.time since the last measurement, \
88 divided by the elapsed time and number of CPUs available to the process.",
89 ),
90 ))
91 .await
92 .unwrap_or_default();
93
94 metrics_sender
95 .send(Metric::new(
96 "process.memory.usage",
97 vec![],
98 MetricType::UpDownCounter,
99 MetricValue::I64(rss_diff),
100 Some("By"),
101 Some("The amount of physical memory in use."),
102 ))
103 .await
104 .unwrap_or_default();
105
106 metrics_sender
107 .send(Metric::new(
108 "process.memory.virtual",
109 vec![],
110 MetricType::UpDownCounter,
111 MetricValue::I64(vms_diff),
112 Some("By"),
113 Some("The amount of committed virtual memory."),
114 ))
115 .await
116 .unwrap_or_default();
117 }
118 }
119 }
120}