use prometheus::{
IntGauge, IntGaugeVec, Opts,
core::{Collector, Desc},
process_collector::pid_t,
proto,
};
use std::thread;
use sysinfo::{CpuRefreshKind, MemoryRefreshKind, Networks, RefreshKind, System};
const METRICS_NUMBER: usize = 8;
pub(crate) struct SysInfoCollector {
_pid: pid_t,
descs: Vec<Desc>,
total_cpu_usage: IntGauge,
cpu_usage: IntGaugeVec,
cpu_count: IntGauge,
network_received: IntGaugeVec,
network_transmitted: IntGaugeVec,
system_memory_usage: IntGauge,
system_total_memory: IntGauge,
}
impl SysInfoCollector {
pub fn with_process_and_namespace<S: Into<String>>(
_pid: pid_t,
namespace: S,
) -> Result<SysInfoCollector, prometheus::Error> {
let namespace = namespace.into();
let mut descs = Vec::new();
let opts = |name: &str, help: &str| Opts::new(name, help).namespace(namespace.clone());
let mut collect_descs = |collector: &dyn prometheus::core::Collector| {
descs.extend(collector.desc().into_iter().cloned());
};
let total_cpu_usage = IntGauge::with_opts(opts(
"process_total_cpu_usage",
"Total CPU utilization (in %). Divide by CPU count to get a value between 0% and 100%.",
))?;
collect_descs(&total_cpu_usage);
let cpu_count = IntGauge::with_opts(opts(
"process_system_cpu_count",
"Returns the list of the CPUs.",
))?;
collect_descs(&cpu_count);
let cpu_usage = IntGaugeVec::new(
opts(
"process_system_cpu_usage_per_core",
"CPU utilization (in %) per core.",
),
&["core"],
)?;
collect_descs(&cpu_usage);
let network_received = IntGaugeVec::new(
opts(
"process_system_network_received_bytes",
"Network received bytes.",
),
&["interface"],
)?;
collect_descs(&network_received);
let network_transmitted = IntGaugeVec::new(
opts(
"process_system_network_transmitted_bytes",
"Network transmitted bytes.",
),
&["interface"],
)?;
collect_descs(&network_transmitted);
let system_memory_usage = IntGauge::with_opts(opts(
"process_system_memory_usage",
"System memory utilization in bytes.",
))?;
collect_descs(&system_memory_usage);
let system_total_memory = IntGauge::with_opts(opts(
"process_system_total_memory",
"System total memory in bytes.",
))?;
collect_descs(&system_total_memory);
Ok(SysInfoCollector {
_pid,
descs,
total_cpu_usage,
cpu_usage,
cpu_count,
network_received,
network_transmitted,
system_memory_usage,
system_total_memory,
})
}
pub fn _with_namespace<S: Into<String>>(
namespace: S,
) -> Result<SysInfoCollector, prometheus::Error> {
let pid = std::process::id();
SysInfoCollector::with_process_and_namespace(pid as i32, namespace)
}
}
impl Collector for SysInfoCollector {
fn desc(&self) -> Vec<&Desc> {
self.descs.iter().collect()
}
fn collect(&self) -> Vec<proto::MetricFamily> {
let mut networks = Networks::new_with_refreshed_list();
let mut sys = System::new_with_specifics(
RefreshKind::nothing().with_cpu(CpuRefreshKind::everything()),
);
sys.refresh_cpu_all();
thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
sys.refresh_cpu_all();
self.cpu_count.set(sys.cpus().len() as i64);
self.total_cpu_usage.set(sys.global_cpu_usage() as i64);
sys.cpus().iter().for_each(|cpu| {
let number = cpu
.name()
.strip_prefix("cpu")
.expect("Invalid CPU index in metrics.");
let core_index: u32 = number.parse().unwrap_or(0);
let core_label = format!("CPU{:02}", core_index + 1);
let gauge = self.cpu_usage.with_label_values(&[&core_label]);
gauge.set(cpu.cpu_usage() as i64);
});
networks.refresh(false);
for (interface_name, data) in &networks {
let received_gauge = self.network_received.with_label_values(&[&interface_name]);
received_gauge.set(data.received() as i64);
let transmitted_gauge = self
.network_transmitted
.with_label_values(&[&interface_name]);
transmitted_gauge.set(data.transmitted() as i64);
}
sys.refresh_memory_specifics(MemoryRefreshKind::nothing().with_ram());
self.system_memory_usage.set(sys.used_memory() as i64);
self.system_total_memory.set(sys.total_memory() as i64);
let mut mfs = Vec::with_capacity(METRICS_NUMBER);
mfs.extend(self.total_cpu_usage.collect());
mfs.extend(self.cpu_usage.collect());
mfs.extend(self.cpu_count.collect());
mfs.extend(self.network_received.collect());
mfs.extend(self.network_transmitted.collect());
mfs.extend(self.system_memory_usage.collect());
mfs.extend(self.system_total_memory.collect());
mfs
}
}