#![warn(missing_docs)]
#![allow(unsafe_code)]
use std::sync::OnceLock;
#[cfg(feature = "sample")]
mod adaptive;
#[cfg(feature = "async")]
mod async_support;
#[cfg(feature = "count")]
mod counter;
#[cfg(feature = "gauge")]
mod gauge;
#[cfg(feature = "meter")]
mod rate_meter;
#[cfg(feature = "timer")]
mod timer;
mod registry;
mod system_health;
#[cfg(feature = "sample")]
pub use adaptive::{
AdaptiveSampler, BackpressureController, MetricCircuitBreaker, SamplingStrategy,
};
#[cfg(feature = "async")]
pub use async_support::{AsyncMetricBatch, AsyncMetricsBatcher, AsyncTimerExt, AsyncTimerGuard};
#[cfg(feature = "count")]
pub use counter::*;
#[cfg(feature = "gauge")]
pub use gauge::{Gauge, GaugeStats};
#[cfg(feature = "meter")]
pub use rate_meter::{RateMeter, RateStats};
#[cfg(feature = "timer")]
pub use timer::*;
pub use registry::*;
pub use system_health::*;
#[cfg(feature = "gauge")]
pub use gauge::specialized as gauge_specialized;
#[cfg(feature = "meter")]
pub use rate_meter::specialized as rate_meter_specialized;
pub static METRICS: OnceLock<MetricsCore> = OnceLock::new();
pub fn init() -> &'static MetricsCore {
METRICS.get_or_init(MetricsCore::new)
}
pub fn metrics() -> &'static MetricsCore {
METRICS
.get()
.expect("Metrics not initialized - call metrics_lib::init() first")
}
#[repr(align(64))] pub struct MetricsCore {
registry: Registry,
system: SystemHealth,
}
impl MetricsCore {
pub fn new() -> Self {
Self {
registry: Registry::new(),
system: SystemHealth::new(),
}
}
#[cfg(feature = "count")]
#[inline(always)]
pub fn counter(&self, name: &'static str) -> std::sync::Arc<Counter> {
self.registry.get_or_create_counter(name)
}
#[cfg(feature = "gauge")]
#[inline(always)]
pub fn gauge(&self, name: &'static str) -> std::sync::Arc<Gauge> {
self.registry.get_or_create_gauge(name)
}
#[cfg(feature = "timer")]
#[inline(always)]
pub fn timer(&self, name: &'static str) -> std::sync::Arc<Timer> {
self.registry.get_or_create_timer(name)
}
#[cfg(feature = "meter")]
#[inline(always)]
pub fn rate(&self, name: &'static str) -> std::sync::Arc<RateMeter> {
self.registry.get_or_create_rate_meter(name)
}
#[cfg(feature = "timer")]
#[inline]
pub fn time<T>(&self, name: &'static str, f: impl FnOnce() -> T) -> T {
let binding = self.timer(name);
let timer = binding.start();
let result = f();
timer.stop();
result
}
#[inline(always)]
pub fn system(&self) -> &SystemHealth {
&self.system
}
#[inline(always)]
pub fn registry(&self) -> &Registry {
&self.registry
}
}
impl Default for MetricsCore {
fn default() -> Self {
Self::new()
}
}
pub type Result<T> = std::result::Result<T, MetricsError>;
#[derive(Debug, Clone, PartialEq)]
pub enum MetricsError {
CircuitOpen,
Overloaded,
InvalidName,
InvalidValue {
reason: &'static str,
},
Overflow,
Underflow,
OverLimit,
WouldBlock,
NotInitialized,
Config(String),
}
impl std::fmt::Display for MetricsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MetricsError::CircuitOpen => write!(f, "Circuit breaker is open"),
MetricsError::Overloaded => write!(f, "System is overloaded"),
MetricsError::InvalidName => write!(f, "Invalid metric name"),
MetricsError::InvalidValue { reason } => write!(f, "Invalid value: {reason}"),
MetricsError::Overflow => write!(f, "Operation would overflow"),
MetricsError::Underflow => write!(f, "Operation would underflow"),
MetricsError::OverLimit => write!(f, "Operation would exceed limit"),
MetricsError::WouldBlock => write!(f, "Operation would block"),
MetricsError::NotInitialized => write!(f, "Global metrics not initialized"),
MetricsError::Config(msg) => write!(f, "Configuration error: {msg}"),
}
}
}
impl std::error::Error for MetricsError {}
pub mod prelude {
#[cfg(feature = "count")]
pub use crate::Counter;
#[cfg(feature = "gauge")]
pub use crate::Gauge;
#[cfg(feature = "meter")]
pub use crate::RateMeter;
#[cfg(feature = "timer")]
pub use crate::Timer;
pub use crate::{init, metrics, MetricsCore, MetricsError, Result, METRICS};
pub use crate::{Registry, SystemHealth};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_initialization() {
let metrics = MetricsCore::new();
let _cpu = metrics.system().cpu_used();
let _mem = metrics.system().mem_used_mb();
#[cfg(feature = "count")]
{
metrics.counter("test").inc();
assert_eq!(metrics.counter("test").get(), 1);
}
#[cfg(feature = "gauge")]
{
metrics.gauge("test").set(42.5);
assert_eq!(metrics.gauge("test").get(), 42.5);
}
}
#[cfg(feature = "count")]
#[test]
fn test_global_metrics() {
let _metrics = init();
metrics().counter("global_test").inc();
assert_eq!(metrics().counter("global_test").get(), 1);
}
#[cfg(feature = "timer")]
#[test]
fn test_time_function_records_and_returns() {
let metrics = MetricsCore::new();
let result = metrics.time("timed_op", || 123usize);
assert_eq!(result, 123);
assert_eq!(metrics.timer("timed_op").count(), 1);
}
#[cfg(feature = "count")]
#[test]
fn test_accessors_system_and_registry() {
let metrics = MetricsCore::new();
let _ = metrics.system().cpu_used();
let reg = metrics.registry();
let c = reg.get_or_create_counter("from_registry");
c.add(2);
assert_eq!(metrics.counter("from_registry").get(), 2);
}
#[cfg(feature = "count")]
#[test]
fn test_default_impl() {
let metrics: MetricsCore = Default::default();
metrics.counter("default_impl").inc();
assert_eq!(metrics.counter("default_impl").get(), 1);
}
#[test]
fn test_metrics_error_display() {
let e1 = MetricsError::CircuitOpen;
let e2 = MetricsError::Overloaded;
let e3 = MetricsError::InvalidName;
let e4 = MetricsError::Config("bad cfg".to_string());
let s1 = format!("{e1}");
let s2 = format!("{e2}");
let s3 = format!("{e3}");
let s4 = format!("{e4}");
assert!(s1.contains("Circuit breaker is open"));
assert!(s2.contains("System is overloaded"));
assert!(s3.contains("Invalid metric name"));
assert!(s4.contains("Configuration error"));
assert!(s4.contains("bad cfg"));
}
}