use std::collections::HashMap;
use std::time::{Duration, Instant};
pub struct Metrics {
timings: HashMap<String, Vec<Duration>>,
cache_hits: u64,
cache_misses: u64,
start_time: Instant,
}
impl Metrics {
pub fn new() -> Self {
Self {
timings: HashMap::new(),
cache_hits: 0,
cache_misses: 0,
start_time: Instant::now(),
}
}
pub fn record_timing(&mut self, operation: &str, duration: Duration) {
self.timings
.entry(operation.to_string())
.or_insert_with(Vec::new)
.push(duration);
}
pub fn timed<F, T>(&mut self, operation: &str, f: F) -> T
where
F: FnOnce() -> T,
{
let start = Instant::now();
let result = f();
self.record_timing(operation, start.elapsed());
result
}
pub fn record_cache_hit(&mut self) {
self.cache_hits += 1;
}
pub fn record_cache_miss(&mut self) {
self.cache_misses += 1;
}
pub fn cache_hit_rate(&self) -> f64 {
let total = self.cache_hits + self.cache_misses;
if total == 0 {
0.0
} else {
self.cache_hits as f64 / total as f64
}
}
pub fn avg_timing(&self, operation: &str) -> Option<Duration> {
self.timings.get(operation).map(|durations| {
if durations.is_empty() {
Duration::ZERO
} else {
let total: Duration = durations.iter().sum();
total / durations.len() as u32
}
})
}
pub fn uptime(&self) -> Duration {
self.start_time.elapsed()
}
pub fn summary(&self) -> MetricsSummary {
MetricsSummary {
uptime: self.uptime(),
cache_hit_rate: self.cache_hit_rate(),
total_cache_ops: self.cache_hits + self.cache_misses,
operation_counts: self.timings
.iter()
.map(|(k, v)| (k.clone(), v.len()))
.collect(),
}
}
pub fn reset(&mut self) {
self.timings.clear();
self.cache_hits = 0;
self.cache_misses = 0;
}
}
impl Default for Metrics {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct MetricsSummary {
pub uptime: Duration,
pub cache_hit_rate: f64,
pub total_cache_ops: u64,
pub operation_counts: HashMap<String, usize>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_hit_rate() {
let mut metrics = Metrics::new();
metrics.record_cache_hit();
metrics.record_cache_hit();
metrics.record_cache_miss();
assert!((metrics.cache_hit_rate() - 0.666).abs() < 0.01);
}
#[test]
fn test_timing() {
let mut metrics = Metrics::new();
let result = metrics.timed("test_op", || {
std::thread::sleep(Duration::from_millis(10));
42
});
assert_eq!(result, 42);
assert!(metrics.avg_timing("test_op").unwrap() >= Duration::from_millis(10));
}
}