laminar_connectors/prom/
mod.rs1use prometheus::{IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Opts, Registry};
7
8pub struct RegHandle<'a> {
10 registry: &'a Registry,
11 _local: Option<Registry>,
12}
13
14impl RegHandle<'_> {
15 #[must_use]
17 pub fn registry(&self) -> &Registry {
18 self.registry
19 }
20
21 #[must_use]
27 pub fn counter(&self, name: &str, help: &str) -> IntCounter {
28 let c =
29 IntCounter::new(name, help).unwrap_or_else(|e| panic!("invalid counter '{name}': {e}"));
30 self.registry.register(Box::new(c.clone())).ok();
31 c
32 }
33
34 #[must_use]
40 pub fn gauge(&self, name: &str, help: &str) -> IntGauge {
41 let g = IntGauge::new(name, help).unwrap_or_else(|e| panic!("invalid gauge '{name}': {e}"));
42 self.registry.register(Box::new(g.clone())).ok();
43 g
44 }
45
46 #[must_use]
52 pub fn counter_vec(&self, name: &str, help: &str, labels: &[&str]) -> IntCounterVec {
53 let v = IntCounterVec::new(Opts::new(name, help), labels)
54 .unwrap_or_else(|e| panic!("invalid counter_vec '{name}': {e}"));
55 self.registry.register(Box::new(v.clone())).ok();
56 v
57 }
58
59 #[must_use]
65 pub fn gauge_vec(&self, name: &str, help: &str, labels: &[&str]) -> IntGaugeVec {
66 let v = IntGaugeVec::new(Opts::new(name, help), labels)
67 .unwrap_or_else(|e| panic!("invalid gauge_vec '{name}': {e}"));
68 self.registry.register(Box::new(v.clone())).ok();
69 v
70 }
71}
72
73#[must_use]
77#[allow(clippy::missing_panics_doc)] pub fn reg_or_local<'a>(
79 registry: Option<&'a Registry>,
80 local: &'a mut Option<Registry>,
81) -> RegHandle<'a> {
82 if let Some(r) = registry {
83 RegHandle {
84 registry: r,
85 _local: None,
86 }
87 } else {
88 *local = Some(Registry::new());
89 RegHandle {
90 registry: local.as_ref().expect("just initialized"),
91 _local: None,
92 }
93 }
94}
95
96pub struct LabelledFamilies {
102 label: (&'static str, String),
103 counters: Vec<IntCounter>,
104 gauges: Vec<IntGauge>,
105 registry: Option<Registry>,
106}
107
108impl LabelledFamilies {
109 #[must_use]
111 pub fn new(key: &'static str, value: &str) -> Self {
112 Self {
113 label: (key, value.to_string()),
114 counters: Vec::new(),
115 gauges: Vec::new(),
116 registry: None,
117 }
118 }
119
120 pub fn counter(&mut self, name: &str, help: &str) -> Result<IntCounter, prometheus::Error> {
125 let counter =
126 IntCounter::with_opts(Opts::new(name, help).const_label(self.label.0, &self.label.1))?;
127 self.counters.push(counter.clone());
128 Ok(counter)
129 }
130
131 pub fn gauge(&mut self, name: &str, help: &str) -> Result<IntGauge, prometheus::Error> {
136 let gauge =
137 IntGauge::with_opts(Opts::new(name, help).const_label(self.label.0, &self.label.1))?;
138 self.gauges.push(gauge.clone());
139 Ok(gauge)
140 }
141
142 fn collectors(&self) -> Vec<Box<dyn prometheus::core::Collector>> {
143 let counters = self
144 .counters
145 .iter()
146 .map(|counter| Box::new(counter.clone()) as Box<dyn prometheus::core::Collector>);
147 let gauges = self
148 .gauges
149 .iter()
150 .map(|gauge| Box::new(gauge.clone()) as Box<dyn prometheus::core::Collector>);
151 counters.chain(gauges).collect()
152 }
153
154 #[must_use]
156 pub fn register(mut self, registry: Option<&Registry>) -> Self {
157 let Some(registry) = registry else {
158 return self;
159 };
160 for (index, collector) in self.collectors().into_iter().enumerate() {
161 if let Err(error) = registry.register(collector) {
162 for previous in self.collectors().into_iter().take(index) {
163 let _ = registry.unregister(previous);
164 }
165 tracing::warn!(%error, "connector metrics are not exported for this instance");
166 return self;
167 }
168 }
169 self.registry = Some(registry.clone());
170 self
171 }
172}
173
174impl Drop for LabelledFamilies {
175 fn drop(&mut self) {
176 if let Some(registry) = self.registry.take() {
177 for collector in self.collectors() {
178 let _ = registry.unregister(collector);
179 }
180 }
181 }
182}
183
184#[cfg(test)]
185mod tests;