Line data Source code
1 : //! We re-export those from prometheus crate to
2 : //! make sure that we use the same dep version everywhere.
3 : //! Otherwise, we might not see all metrics registered via
4 : //! a default registry.
5 : use once_cell::sync::Lazy;
6 : use prometheus::core::{AtomicU64, Collector, GenericGauge, GenericGaugeVec};
7 : pub use prometheus::opts;
8 : pub use prometheus::register;
9 : pub use prometheus::Error;
10 : pub use prometheus::{core, default_registry, proto};
11 : pub use prometheus::{exponential_buckets, linear_buckets};
12 : pub use prometheus::{register_counter_vec, Counter, CounterVec};
13 : pub use prometheus::{register_gauge, Gauge};
14 : pub use prometheus::{register_gauge_vec, GaugeVec};
15 : pub use prometheus::{register_histogram, Histogram};
16 : pub use prometheus::{register_histogram_vec, HistogramVec};
17 : pub use prometheus::{register_int_counter, IntCounter};
18 : pub use prometheus::{register_int_counter_vec, IntCounterVec};
19 : pub use prometheus::{register_int_gauge, IntGauge};
20 : pub use prometheus::{register_int_gauge_vec, IntGaugeVec};
21 : pub use prometheus::{Encoder, TextEncoder};
22 : use prometheus::{Registry, Result};
23 :
24 : pub mod launch_timestamp;
25 : mod wrappers;
26 : pub use wrappers::{CountedReader, CountedWriter};
27 : pub mod metric_vec_duration;
28 :
29 : pub type UIntGauge = GenericGauge<AtomicU64>;
30 : pub type UIntGaugeVec = GenericGaugeVec<AtomicU64>;
31 :
32 : #[macro_export]
33 : macro_rules! register_uint_gauge_vec {
34 : ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{
35 : let gauge_vec = UIntGaugeVec::new($crate::opts!($NAME, $HELP), $LABELS_NAMES).unwrap();
36 : $crate::register(Box::new(gauge_vec.clone())).map(|_| gauge_vec)
37 : }};
38 : }
39 :
40 : #[macro_export]
41 : macro_rules! register_uint_gauge {
42 : ($NAME:expr, $HELP:expr $(,)?) => {{
43 : let gauge = $crate::UIntGauge::new($NAME, $HELP).unwrap();
44 : $crate::register(Box::new(gauge.clone())).map(|_| gauge)
45 : }};
46 : }
47 :
48 : /// Special internal registry, to collect metrics independently from the default registry.
49 : /// Was introduced to fix deadlock with lazy registration of metrics in the default registry.
50 : static INTERNAL_REGISTRY: Lazy<Registry> = Lazy::new(Registry::new);
51 :
52 : /// Register a collector in the internal registry. MUST be called before the first call to `gather()`.
53 : /// Otherwise, we can have a deadlock in the `gather()` call, trying to register a new collector
54 : /// while holding the lock.
55 517 : pub fn register_internal(c: Box<dyn Collector>) -> Result<()> {
56 517 : INTERNAL_REGISTRY.register(c)
57 517 : }
58 :
59 : /// Gathers all Prometheus metrics and records the I/O stats just before that.
60 : ///
61 : /// Metrics gathering is a relatively simple and standalone operation, so
62 : /// it might be fine to do it this way to keep things simple.
63 416 : pub fn gather() -> Vec<prometheus::proto::MetricFamily> {
64 416 : update_rusage_metrics();
65 416 : let mut mfs = prometheus::gather();
66 416 : let mut internal_mfs = INTERNAL_REGISTRY.gather();
67 416 : mfs.append(&mut internal_mfs);
68 416 : mfs
69 416 : }
70 :
71 69 : static DISK_IO_BYTES: Lazy<IntGaugeVec> = Lazy::new(|| {
72 69 : register_int_gauge_vec!(
73 69 : "libmetrics_disk_io_bytes_total",
74 69 : "Bytes written and read from disk, grouped by the operation (read|write)",
75 69 : &["io_operation"]
76 69 : )
77 69 : .expect("Failed to register disk i/o bytes int gauge vec")
78 69 : });
79 :
80 69 : static MAXRSS_KB: Lazy<IntGauge> = Lazy::new(|| {
81 69 : register_int_gauge!(
82 69 : "libmetrics_maxrss_kb",
83 69 : "Memory usage (Maximum Resident Set Size)"
84 69 : )
85 69 : .expect("Failed to register maxrss_kb int gauge")
86 69 : });
87 :
88 : pub const DISK_WRITE_SECONDS_BUCKETS: &[f64] = &[
89 : 0.000_050, 0.000_100, 0.000_500, 0.001, 0.003, 0.005, 0.01, 0.05, 0.1, 0.3, 0.5,
90 : ];
91 :
92 1478 : pub fn set_build_info_metric(revision: &str) {
93 1478 : let metric = register_int_gauge_vec!(
94 1478 : "libmetrics_build_info",
95 1478 : "Build/version information",
96 1478 : &["revision"]
97 1478 : )
98 1478 : .expect("Failed to register build info metric");
99 1478 : metric.with_label_values(&[revision]).set(1);
100 1478 : }
101 :
102 : // Records I/O stats in a "cross-platform" way.
103 : // Compiles both on macOS and Linux, but current macOS implementation always returns 0 as values for I/O stats.
104 : // An alternative is to read procfs (`/proc/[pid]/io`) which does not work under macOS at all, hence abandoned.
105 : //
106 : // Uses https://www.freebsd.org/cgi/man.cgi?query=getrusage to retrieve the number of block operations
107 : // performed by the process.
108 : // We know the size of the block, so we can determine the I/O bytes out of it.
109 : // The value might be not 100% exact, but should be fine for Prometheus metrics in this case.
110 : #[allow(clippy::unnecessary_cast)]
111 416 : fn update_rusage_metrics() {
112 416 : let rusage_stats = get_rusage_stats();
113 416 :
114 416 : const BYTES_IN_BLOCK: i64 = 512;
115 416 : DISK_IO_BYTES
116 416 : .with_label_values(&["read"])
117 416 : .set(rusage_stats.ru_inblock * BYTES_IN_BLOCK);
118 416 : DISK_IO_BYTES
119 416 : .with_label_values(&["write"])
120 416 : .set(rusage_stats.ru_oublock * BYTES_IN_BLOCK);
121 416 : MAXRSS_KB.set(rusage_stats.ru_maxrss);
122 416 : }
123 :
124 416 : fn get_rusage_stats() -> libc::rusage {
125 416 : let mut rusage = std::mem::MaybeUninit::uninit();
126 416 :
127 416 : // SAFETY: kernel will initialize the struct for us
128 416 : unsafe {
129 416 : let ret = libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr());
130 416 : assert!(ret == 0, "getrusage failed: bad args");
131 416 : rusage.assume_init()
132 416 : }
133 416 : }
|