vector/sources/host_metrics/
process.rs

1use super::{default_all_processes, example_processes, FilterList, HostMetrics};
2use std::ffi::OsStr;
3use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, UpdateKind};
4use vector_lib::configurable::configurable_component;
5#[cfg(target_os = "linux")]
6use vector_lib::metric_tags;
7
8/// Options for the process metrics collector.
9#[configurable_component]
10#[derive(Clone, Debug, Default)]
11pub struct ProcessConfig {
12    /// Lists of process name patterns to include or exclude.
13    #[serde(default = "default_all_processes")]
14    #[configurable(metadata(docs::examples = "example_processes()"))]
15    processes: FilterList,
16}
17
18const RUNTIME: &str = "process_runtime";
19const CPU_USAGE: &str = "process_cpu_usage";
20const MEMORY_USAGE: &str = "process_memory_usage";
21const MEMORY_VIRTUAL_USAGE: &str = "process_memory_virtual_usage";
22
23impl HostMetrics {
24    pub async fn process_metrics(&mut self, output: &mut super::MetricsBuffer) {
25        self.system.refresh_processes_specifics(
26            ProcessesToUpdate::All,
27            true,
28            ProcessRefreshKind::default()
29                .with_memory()
30                .with_cpu()
31                .with_cmd(UpdateKind::OnlyIfNotSet),
32        );
33        output.name = "process";
34        let sep = OsStr::new(" ");
35        for (pid, process) in self.system.processes().iter().filter(|&(_, proc)| {
36            self.config
37                .process
38                .processes
39                .contains_str(proc.name().to_str())
40        }) {
41            let tags = || {
42                metric_tags!(
43                "pid" => pid.as_u32().to_string(),
44                "name" => process.name().to_str().unwrap_or("unknown"),
45                "command" => process.cmd().join(sep).to_str().unwrap_or(""))
46            };
47            output.gauge(CPU_USAGE, process.cpu_usage().into(), tags());
48            output.gauge(MEMORY_USAGE, process.memory() as f64, tags());
49            output.gauge(
50                MEMORY_VIRTUAL_USAGE,
51                process.virtual_memory() as f64,
52                tags(),
53            );
54            output.counter(RUNTIME, process.run_time() as f64, tags());
55        }
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use crate::sources::host_metrics::tests::count_tag;
62
63    use super::super::{HostMetrics, HostMetricsConfig, MetricsBuffer};
64
65    #[tokio::test]
66    async fn generates_process_metrics() {
67        let mut buffer = MetricsBuffer::new(None);
68        HostMetrics::new(HostMetricsConfig::default())
69            .process_metrics(&mut buffer)
70            .await;
71        let metrics = buffer.metrics;
72        assert!(!metrics.is_empty());
73
74        // All metrics are named process_*
75        assert!(!metrics
76            .iter()
77            .any(|metric| !metric.name().starts_with("process_")));
78
79        // They should all have the required tag
80        assert_eq!(count_tag(&metrics, "pid"), metrics.len());
81        assert_eq!(count_tag(&metrics, "name"), metrics.len());
82        assert_eq!(count_tag(&metrics, "command"), metrics.len());
83    }
84}