Skip to main content

shadow_utils/
metrics.rs

1//! Network and node metrics
2
3use std::sync::atomic::{AtomicU64, Ordering};
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6
7/// Network metrics
8pub struct Metrics {
9    /// Packets sent
10    packets_sent: Arc<AtomicU64>,
11    /// Packets received
12    packets_received: Arc<AtomicU64>,
13    /// Bytes sent
14    bytes_sent: Arc<AtomicU64>,
15    /// Bytes received
16    bytes_received: Arc<AtomicU64>,
17    /// Failed operations
18    errors: Arc<AtomicU64>,
19    /// Start time
20    start_time: Instant,
21}
22
23impl Metrics {
24    /// Create new metrics tracker
25    pub fn new() -> Self {
26        Self {
27            packets_sent: Arc::new(AtomicU64::new(0)),
28            packets_received: Arc::new(AtomicU64::new(0)),
29            bytes_sent: Arc::new(AtomicU64::new(0)),
30            bytes_received: Arc::new(AtomicU64::new(0)),
31            errors: Arc::new(AtomicU64::new(0)),
32            start_time: Instant::now(),
33        }
34    }
35
36    /// Record packet sent
37    pub fn record_packet_sent(&self, size: usize) {
38        self.packets_sent.fetch_add(1, Ordering::Relaxed);
39        self.bytes_sent.fetch_add(size as u64, Ordering::Relaxed);
40    }
41
42    /// Record packet received
43    pub fn record_packet_received(&self, size: usize) {
44        self.packets_received.fetch_add(1, Ordering::Relaxed);
45        self.bytes_received.fetch_add(size as u64, Ordering::Relaxed);
46    }
47
48    /// Record error
49    pub fn record_error(&self) {
50        self.errors.fetch_add(1, Ordering::Relaxed);
51    }
52
53    /// Get snapshot of current metrics
54    pub fn snapshot(&self) -> MetricsSnapshot {
55        MetricsSnapshot {
56            packets_sent: self.packets_sent.load(Ordering::Relaxed),
57            packets_received: self.packets_received.load(Ordering::Relaxed),
58            bytes_sent: self.bytes_sent.load(Ordering::Relaxed),
59            bytes_received: self.bytes_received.load(Ordering::Relaxed),
60            errors: self.errors.load(Ordering::Relaxed),
61            uptime: self.start_time.elapsed(),
62        }
63    }
64
65    /// Reset all metrics
66    pub fn reset(&self) {
67        self.packets_sent.store(0, Ordering::Relaxed);
68        self.packets_received.store(0, Ordering::Relaxed);
69        self.bytes_sent.store(0, Ordering::Relaxed);
70        self.bytes_received.store(0, Ordering::Relaxed);
71        self.errors.store(0, Ordering::Relaxed);
72    }
73}
74
75impl Default for Metrics {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81impl Clone for Metrics {
82    fn clone(&self) -> Self {
83        Self {
84            packets_sent: self.packets_sent.clone(),
85            packets_received: self.packets_received.clone(),
86            bytes_sent: self.bytes_sent.clone(),
87            bytes_received: self.bytes_received.clone(),
88            errors: self.errors.clone(),
89            start_time: self.start_time,
90        }
91    }
92}
93
94/// Snapshot of metrics at a point in time
95#[derive(Debug, Clone)]
96pub struct MetricsSnapshot {
97    pub packets_sent: u64,
98    pub packets_received: u64,
99    pub bytes_sent: u64,
100    pub bytes_received: u64,
101    pub errors: u64,
102    pub uptime: Duration,
103}
104
105impl MetricsSnapshot {
106    /// Calculate packets per second
107    pub fn packets_per_second(&self) -> f64 {
108        let total_packets = self.packets_sent + self.packets_received;
109        total_packets as f64 / self.uptime.as_secs_f64()
110    }
111
112    /// Calculate bytes per second
113    pub fn bytes_per_second(&self) -> f64 {
114        let total_bytes = self.bytes_sent + self.bytes_received;
115        total_bytes as f64 / self.uptime.as_secs_f64()
116    }
117
118    /// Calculate error rate
119    pub fn error_rate(&self) -> f64 {
120        let total_ops = self.packets_sent + self.packets_received;
121        if total_ops == 0 {
122            0.0
123        } else {
124            self.errors as f64 / total_ops as f64
125        }
126    }
127
128    /// Format as human-readable string
129    pub fn format(&self) -> String {
130        format!(
131            "Packets: {} sent, {} received | Bytes: {} sent, {} received | Errors: {} | Uptime: {:?}",
132            self.packets_sent,
133            self.packets_received,
134            self.bytes_sent,
135            self.bytes_received,
136            self.errors,
137            self.uptime
138        )
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_metrics_tracking() {
148        let metrics = Metrics::new();
149        
150        metrics.record_packet_sent(100);
151        metrics.record_packet_received(200);
152        
153        let snapshot = metrics.snapshot();
154        
155        assert_eq!(snapshot.packets_sent, 1);
156        assert_eq!(snapshot.packets_received, 1);
157        assert_eq!(snapshot.bytes_sent, 100);
158        assert_eq!(snapshot.bytes_received, 200);
159    }
160
161    #[test]
162    fn test_metrics_reset() {
163        let metrics = Metrics::new();
164        
165        metrics.record_packet_sent(100);
166        metrics.reset();
167        
168        let snapshot = metrics.snapshot();
169        assert_eq!(snapshot.packets_sent, 0);
170    }
171
172    #[test]
173    fn test_snapshot_calculations() {
174        let snapshot = MetricsSnapshot {
175            packets_sent: 100,
176            packets_received: 100,
177            bytes_sent: 10000,
178            bytes_received: 10000,
179            errors: 5,
180            uptime: Duration::from_secs(10),
181        };
182        
183        assert_eq!(snapshot.packets_per_second(), 20.0);
184        assert_eq!(snapshot.bytes_per_second(), 2000.0);
185        assert!((snapshot.error_rate() - 0.025).abs() < 0.001);
186    }
187}