1use std::sync::{Arc, Mutex, Weak};
16use std::time::Instant;
17
18use prometheus::core::{Collector, Desc};
19use prometheus::proto::{Gauge, MetricFamily};
20use prometheus::{Histogram, IntCounter, IntGauge, IntGaugeVec, Opts};
21
22#[derive(Clone)]
27pub struct CacheMetrics {
28 pub requests: OperationMetrics,
29 pub loads: OperationMetrics,
30}
31
32#[derive(Clone)]
35pub struct OperationMetrics {
36 pub started: IntCounter,
37 pub succeeded: IntCounter,
38 pub failed: IntCounter,
39 pub cancelled: IntCounter,
40 pub active: IntGauge,
41 pub elapsed: Histogram,
43}
44
45impl OperationMetrics {
46 pub(crate) fn start(&self) -> OperationGuard<'_> {
48 self.started.inc();
49 self.active.inc();
50 OperationGuard {
51 metrics: self,
52 started: Instant::now(),
53 completed: false,
54 }
55 }
56}
57
58pub(crate) struct OperationGuard<'a> {
60 metrics: &'a OperationMetrics,
61 started: Instant,
62 completed: bool,
63}
64
65impl OperationGuard<'_> {
66 pub(crate) fn finish<T, E>(mut self, result: &Result<T, E>) {
68 if result.is_ok() {
69 self.metrics.succeeded.inc();
70 } else {
71 self.metrics.failed.inc();
72 }
73 self.completed = true;
74 }
75}
76
77impl Drop for OperationGuard<'_> {
78 fn drop(&mut self) {
79 if !self.completed {
80 self.metrics.cancelled.inc();
81 }
82 self.metrics.active.dec();
83 self.metrics
84 .elapsed
85 .observe(self.started.elapsed().as_secs_f64());
86 }
87}
88
89#[derive(Clone)]
92pub struct CacheSizeMetrics {
93 gauges: IntGaugeVec,
94 sources: Arc<Mutex<Vec<CacheSizeSource>>>,
95}
96
97#[derive(Clone)]
99pub struct CacheSizeMetric {
100 metrics: CacheSizeMetrics,
101 gauge: IntGauge,
102}
103
104#[derive(Clone)]
106pub struct CacheSizeRegistration {
107 _reader: Arc<dyn Fn() -> u64 + Send + Sync>,
108}
109
110struct CacheSizeSource {
111 gauge: IntGauge,
112 read: Weak<dyn Fn() -> u64 + Send + Sync>,
113}
114
115impl CacheSizeMetrics {
116 pub fn new(opts: Opts, labels: &[&str]) -> prometheus::Result<Self> {
118 Ok(Self {
119 gauges: IntGaugeVec::new(opts, labels)?,
120 sources: Arc::new(Mutex::new(Vec::new())),
121 })
122 }
123
124 pub fn gauges(&self) -> &IntGaugeVec {
126 &self.gauges
127 }
128
129 pub fn with_label_values(&self, labels: &[&str]) -> IntGauge {
131 self.gauges.with_label_values(labels)
132 }
133
134 pub fn bind(&self, labels: &[&str]) -> CacheSizeMetric {
136 CacheSizeMetric {
137 metrics: self.clone(),
138 gauge: self.gauges.with_label_values(labels),
139 }
140 }
141}
142
143impl CacheSizeMetric {
144 pub fn register(
147 &self,
148 read: impl Fn() -> u64 + Send + Sync + 'static,
149 ) -> CacheSizeRegistration {
150 let read: Arc<dyn Fn() -> u64 + Send + Sync> = Arc::new(read);
151 self.metrics
152 .sources
153 .lock()
154 .unwrap_or_else(|e| e.into_inner())
155 .push(CacheSizeSource {
156 gauge: self.gauge.clone(),
157 read: Arc::downgrade(&read),
158 });
159 CacheSizeRegistration { _reader: read }
160 }
161}
162
163impl Collector for CacheSizeMetrics {
164 fn desc(&self) -> Vec<&Desc> {
165 self.gauges.desc()
166 }
167
168 fn collect(&self) -> Vec<MetricFamily> {
169 let mut families = self.gauges.collect();
170 let mut sources = self.sources.lock().unwrap_or_else(|e| e.into_inner());
171 sources.retain(|source| {
172 let Some(read) = source.read.upgrade() else {
173 return false;
174 };
175 let labels = source.gauge.collect();
176 for family in &mut families {
177 for metric in family.mut_metric() {
178 if metric.get_label() == labels[0].get_metric()[0].get_label() {
179 let bytes = metric
180 .get_gauge()
181 .as_ref()
182 .map_or(0.0, |gauge| gauge.value())
183 + read() as f64;
184 let mut gauge = Gauge::default();
185 gauge.set_value(bytes);
186 metric.set_gauge(gauge);
187 }
188 }
189 }
190 true
191 });
192 families
193 }
194}