use std::collections::HashMap;
use std::time::{Duration, Instant};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::api::MetricValue;
pub mod aggregator;
pub mod collector;
pub use aggregator::StatsAggregator;
pub use collector::StatsCollector;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatsRecord {
pub id: String,
pub timestamp: DateTime<Utc>,
pub source: String,
pub metrics: HashMap<String, MetricValue>,
pub duration: Option<Duration>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug)]
pub struct Timer {
start: Instant,
name: String,
}
impl Timer {
pub fn start(name: impl Into<String>) -> Self {
Self {
start: Instant::now(),
name: name.into(),
}
}
pub fn stop(self) -> (String, Duration) {
(self.name, self.start.elapsed())
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn name(&self) -> &str {
&self.name
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStats {
pub peak_bytes: u64,
pub current_bytes: u64,
pub allocations: u64,
pub deallocations: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuStats {
pub usage_percent: f64,
pub user_time_ms: u64,
pub system_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemStats {
pub memory: Option<MemoryStats>,
pub cpu: Option<CpuStats>,
pub load_avg: Option<[f64; 3]>,
pub uptime_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatsSummary {
pub count: u64,
pub time_range: (DateTime<Utc>, DateTime<Utc>),
pub metrics: HashMap<String, MetricSummary>,
pub sources: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricSummary {
pub name: String,
pub min: f64,
pub max: f64,
pub avg: f64,
pub sum: f64,
pub count: u64,
pub std_dev: Option<f64>,
pub percentiles: Option<[f64; 4]>,
}
impl StatsRecord {
pub fn new(source: impl Into<String>) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
timestamp: Utc::now(),
source: source.into(),
metrics: HashMap::new(),
duration: None,
metadata: HashMap::new(),
}
}
pub fn add_metric(&mut self, name: impl Into<String>, value: MetricValue) {
self.metrics.insert(name.into(), value);
}
pub fn set_duration(&mut self, duration: Duration) {
self.duration = Some(duration);
}
pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.metadata.insert(key.into(), value.into());
}
}
pub mod utils {
pub fn percentile(sorted_values: &[f64], p: f64) -> f64 {
if sorted_values.is_empty() {
return 0.0;
}
let index = (p / 100.0) * (sorted_values.len() - 1) as f64;
let lower = index.floor() as usize;
let upper = index.ceil() as usize;
if lower == upper {
sorted_values[lower]
} else {
let weight = index - lower as f64;
sorted_values[lower] * (1.0 - weight) + sorted_values[upper] * weight
}
}
pub fn std_deviation(values: &[f64]) -> f64 {
if values.len() <= 1 {
return 0.0;
}
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance =
values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (values.len() - 1) as f64;
variance.sqrt()
}
}