hyperlight_host/metrics/
mod.rs1use std::collections::HashMap;
18use std::sync::Once;
19
20use log::error;
21use once_cell::sync::OnceCell;
22use prometheus::{default_registry, histogram_opts, opts, HistogramOpts, Opts, Registry};
23use strum::{IntoEnumIterator, VariantNames};
24
25use crate::error::HyperlightError::{Error, MetricNotFound};
26use crate::{log_then_return, new_error, Result};
27mod int_gauge_vec;
28pub use int_gauge_vec::IntGaugeVec;
31mod int_gauge;
32pub use int_gauge::IntGauge;
34mod int_counter_vec;
35pub use int_counter_vec::IntCounterVec;
37mod int_counter;
38pub use int_counter::IntCounter;
40mod histogram_vec;
41pub use histogram_vec::HistogramVec;
43mod histogram;
44pub use histogram::Histogram;
46pub trait HyperlightMetricEnum<T>:
48 IntoEnumIterator + VariantNames + From<T> + Into<&'static str>
49where
50 &'static str: From<Self>,
51 &'static str: for<'a> From<&'a Self>,
52{
53 fn get_init_metrics() -> &'static Once;
55 fn get_metrics() -> &'static OnceCell<HashMap<&'static str, HyperlightMetric>>;
57 fn get_metric_definitions() -> &'static [HyperlightMetricDefinition];
59
60 #[inline]
62 fn get_hyperlight_metric(&self) -> Result<&HyperlightMetric> {
63 Self::get_init_metrics().call_once(|| {
64 let result = init_metrics(Self::get_metric_definitions(), Self::get_metrics());
65 if let Err(e) = result {
66 error!("Error initializing metrics : {0:?}", e);
67 }
68 });
69 let key: &'static str = <&Self as Into<&'static str>>::into(self);
70 HyperlightMetric::get_metric_using_key(key, Self::get_hash_map()?)
71 }
72 #[inline]
74 fn get_hash_map() -> Result<&'static HashMap<&'static str, HyperlightMetric>> {
75 Self::get_metrics()
76 .get()
77 .ok_or_else(|| Error("metrics hashmap not initialized".to_string()))
78 }
79}
80pub trait HyperlightMetricOps {
83 fn get_metric(&self) -> Result<&HyperlightMetric>;
85}
86
87pub trait GetHyperlightMetric<T> {
89 fn metric(&self) -> Result<&T>;
91}
92
93impl<T: HyperlightMetricEnum<T>> HyperlightMetricOps for T
94where
95 &'static str: From<T>,
96 for<'a> &'static str: From<&'a T>,
97{
98 fn get_metric(&self) -> Result<&HyperlightMetric> {
99 self.get_hyperlight_metric()
100 }
101}
102
103#[inline]
105fn init_metrics(
106 metric_definitions: &[HyperlightMetricDefinition],
107 metrics: &OnceCell<HashMap<&'static str, HyperlightMetric>>,
108) -> Result<()> {
109 let mut hash_map: HashMap<&'static str, HyperlightMetric> = HashMap::new();
110 register_metrics(metric_definitions, &mut hash_map)?;
111 if let Err(e) = metrics.set(hash_map) {
114 error!("metrics hashmap already set : {0:?}", e);
115 }
116 Ok(())
117}
118#[allow(dead_code)]
120#[derive(Debug)]
121pub enum HyperlightMetricType {
123 IntCounter,
125 IntCounterVec,
127 IntGauge,
129 IntGaugeVec,
131 Histogram,
133 HistogramVec,
135}
136
137pub struct HyperlightMetricDefinition {
139 pub name: &'static str,
141 pub help: &'static str,
143 pub metric_type: HyperlightMetricType,
145 pub labels: &'static [&'static str],
147 pub buckets: &'static [f64],
149}
150
151fn register_metrics(
152 metric_definitions: &[HyperlightMetricDefinition],
153 hash_map: &mut HashMap<&'static str, HyperlightMetric>,
154) -> Result<()> {
155 for metric_definition in metric_definitions {
156 let metric: HyperlightMetric = match &metric_definition.metric_type {
157 HyperlightMetricType::IntGauge => {
158 IntGauge::new(metric_definition.name, metric_definition.help)?.into()
159 }
160
161 HyperlightMetricType::IntCounterVec => IntCounterVec::new(
162 metric_definition.name,
163 metric_definition.help,
164 metric_definition.labels,
165 )?
166 .into(),
167
168 HyperlightMetricType::IntCounter => {
169 IntCounter::new(metric_definition.name, metric_definition.help)?.into()
170 }
171 HyperlightMetricType::HistogramVec => HistogramVec::new(
172 metric_definition.name,
173 metric_definition.help,
174 metric_definition.labels,
175 metric_definition.buckets.to_vec(),
176 )?
177 .into(),
178 HyperlightMetricType::Histogram => Histogram::new(
179 metric_definition.name,
180 metric_definition.help,
181 metric_definition.buckets.to_vec(),
182 )?
183 .into(),
184 HyperlightMetricType::IntGaugeVec => IntGaugeVec::new(
185 metric_definition.name,
186 metric_definition.help,
187 metric_definition.labels,
188 )?
189 .into(),
190 };
191
192 hash_map.insert(metric_definition.name, metric);
193 }
194 Ok(())
195}
196
197#[derive(Debug)]
198pub enum HyperlightMetric {
200 IntCounter(IntCounter),
202 IntCounterVec(IntCounterVec),
204 IntGauge(IntGauge),
206 IntGaugeVec(IntGaugeVec),
208 Histogram(Histogram),
210 HistogramVec(HistogramVec),
212}
213
214impl HyperlightMetric {
215 #[inline]
216 fn get_metric_using_key<'a>(
217 key: &'static str,
218 hash_map: &'a HashMap<&'static str, HyperlightMetric>,
219 ) -> Result<&'a HyperlightMetric> {
220 hash_map.get(key).ok_or_else(|| MetricNotFound(key))
221 }
222}
223
224static REGISTRY: OnceCell<&Registry> = OnceCell::new();
227
228#[inline]
230pub fn get_metrics_registry() -> &'static Registry {
231 REGISTRY.get_or_init(default_registry)
232}
233pub fn set_metrics_registry(registry: &'static Registry) -> Result<()> {
238 match REGISTRY.get() {
239 Some(_) => {
240 log_then_return!("Registry was already set");
241 }
242 None => {
243 REGISTRY
244 .set(registry)
245 .map_err(|e| new_error!("Registry already set : {0:?}", e))
247 }
248 }
249}
250
251fn get_metric_opts(name: &str, help: &str) -> Opts {
252 let opts = opts!(name, help);
253 opts.namespace("hyperlight")
254}
255
256fn get_histogram_opts(name: &str, help: &str, buckets: Vec<f64>) -> HistogramOpts {
257 let mut opts = histogram_opts!(name, help);
258 opts = opts.namespace("hyperlight");
259 opts.buckets(buckets)
260}
261
262#[allow(clippy::disallowed_macros)]
263pub mod tests {
265 use std::collections::HashSet;
266
267 use super::*;
268
269 pub trait HyperlightMetricEnumTest<T>:
271 HyperlightMetricEnum<T> + From<T> + Into<&'static str>
272 where
273 &'static str: From<Self>,
274 &'static str: for<'a> From<&'a Self>,
275 {
276 fn get_enum_variant_names() -> &'static [&'static str];
278
279 #[track_caller]
282 fn enum_has_variant_for_all_metrics() {
283 let metric_definitions = Self::get_metric_definitions().iter();
284 for metric_definition in metric_definitions {
285 let metric_definition_name = metric_definition.name;
286 assert!(
287 Self::get_enum_variant_names().contains(&metric_definition_name),
288 "Metric Definition Name {} not found",
289 metric_definition_name,
290 );
291 }
292 }
293
294 #[track_caller]
298 fn check_metric_definitions() {
299 let sandbox_metric_definitions = Self::get_metric_definitions();
300 let metric_definitions = sandbox_metric_definitions.iter();
301 let mut help_text = HashSet::new();
302 for metric_definition in metric_definitions {
303 assert!(
304 help_text.insert(metric_definition.help),
305 "duplicate metric help definition for {}",
306 metric_definition.name
307 );
308 }
309 assert_eq!(
310 Self::get_enum_variant_names().len(),
311 sandbox_metric_definitions.len()
312 );
313 }
314
315 fn get_intguage_metric(name: &str) -> Result<&IntGauge> {
317 Self::get_metrics()
318 .get()
319 .ok_or_else(|| new_error!("metrics hashmap not initialized"))?
320 .get(name)
321 .ok_or_else(|| new_error!("metric not found : {0:?}", name))?
322 .try_into()
323 }
324
325 fn get_intcountervec_metric(name: &str) -> Result<&IntCounterVec> {
327 Self::get_metrics()
328 .get()
329 .ok_or_else(|| new_error!("metrics hashmap not initialized"))?
330 .get(name)
331 .ok_or_else(|| new_error!("metric not found : {0:?}", name))?
332 .try_into()
333 }
334
335 fn get_intcounter_metric(name: &str) -> Result<&IntCounter> {
337 Self::get_metrics()
338 .get()
339 .ok_or_else(|| new_error!("metrics hashmap not initialized"))?
340 .get(name)
341 .ok_or_else(|| new_error!("metric not found : {0:?}", name))?
342 .try_into()
343 }
344
345 fn get_histogramvec_metric(name: &str) -> Result<&HistogramVec> {
347 Self::get_metrics()
348 .get()
349 .ok_or_else(|| new_error!("metrics hashmap not initialized"))?
350 .get(name)
351 .ok_or_else(|| new_error!("metric not found : {0:?}", name))?
352 .try_into()
353 }
354 }
355}