1#![warn(missing_docs)]
67#![allow(unsafe_code)] use std::sync::OnceLock;
70
71#[cfg(feature = "sample")]
73mod adaptive;
74#[cfg(feature = "async")]
75mod async_support;
76#[cfg(feature = "count")]
77mod counter;
78#[cfg(feature = "gauge")]
79mod gauge;
80#[cfg(feature = "meter")]
81mod rate_meter;
82#[cfg(feature = "timer")]
83mod timer;
84
85mod registry;
87mod system_health;
88
89#[cfg(feature = "sample")]
91pub use adaptive::{
92 AdaptiveSampler, BackpressureController, MetricCircuitBreaker, SamplingStrategy,
93};
94#[cfg(feature = "async")]
95pub use async_support::{AsyncMetricBatch, AsyncMetricsBatcher, AsyncTimerExt, AsyncTimerGuard};
96#[cfg(feature = "count")]
97pub use counter::*;
98#[cfg(feature = "gauge")]
99pub use gauge::{Gauge, GaugeStats};
100#[cfg(feature = "meter")]
101pub use rate_meter::{RateMeter, RateStats};
102#[cfg(feature = "timer")]
103pub use timer::*;
104
105pub use registry::*;
106pub use system_health::*;
107
108#[cfg(feature = "gauge")]
110pub use gauge::specialized as gauge_specialized;
111#[cfg(feature = "meter")]
112pub use rate_meter::specialized as rate_meter_specialized;
113
114pub static METRICS: OnceLock<MetricsCore> = OnceLock::new();
116
117pub fn init() -> &'static MetricsCore {
121 METRICS.get_or_init(MetricsCore::new)
122}
123
124pub fn metrics() -> &'static MetricsCore {
132 METRICS
133 .get()
134 .expect("Metrics not initialized - call metrics_lib::init() first")
135}
136
137#[repr(align(64))] pub struct MetricsCore {
140 registry: Registry,
141 system: SystemHealth,
142}
143
144impl MetricsCore {
145 pub fn new() -> Self {
147 Self {
148 registry: Registry::new(),
149 system: SystemHealth::new(),
150 }
151 }
152
153 #[cfg(feature = "count")]
160 #[inline(always)]
161 pub fn counter(&self, name: &str) -> std::sync::Arc<Counter> {
162 self.registry.get_or_create_counter(name)
163 }
164
165 #[cfg(feature = "gauge")]
170 #[inline(always)]
171 pub fn gauge(&self, name: &str) -> std::sync::Arc<Gauge> {
172 self.registry.get_or_create_gauge(name)
173 }
174
175 #[cfg(feature = "timer")]
180 #[inline(always)]
181 pub fn timer(&self, name: &str) -> std::sync::Arc<Timer> {
182 self.registry.get_or_create_timer(name)
183 }
184
185 #[cfg(feature = "meter")]
190 #[inline(always)]
191 pub fn rate(&self, name: &str) -> std::sync::Arc<RateMeter> {
192 self.registry.get_or_create_rate_meter(name)
193 }
194
195 #[cfg(feature = "timer")]
198 #[inline]
199 pub fn time<T>(&self, name: &str, f: impl FnOnce() -> T) -> T {
200 let binding = self.timer(name);
201 let timer = binding.start();
202 let result = f();
203 timer.stop();
204 result
205 }
206
207 #[inline(always)]
209 pub fn system(&self) -> &SystemHealth {
210 &self.system
211 }
212
213 #[inline(always)]
215 pub fn registry(&self) -> &Registry {
216 &self.registry
217 }
218}
219
220impl Default for MetricsCore {
221 fn default() -> Self {
222 Self::new()
223 }
224}
225
226pub type Result<T> = std::result::Result<T, MetricsError>;
228
229#[derive(Debug, Clone, PartialEq)]
231pub enum MetricsError {
232 CircuitOpen,
234 Overloaded,
236 InvalidName,
238 InvalidValue {
240 reason: &'static str,
242 },
243 Overflow,
245 Underflow,
247 OverLimit,
249 WouldBlock,
251 NotInitialized,
253 Config(String),
255}
256
257impl std::fmt::Display for MetricsError {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 match self {
260 MetricsError::CircuitOpen => write!(f, "Circuit breaker is open"),
261 MetricsError::Overloaded => write!(f, "System is overloaded"),
262 MetricsError::InvalidName => write!(f, "Invalid metric name"),
263 MetricsError::InvalidValue { reason } => write!(f, "Invalid value: {reason}"),
264 MetricsError::Overflow => write!(f, "Operation would overflow"),
265 MetricsError::Underflow => write!(f, "Operation would underflow"),
266 MetricsError::OverLimit => write!(f, "Operation would exceed limit"),
267 MetricsError::WouldBlock => write!(f, "Operation would block"),
268 MetricsError::NotInitialized => write!(f, "Global metrics not initialized"),
269 MetricsError::Config(msg) => write!(f, "Configuration error: {msg}"),
270 }
271 }
272}
273
274impl std::error::Error for MetricsError {}
275
276pub mod prelude {
281 #[cfg(feature = "count")]
282 pub use crate::Counter;
283 #[cfg(feature = "gauge")]
284 pub use crate::Gauge;
285 #[cfg(feature = "meter")]
286 pub use crate::RateMeter;
287 #[cfg(feature = "timer")]
288 pub use crate::Timer;
289 pub use crate::{init, metrics, MetricsCore, MetricsError, Result, METRICS};
290 pub use crate::{Registry, SystemHealth};
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 #[test]
298 fn test_metrics_initialization() {
299 let metrics = MetricsCore::new();
300 let _cpu = metrics.system().cpu_used();
302 let _mem = metrics.system().mem_used_mb();
303 #[cfg(feature = "count")]
304 {
305 metrics.counter("test").inc();
306 assert_eq!(metrics.counter("test").get(), 1);
307 }
308 #[cfg(feature = "gauge")]
309 {
310 metrics.gauge("test").set(42.5);
311 assert_eq!(metrics.gauge("test").get(), 42.5);
312 }
313 }
314
315 #[cfg(feature = "count")]
316 #[test]
317 fn test_global_metrics() {
318 let _metrics = init();
319 metrics().counter("global_test").inc();
320 assert_eq!(metrics().counter("global_test").get(), 1);
321 }
322
323 #[cfg(feature = "timer")]
324 #[test]
325 fn test_time_function_records_and_returns() {
326 let metrics = MetricsCore::new();
327 let result = metrics.time("timed_op", || 123usize);
328 assert_eq!(result, 123);
329 assert_eq!(metrics.timer("timed_op").count(), 1);
330 }
331
332 #[cfg(feature = "count")]
333 #[test]
334 fn test_accessors_system_and_registry() {
335 let metrics = MetricsCore::new();
336 let _ = metrics.system().cpu_used();
337 let reg = metrics.registry();
338 let c = reg.get_or_create_counter("from_registry");
339 c.add(2);
340 assert_eq!(metrics.counter("from_registry").get(), 2);
341 }
342
343 #[cfg(feature = "count")]
344 #[test]
345 fn test_default_impl() {
346 let metrics: MetricsCore = Default::default();
347 metrics.counter("default_impl").inc();
348 assert_eq!(metrics.counter("default_impl").get(), 1);
349 }
350
351 #[test]
352 fn test_metrics_error_display() {
353 let e1 = MetricsError::CircuitOpen;
354 let e2 = MetricsError::Overloaded;
355 let e3 = MetricsError::InvalidName;
356 let e4 = MetricsError::Config("bad cfg".to_string());
357
358 let s1 = format!("{e1}");
359 let s2 = format!("{e2}");
360 let s3 = format!("{e3}");
361 let s4 = format!("{e4}");
362
363 assert!(s1.contains("Circuit breaker is open"));
364 assert!(s2.contains("System is overloaded"));
365 assert!(s3.contains("Invalid metric name"));
366 assert!(s4.contains("Configuration error"));
367 assert!(s4.contains("bad cfg"));
368 }
369}