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")]
155 #[inline(always)]
156 pub fn counter(&self, name: &'static str) -> std::sync::Arc<Counter> {
157 self.registry.get_or_create_counter(name)
158 }
159
160 #[cfg(feature = "gauge")]
162 #[inline(always)]
163 pub fn gauge(&self, name: &'static str) -> std::sync::Arc<Gauge> {
164 self.registry.get_or_create_gauge(name)
165 }
166
167 #[cfg(feature = "timer")]
169 #[inline(always)]
170 pub fn timer(&self, name: &'static str) -> std::sync::Arc<Timer> {
171 self.registry.get_or_create_timer(name)
172 }
173
174 #[cfg(feature = "meter")]
176 #[inline(always)]
177 pub fn rate(&self, name: &'static str) -> std::sync::Arc<RateMeter> {
178 self.registry.get_or_create_rate_meter(name)
179 }
180
181 #[cfg(feature = "timer")]
184 #[inline]
185 pub fn time<T>(&self, name: &'static str, f: impl FnOnce() -> T) -> T {
186 let binding = self.timer(name);
187 let timer = binding.start();
188 let result = f();
189 timer.stop();
190 result
191 }
192
193 #[inline(always)]
195 pub fn system(&self) -> &SystemHealth {
196 &self.system
197 }
198
199 #[inline(always)]
201 pub fn registry(&self) -> &Registry {
202 &self.registry
203 }
204}
205
206impl Default for MetricsCore {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212pub type Result<T> = std::result::Result<T, MetricsError>;
214
215#[derive(Debug, Clone, PartialEq)]
217pub enum MetricsError {
218 CircuitOpen,
220 Overloaded,
222 InvalidName,
224 InvalidValue {
226 reason: &'static str,
228 },
229 Overflow,
231 Underflow,
233 OverLimit,
235 WouldBlock,
237 NotInitialized,
239 Config(String),
241}
242
243impl std::fmt::Display for MetricsError {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 match self {
246 MetricsError::CircuitOpen => write!(f, "Circuit breaker is open"),
247 MetricsError::Overloaded => write!(f, "System is overloaded"),
248 MetricsError::InvalidName => write!(f, "Invalid metric name"),
249 MetricsError::InvalidValue { reason } => write!(f, "Invalid value: {reason}"),
250 MetricsError::Overflow => write!(f, "Operation would overflow"),
251 MetricsError::Underflow => write!(f, "Operation would underflow"),
252 MetricsError::OverLimit => write!(f, "Operation would exceed limit"),
253 MetricsError::WouldBlock => write!(f, "Operation would block"),
254 MetricsError::NotInitialized => write!(f, "Global metrics not initialized"),
255 MetricsError::Config(msg) => write!(f, "Configuration error: {msg}"),
256 }
257 }
258}
259
260impl std::error::Error for MetricsError {}
261
262pub mod prelude {
267 #[cfg(feature = "count")]
268 pub use crate::Counter;
269 #[cfg(feature = "gauge")]
270 pub use crate::Gauge;
271 #[cfg(feature = "meter")]
272 pub use crate::RateMeter;
273 #[cfg(feature = "timer")]
274 pub use crate::Timer;
275 pub use crate::{init, metrics, MetricsCore, MetricsError, Result, METRICS};
276 pub use crate::{Registry, SystemHealth};
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 #[test]
284 fn test_metrics_initialization() {
285 let metrics = MetricsCore::new();
286 let _cpu = metrics.system().cpu_used();
288 let _mem = metrics.system().mem_used_mb();
289 #[cfg(feature = "count")]
290 {
291 metrics.counter("test").inc();
292 assert_eq!(metrics.counter("test").get(), 1);
293 }
294 #[cfg(feature = "gauge")]
295 {
296 metrics.gauge("test").set(42.5);
297 assert_eq!(metrics.gauge("test").get(), 42.5);
298 }
299 }
300
301 #[cfg(feature = "count")]
302 #[test]
303 fn test_global_metrics() {
304 let _metrics = init();
305 metrics().counter("global_test").inc();
306 assert_eq!(metrics().counter("global_test").get(), 1);
307 }
308
309 #[cfg(feature = "timer")]
310 #[test]
311 fn test_time_function_records_and_returns() {
312 let metrics = MetricsCore::new();
313 let result = metrics.time("timed_op", || 123usize);
314 assert_eq!(result, 123);
315 assert_eq!(metrics.timer("timed_op").count(), 1);
316 }
317
318 #[cfg(feature = "count")]
319 #[test]
320 fn test_accessors_system_and_registry() {
321 let metrics = MetricsCore::new();
322 let _ = metrics.system().cpu_used();
323 let reg = metrics.registry();
324 let c = reg.get_or_create_counter("from_registry");
325 c.add(2);
326 assert_eq!(metrics.counter("from_registry").get(), 2);
327 }
328
329 #[cfg(feature = "count")]
330 #[test]
331 fn test_default_impl() {
332 let metrics: MetricsCore = Default::default();
333 metrics.counter("default_impl").inc();
334 assert_eq!(metrics.counter("default_impl").get(), 1);
335 }
336
337 #[test]
338 fn test_metrics_error_display() {
339 let e1 = MetricsError::CircuitOpen;
340 let e2 = MetricsError::Overloaded;
341 let e3 = MetricsError::InvalidName;
342 let e4 = MetricsError::Config("bad cfg".to_string());
343
344 let s1 = format!("{e1}");
345 let s2 = format!("{e2}");
346 let s3 = format!("{e3}");
347 let s4 = format!("{e4}");
348
349 assert!(s1.contains("Circuit breaker is open"));
350 assert!(s2.contains("System is overloaded"));
351 assert!(s3.contains("Invalid metric name"));
352 assert!(s4.contains("Configuration error"));
353 assert!(s4.contains("bad cfg"));
354 }
355}