#![forbid(unsafe_code)]
use std::sync::Arc;
use std::time::Duration;
pub trait IoMetrics: Send + Sync {
fn observe_io_duration(&self, _io_type: &str, _duration: Duration) {}
fn inc_io_errors(&self, _io_type: &str) {}
fn inc_sessions(&self) {}
fn dec_sessions(&self) {}
fn set_sessions(&self, _n: i64) {}
fn inc_commands(&self, _instruction_type: &str) {}
fn set_facts_log_version(&self, _version: u64) {}
fn inc_sse_connections(&self) {}
fn dec_sse_connections(&self) {}
fn set_sse_connections(&self, _n: i64) {}
fn inc_http_requests(&self, _method: &str, _path: &str, _status: &str) {}
fn render_as_text(&self) -> String {
String::new()
}
}
#[derive(Debug, Default, Clone)]
pub struct NoOpMetrics;
impl IoMetrics for NoOpMetrics {
fn observe_io_duration(&self, _io_type: &str, _duration: Duration) {}
fn inc_io_errors(&self, _io_type: &str) {}
}
pub type SharedMetrics = Arc<dyn IoMetrics>;
pub fn noop_metrics() -> SharedMetrics {
Arc::new(NoOpMetrics)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic, clippy::expect_used)]
use super::*;
#[test]
fn test_noop_metrics_does_not_panic() {
let m = NoOpMetrics;
m.observe_io_duration("call_external", Duration::from_millis(100));
m.inc_io_errors("call_external");
m.inc_sessions();
m.dec_sessions();
m.set_sessions(5);
m.inc_commands("increment");
m.set_facts_log_version(42);
m.inc_sse_connections();
m.dec_sse_connections();
m.set_sse_connections(3);
m.inc_http_requests("GET", "/api/health", "200");
}
#[test]
fn test_noop_metrics_render_returns_empty() {
let m = NoOpMetrics;
assert_eq!(m.render_as_text(), "");
}
#[test]
fn test_shared_metrics_via_trait_object() {
let m: SharedMetrics = noop_metrics();
m.observe_io_duration("test", Duration::from_secs(0));
m.inc_io_errors("test");
m.inc_sessions();
m.set_facts_log_version(1);
assert_eq!(m.render_as_text(), "");
}
#[test]
fn test_shared_metrics_clone_preserves_behavior() {
let m1: SharedMetrics = noop_metrics();
let m2 = m1.clone();
m1.observe_io_duration("a", Duration::from_millis(1));
m2.observe_io_duration("b", Duration::from_millis(2));
}
struct CountingMetrics {
count: std::sync::Arc<std::sync::atomic::AtomicU32>,
}
impl IoMetrics for CountingMetrics {
fn inc_sessions(&self) {
self.count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
}
#[test]
fn test_custom_metrics_can_be_injected() {
let counter = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
let m: SharedMetrics = Arc::new(CountingMetrics {
count: counter.clone(),
});
m.inc_sessions();
m.inc_sessions();
m.inc_sessions();
assert_eq!(counter.load(std::sync::atomic::Ordering::SeqCst), 3);
}
}