cstats_core/stats/
mod.rs

1//! Statistics collection and analysis
2
3use 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/// Statistics record containing collected metrics
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct StatsRecord {
20    /// Unique identifier for this record
21    pub id: String,
22
23    /// When the statistics were collected
24    pub timestamp: DateTime<Utc>,
25
26    /// Source identifier
27    pub source: String,
28
29    /// Collected metrics
30    pub metrics: HashMap<String, MetricValue>,
31
32    /// Duration of the measured operation
33    pub duration: Option<Duration>,
34
35    /// Additional metadata
36    pub metadata: HashMap<String, String>,
37}
38
39/// Timer for measuring execution time
40#[derive(Debug)]
41pub struct Timer {
42    start: Instant,
43    name: String,
44}
45
46impl Timer {
47    /// Start a new timer with the given name
48    pub fn start(name: impl Into<String>) -> Self {
49        Self {
50            start: Instant::now(),
51            name: name.into(),
52        }
53    }
54
55    /// Stop the timer and return the elapsed duration
56    pub fn stop(self) -> (String, Duration) {
57        (self.name, self.start.elapsed())
58    }
59
60    /// Get the elapsed time without stopping the timer
61    pub fn elapsed(&self) -> Duration {
62        self.start.elapsed()
63    }
64
65    /// Get the timer name
66    pub fn name(&self) -> &str {
67        &self.name
68    }
69}
70
71/// Memory usage statistics
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct MemoryStats {
74    /// Peak memory usage in bytes
75    pub peak_bytes: u64,
76
77    /// Current memory usage in bytes
78    pub current_bytes: u64,
79
80    /// Number of allocations
81    pub allocations: u64,
82
83    /// Number of deallocations
84    pub deallocations: u64,
85}
86
87/// CPU usage statistics
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct CpuStats {
90    /// CPU usage percentage
91    pub usage_percent: f64,
92
93    /// User time in milliseconds
94    pub user_time_ms: u64,
95
96    /// System time in milliseconds
97    pub system_time_ms: u64,
98}
99
100/// System statistics
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct SystemStats {
103    /// Memory statistics
104    pub memory: Option<MemoryStats>,
105
106    /// CPU statistics
107    pub cpu: Option<CpuStats>,
108
109    /// Load averages
110    pub load_avg: Option<[f64; 3]>,
111
112    /// Uptime in seconds
113    pub uptime_seconds: Option<u64>,
114}
115
116/// Statistics summary for aggregated data
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct StatsSummary {
119    /// Number of records processed
120    pub count: u64,
121
122    /// Time range of the summary
123    pub time_range: (DateTime<Utc>, DateTime<Utc>),
124
125    /// Aggregated metrics
126    pub metrics: HashMap<String, MetricSummary>,
127
128    /// Sources included in the summary
129    pub sources: Vec<String>,
130}
131
132/// Summary statistics for a specific metric
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct MetricSummary {
135    /// Metric name
136    pub name: String,
137
138    /// Minimum value
139    pub min: f64,
140
141    /// Maximum value
142    pub max: f64,
143
144    /// Average value
145    pub avg: f64,
146
147    /// Sum of all values
148    pub sum: f64,
149
150    /// Number of data points
151    pub count: u64,
152
153    /// Standard deviation
154    pub std_dev: Option<f64>,
155
156    /// Percentiles (50th, 90th, 95th, 99th)
157    pub percentiles: Option<[f64; 4]>,
158}
159
160impl StatsRecord {
161    /// Create a new statistics record
162    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    /// Add a metric to the record
174    pub fn add_metric(&mut self, name: impl Into<String>, value: MetricValue) {
175        self.metrics.insert(name.into(), value);
176    }
177
178    /// Set the duration for the record
179    pub fn set_duration(&mut self, duration: Duration) {
180        self.duration = Some(duration);
181    }
182
183    /// Add metadata to the record
184    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
189/// Utility functions for statistics calculations
190pub mod utils {
191    /// Calculate percentile from a sorted vector of values
192    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    /// Calculate standard deviation
210    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}