1#![warn(missing_docs)]
52#![allow(unsafe_code)] use std::sync::OnceLock;
55
56mod adaptive;
57mod async_support;
58mod counter;
59mod gauge;
60mod rate_meter;
61mod registry;
62mod system_health;
63mod timer;
64
65pub use adaptive::{
66 AdaptiveSampler, BackpressureController, MetricCircuitBreaker, SamplingStrategy,
67};
68pub use async_support::{AsyncMetricBatch, AsyncTimerExt, AsyncTimerGuard};
69pub use counter::*;
70pub use gauge::{Gauge, GaugeStats};
71pub use rate_meter::{RateMeter, RateStats};
72pub use registry::*;
73pub use system_health::*;
74pub use timer::*;
75
76pub use gauge::specialized as gauge_specialized;
78pub use rate_meter::specialized as rate_meter_specialized;
79
80pub static METRICS: OnceLock<MetricsCore> = OnceLock::new();
82
83pub fn init() -> &'static MetricsCore {
87 METRICS.get_or_init(MetricsCore::new)
88}
89
90pub fn metrics() -> &'static MetricsCore {
98 METRICS
99 .get()
100 .expect("Metrics not initialized - call metrics_lib::init() first")
101}
102
103#[repr(align(64))] pub struct MetricsCore {
106 registry: Registry,
107 system: SystemHealth,
108}
109
110impl MetricsCore {
111 pub fn new() -> Self {
113 Self {
114 registry: Registry::new(),
115 system: SystemHealth::new(),
116 }
117 }
118
119 #[inline(always)]
121 pub fn counter(&self, name: &'static str) -> std::sync::Arc<Counter> {
122 self.registry.get_or_create_counter(name)
123 }
124
125 #[inline(always)]
127 pub fn gauge(&self, name: &'static str) -> std::sync::Arc<Gauge> {
128 self.registry.get_or_create_gauge(name)
129 }
130
131 #[inline(always)]
133 pub fn timer(&self, name: &'static str) -> std::sync::Arc<Timer> {
134 self.registry.get_or_create_timer(name)
135 }
136
137 #[inline(always)]
139 pub fn rate(&self, name: &'static str) -> std::sync::Arc<RateMeter> {
140 self.registry.get_or_create_rate_meter(name)
141 }
142
143 #[inline]
145 pub fn time<T>(&self, name: &'static str, f: impl FnOnce() -> T) -> T {
146 let binding = self.timer(name);
147 let timer = binding.start();
148 let result = f();
149 timer.stop();
150 result
151 }
152
153 #[inline(always)]
155 pub fn system(&self) -> &SystemHealth {
156 &self.system
157 }
158
159 #[inline(always)]
161 pub fn registry(&self) -> &Registry {
162 &self.registry
163 }
164}
165
166impl Default for MetricsCore {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172pub type Result<T> = std::result::Result<T, MetricsError>;
174
175#[derive(Debug, Clone, PartialEq)]
177pub enum MetricsError {
178 CircuitOpen,
180 Overloaded,
182 InvalidName,
184 InvalidValue {
186 reason: &'static str,
188 },
189 Overflow,
191 Underflow,
193 OverLimit,
195 WouldBlock,
197 NotInitialized,
199 Config(String),
201}
202
203impl std::fmt::Display for MetricsError {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 match self {
206 MetricsError::CircuitOpen => write!(f, "Circuit breaker is open"),
207 MetricsError::Overloaded => write!(f, "System is overloaded"),
208 MetricsError::InvalidName => write!(f, "Invalid metric name"),
209 MetricsError::InvalidValue { reason } => write!(f, "Invalid value: {reason}"),
210 MetricsError::Overflow => write!(f, "Operation would overflow"),
211 MetricsError::Underflow => write!(f, "Operation would underflow"),
212 MetricsError::OverLimit => write!(f, "Operation would exceed limit"),
213 MetricsError::WouldBlock => write!(f, "Operation would block"),
214 MetricsError::NotInitialized => write!(f, "Global metrics not initialized"),
215 MetricsError::Config(msg) => write!(f, "Configuration error: {msg}"),
216 }
217 }
218}
219
220impl std::error::Error for MetricsError {}
221
222pub mod prelude {
224 pub use crate::{init, metrics, MetricsCore, MetricsError, Result, METRICS};
225 pub use crate::{Counter, Gauge, RateMeter, Registry, SystemHealth, Timer};
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_metrics_initialization() {
234 let metrics = MetricsCore::new();
235
236 metrics.counter("test").inc();
238 assert_eq!(metrics.counter("test").get(), 1);
239
240 metrics.gauge("test").set(42.5);
241 assert_eq!(metrics.gauge("test").get(), 42.5);
242
243 let _cpu = metrics.system().cpu_used();
245 let _mem = metrics.system().mem_used_mb();
246 }
247
248 #[test]
249 fn test_global_metrics() {
250 let _metrics = init();
251
252 metrics().counter("global_test").inc();
254 assert_eq!(metrics().counter("global_test").get(), 1);
255 }
256
257 #[test]
258 fn test_time_function_records_and_returns() {
259 let metrics = MetricsCore::new();
260
261 let result = metrics.time("timed_op", || 123usize);
262 assert_eq!(result, 123);
263 assert_eq!(metrics.timer("timed_op").count(), 1);
265 }
266
267 #[test]
268 fn test_accessors_system_and_registry() {
269 let metrics = MetricsCore::new();
270
271 let _ = metrics.system().cpu_used();
273
274 let reg = metrics.registry();
276 let c = reg.get_or_create_counter("from_registry");
277 c.add(2);
278 assert_eq!(metrics.counter("from_registry").get(), 2);
279 }
280
281 #[test]
282 fn test_default_impl() {
283 let metrics: MetricsCore = Default::default();
284 metrics.counter("default_impl").inc();
285 assert_eq!(metrics.counter("default_impl").get(), 1);
286 }
287
288 #[test]
289 fn test_metrics_error_display() {
290 let e1 = MetricsError::CircuitOpen;
291 let e2 = MetricsError::Overloaded;
292 let e3 = MetricsError::InvalidName;
293 let e4 = MetricsError::Config("bad cfg".to_string());
294
295 let s1 = format!("{e1}");
296 let s2 = format!("{e2}");
297 let s3 = format!("{e3}");
298 let s4 = format!("{e4}");
299
300 assert!(s1.contains("Circuit breaker is open"));
301 assert!(s2.contains("System is overloaded"));
302 assert!(s3.contains("Invalid metric name"));
303 assert!(s4.contains("Configuration error"));
304 assert!(s4.contains("bad cfg"));
305 }
306}