1use std::collections::HashMap;
4use std::time::{Duration, Instant};
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use crate::api::MetricValue;
10
11pub mod aggregator;
12pub mod collector;
13
14pub use aggregator::StatsAggregator;
15pub use collector::StatsCollector;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct StatsRecord {
20 pub id: String,
22
23 pub timestamp: DateTime<Utc>,
25
26 pub source: String,
28
29 pub metrics: HashMap<String, MetricValue>,
31
32 pub duration: Option<Duration>,
34
35 pub metadata: HashMap<String, String>,
37}
38
39#[derive(Debug)]
41pub struct Timer {
42 start: Instant,
43 name: String,
44}
45
46impl Timer {
47 pub fn start(name: impl Into<String>) -> Self {
49 Self {
50 start: Instant::now(),
51 name: name.into(),
52 }
53 }
54
55 pub fn stop(self) -> (String, Duration) {
57 (self.name, self.start.elapsed())
58 }
59
60 pub fn elapsed(&self) -> Duration {
62 self.start.elapsed()
63 }
64
65 pub fn name(&self) -> &str {
67 &self.name
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct MemoryStats {
74 pub peak_bytes: u64,
76
77 pub current_bytes: u64,
79
80 pub allocations: u64,
82
83 pub deallocations: u64,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct CpuStats {
90 pub usage_percent: f64,
92
93 pub user_time_ms: u64,
95
96 pub system_time_ms: u64,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct SystemStats {
103 pub memory: Option<MemoryStats>,
105
106 pub cpu: Option<CpuStats>,
108
109 pub load_avg: Option<[f64; 3]>,
111
112 pub uptime_seconds: Option<u64>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct StatsSummary {
119 pub count: u64,
121
122 pub time_range: (DateTime<Utc>, DateTime<Utc>),
124
125 pub metrics: HashMap<String, MetricSummary>,
127
128 pub sources: Vec<String>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct MetricSummary {
135 pub name: String,
137
138 pub min: f64,
140
141 pub max: f64,
143
144 pub avg: f64,
146
147 pub sum: f64,
149
150 pub count: u64,
152
153 pub std_dev: Option<f64>,
155
156 pub percentiles: Option<[f64; 4]>,
158}
159
160impl StatsRecord {
161 pub fn new(source: impl Into<String>) -> Self {
163 Self {
164 id: uuid::Uuid::new_v4().to_string(),
165 timestamp: Utc::now(),
166 source: source.into(),
167 metrics: HashMap::new(),
168 duration: None,
169 metadata: HashMap::new(),
170 }
171 }
172
173 pub fn add_metric(&mut self, name: impl Into<String>, value: MetricValue) {
175 self.metrics.insert(name.into(), value);
176 }
177
178 pub fn set_duration(&mut self, duration: Duration) {
180 self.duration = Some(duration);
181 }
182
183 pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
185 self.metadata.insert(key.into(), value.into());
186 }
187}
188
189pub mod utils {
191 pub fn percentile(sorted_values: &[f64], p: f64) -> f64 {
193 if sorted_values.is_empty() {
194 return 0.0;
195 }
196
197 let index = (p / 100.0) * (sorted_values.len() - 1) as f64;
198 let lower = index.floor() as usize;
199 let upper = index.ceil() as usize;
200
201 if lower == upper {
202 sorted_values[lower]
203 } else {
204 let weight = index - lower as f64;
205 sorted_values[lower] * (1.0 - weight) + sorted_values[upper] * weight
206 }
207 }
208
209 pub fn std_deviation(values: &[f64]) -> f64 {
211 if values.len() <= 1 {
212 return 0.0;
213 }
214
215 let mean = values.iter().sum::<f64>() / values.len() as f64;
216 let variance =
217 values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (values.len() - 1) as f64;
218
219 variance.sqrt()
220 }
221}