ferron/setup/
metrics.rs

1use std::time::Duration;
2
3use async_channel::Sender;
4use ferron_common::observability::{Metric, MetricAttributeValue, MetricType, MetricValue};
5
6/// Performs background periodic metrics collection.
7pub 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    // Sleep for 1 second
15    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}