1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use std::io::{Error, ErrorKind, Result};
use lazy_static::lazy_static;
use prometheus::core::{Collector, Desc};
use prometheus::{proto, Counter, Gauge, Opts};
use std::sync::Mutex;
pub fn monitor_process() -> Result<()> {
let pid = unsafe { libc::getpid() };
let tc = ProcessCollector::new(pid);
prometheus::register(Box::new(tc)).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
pub struct ProcessCollector {
pid: libc::pid_t,
descs: Vec<Desc>,
cpu_total: Mutex<Counter>,
vsize: Gauge,
rss: Gauge,
start_time: Gauge,
}
impl ProcessCollector {
pub fn new(pid: libc::pid_t) -> Self {
let mut descs = Vec::new();
let cpu_total = Counter::with_opts(Opts::new(
"process_cpu_seconds_total",
"Total user and system CPU time spent in \
seconds.",
))
.unwrap();
descs.extend(cpu_total.desc().into_iter().cloned());
let vsize = Gauge::with_opts(Opts::new(
"process_virtual_memory_bytes",
"Virtual memory size in bytes.",
))
.unwrap();
descs.extend(vsize.desc().into_iter().cloned());
let rss = Gauge::with_opts(Opts::new(
"process_resident_memory_bytes",
"Resident memory size in bytes.",
))
.unwrap();
descs.extend(rss.desc().into_iter().cloned());
let start_time = Gauge::with_opts(Opts::new(
"process_start_time_seconds",
"Start time of the process since unix epoch \
in seconds.",
))
.unwrap();
descs.extend(start_time.desc().into_iter().cloned());
Self {
pid,
descs,
cpu_total: Mutex::new(cpu_total),
vsize,
rss,
start_time,
}
}
}
impl Collector for ProcessCollector {
fn desc(&self) -> Vec<&Desc> {
self.descs.iter().collect()
}
fn collect(&self) -> Vec<proto::MetricFamily> {
let p = match procfs::process::Process::new(self.pid) {
Ok(p) => p,
Err(..) => {
return Vec::new();
}
};
self.vsize.set(p.stat.vsize as f64);
self.rss.set(p.stat.rss as f64 * *PAGESIZE);
if let Some(boot_time) = *BOOT_TIME {
self.start_time
.set(p.stat.starttime as f64 / *CLK_TCK + boot_time);
}
let cpu_total_mfs = {
let cpu_total = self.cpu_total.lock().unwrap();
let total = (p.stat.utime + p.stat.stime) as f64 / *CLK_TCK;
let past = cpu_total.get();
let delta = total - past;
if delta > 0.0 {
cpu_total.inc_by(delta);
}
cpu_total.collect()
};
let mut mfs = Vec::with_capacity(4);
mfs.extend(cpu_total_mfs);
mfs.extend(self.vsize.collect());
mfs.extend(self.rss.collect());
mfs.extend(self.start_time.collect());
mfs
}
}
lazy_static! {
static ref CLK_TCK: f64 = {
unsafe {
libc::sysconf(libc::_SC_CLK_TCK) as f64
}
};
static ref PAGESIZE: f64 = {
unsafe {
libc::sysconf(libc::_SC_PAGESIZE) as f64
}
};
}
lazy_static! {
static ref BOOT_TIME: Option<f64> = procfs::boot_time_secs().ok().map(|i| i as f64);
}