TLA 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 CBC 500 : pub fn register_internal(c: Box<dyn Collector>) -> Result<()> {
56 500 : INTERNAL_REGISTRY.register(c)
57 500 : }
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 565 : pub fn gather() -> Vec<prometheus::proto::MetricFamily> {
64 565 : update_rusage_metrics();
65 565 : let mut mfs = prometheus::gather();
66 565 : let mut internal_mfs = INTERNAL_REGISTRY.gather();
67 565 : mfs.append(&mut internal_mfs);
68 565 : mfs
69 565 : }
70 :
71 81 : static DISK_IO_BYTES: Lazy<IntGaugeVec> = Lazy::new(|| {
72 81 : register_int_gauge_vec!(
73 81 : "libmetrics_disk_io_bytes_total",
74 81 : "Bytes written and read from disk, grouped by the operation (read|write)",
75 81 : &["io_operation"]
76 81 : )
77 81 : .expect("Failed to register disk i/o bytes int gauge vec")
78 81 : });
79 :
80 81 : static MAXRSS_KB: Lazy<IntGauge> = Lazy::new(|| {
81 81 : register_int_gauge!(
82 81 : "libmetrics_maxrss_kb",
83 81 : "Memory usage (Maximum Resident Set Size)"
84 81 : )
85 81 : .expect("Failed to register maxrss_kb int gauge")
86 81 : });
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 1428 : pub fn set_build_info_metric(revision: &str) {
93 1428 : let metric = register_int_gauge_vec!(
94 1428 : "libmetrics_build_info",
95 1428 : "Build/version information",
96 1428 : &["revision"]
97 1428 : )
98 1428 : .expect("Failed to register build info metric");
99 1428 : metric.with_label_values(&[revision]).set(1);
100 1428 : }
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 565 : fn update_rusage_metrics() {
112 565 : let rusage_stats = get_rusage_stats();
113 565 :
114 565 : const BYTES_IN_BLOCK: i64 = 512;
115 565 : DISK_IO_BYTES
116 565 : .with_label_values(&["read"])
117 565 : .set(rusage_stats.ru_inblock * BYTES_IN_BLOCK);
118 565 : DISK_IO_BYTES
119 565 : .with_label_values(&["write"])
120 565 : .set(rusage_stats.ru_oublock * BYTES_IN_BLOCK);
121 565 : MAXRSS_KB.set(rusage_stats.ru_maxrss);
122 565 : }
123 :
124 565 : fn get_rusage_stats() -> libc::rusage {
125 565 : let mut rusage = std::mem::MaybeUninit::uninit();
126 565 :
127 565 : // SAFETY: kernel will initialize the struct for us
128 565 : unsafe {
129 565 : let ret = libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr());
130 565 : assert!(ret == 0, "getrusage failed: bad args");
131 565 : rusage.assume_init()
132 565 : }
133 565 : }
|