Rust · 10112 bytes Raw Blame History
1 use serde::{Deserialize, Serialize};
2 use std::collections::HashMap;
3 use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
4 use tokio::sync::RwLock;
5 use std::sync::Arc;
6
7 #[derive(Debug, Clone, Serialize, Deserialize)]
8 pub struct Metrics {
9 pub commands: CommandMetrics,
10 pub network: NetworkMetrics,
11 pub storage: StorageMetrics,
12 pub performance: PerformanceMetrics,
13 }
14
15 #[derive(Debug, Clone, Serialize, Deserialize)]
16 pub struct CommandMetrics {
17 pub total_commands: u64,
18 pub commands_by_type: HashMap<String, u64>,
19 pub command_durations: HashMap<String, Vec<f64>>, // in milliseconds
20 pub failed_commands: u64,
21 pub last_command_time: Option<u64>, // unix timestamp
22 }
23
24 #[derive(Debug, Clone, Serialize, Deserialize)]
25 pub struct NetworkMetrics {
26 pub total_requests: u64,
27 pub failed_requests: u64,
28 pub average_response_time: f64, // milliseconds
29 pub timeouts: u64,
30 pub bytes_uploaded: u64,
31 pub bytes_downloaded: u64,
32 pub peer_connections: u32,
33 }
34
35 #[derive(Debug, Clone, Serialize, Deserialize)]
36 pub struct StorageMetrics {
37 pub files_uploaded: u64,
38 pub files_downloaded: u64,
39 pub total_bytes_processed: u64,
40 pub cache_hits: u64,
41 pub cache_misses: u64,
42 pub disk_operations: u64,
43 }
44
45 #[derive(Debug, Clone, Serialize, Deserialize)]
46 pub struct PerformanceMetrics {
47 pub memory_usage_mb: f64,
48 pub cpu_usage_percent: f64,
49 pub startup_time_ms: f64,
50 pub gc_collections: u32,
51 pub peak_memory_mb: f64,
52 }
53
54 #[derive(Debug)]
55 pub struct MetricsCollector {
56 metrics: Arc<RwLock<Metrics>>,
57 start_time: Instant,
58 }
59
60 impl Default for Metrics {
61 fn default() -> Self {
62 Self {
63 commands: CommandMetrics {
64 total_commands: 0,
65 commands_by_type: HashMap::new(),
66 command_durations: HashMap::new(),
67 failed_commands: 0,
68 last_command_time: None,
69 },
70 network: NetworkMetrics {
71 total_requests: 0,
72 failed_requests: 0,
73 average_response_time: 0.0,
74 timeouts: 0,
75 bytes_uploaded: 0,
76 bytes_downloaded: 0,
77 peer_connections: 0,
78 },
79 storage: StorageMetrics {
80 files_uploaded: 0,
81 files_downloaded: 0,
82 total_bytes_processed: 0,
83 cache_hits: 0,
84 cache_misses: 0,
85 disk_operations: 0,
86 },
87 performance: PerformanceMetrics {
88 memory_usage_mb: 0.0,
89 cpu_usage_percent: 0.0,
90 startup_time_ms: 0.0,
91 gc_collections: 0,
92 peak_memory_mb: 0.0,
93 },
94 }
95 }
96 }
97
98 impl MetricsCollector {
99 pub fn new() -> Self {
100 Self {
101 metrics: Arc::new(RwLock::new(Metrics::default())),
102 start_time: Instant::now(),
103 }
104 }
105
106 pub async fn record_command_start(&self, command: &str) -> CommandTimer {
107 let mut metrics = self.metrics.write().await;
108 metrics.commands.total_commands += 1;
109 *metrics.commands.commands_by_type.entry(command.to_string()).or_insert(0) += 1;
110 metrics.commands.last_command_time = Some(
111 SystemTime::now()
112 .duration_since(UNIX_EPOCH)
113 .unwrap_or_default()
114 .as_secs()
115 );
116
117 CommandTimer {
118 command: command.to_string(),
119 start_time: Instant::now(),
120 collector: self.metrics.clone(),
121 }
122 }
123
124 pub async fn record_network_request(&self, duration: Duration, success: bool, bytes_transferred: u64, upload: bool) {
125 let mut metrics = self.metrics.write().await;
126 metrics.network.total_requests += 1;
127
128 if !success {
129 metrics.network.failed_requests += 1;
130 }
131
132 // Update average response time (simple moving average)
133 let duration_ms = duration.as_secs_f64() * 1000.0;
134 let total_requests = metrics.network.total_requests as f64;
135 metrics.network.average_response_time =
136 (metrics.network.average_response_time * (total_requests - 1.0) + duration_ms) / total_requests;
137
138 if upload {
139 metrics.network.bytes_uploaded += bytes_transferred;
140 } else {
141 metrics.network.bytes_downloaded += bytes_transferred;
142 }
143 }
144
145 pub async fn record_timeout(&self) {
146 let mut metrics = self.metrics.write().await;
147 metrics.network.timeouts += 1;
148 metrics.network.failed_requests += 1;
149 }
150
151 pub async fn record_file_upload(&self, file_size: u64) {
152 let mut metrics = self.metrics.write().await;
153 metrics.storage.files_uploaded += 1;
154 metrics.storage.total_bytes_processed += file_size;
155 metrics.storage.disk_operations += 1;
156 }
157
158 pub async fn record_file_download(&self, file_size: u64) {
159 let mut metrics = self.metrics.write().await;
160 metrics.storage.files_downloaded += 1;
161 metrics.storage.total_bytes_processed += file_size;
162 metrics.storage.disk_operations += 1;
163 }
164
165 pub async fn record_cache_hit(&self) {
166 let mut metrics = self.metrics.write().await;
167 metrics.storage.cache_hits += 1;
168 }
169
170 pub async fn record_cache_miss(&self) {
171 let mut metrics = self.metrics.write().await;
172 metrics.storage.cache_misses += 1;
173 }
174
175 pub async fn update_peer_count(&self, peer_count: u32) {
176 let mut metrics = self.metrics.write().await;
177 metrics.network.peer_connections = peer_count;
178 }
179
180 pub async fn record_memory_usage(&self, memory_mb: f64) {
181 let mut metrics = self.metrics.write().await;
182 metrics.performance.memory_usage_mb = memory_mb;
183 if memory_mb > metrics.performance.peak_memory_mb {
184 metrics.performance.peak_memory_mb = memory_mb;
185 }
186 }
187
188 pub async fn record_cpu_usage(&self, cpu_percent: f64) {
189 let mut metrics = self.metrics.write().await;
190 metrics.performance.cpu_usage_percent = cpu_percent;
191 }
192
193 pub async fn get_metrics(&self) -> Metrics {
194 let metrics = self.metrics.read().await;
195 let mut result = metrics.clone();
196 result.performance.startup_time_ms = self.start_time.elapsed().as_secs_f64() * 1000.0;
197 result
198 }
199
200 pub async fn get_summary(&self) -> MetricsSummary {
201 let metrics = self.get_metrics().await;
202
203 MetricsSummary {
204 uptime_seconds: self.start_time.elapsed().as_secs(),
205 total_commands: metrics.commands.total_commands,
206 success_rate: if metrics.commands.total_commands > 0 {
207 ((metrics.commands.total_commands - metrics.commands.failed_commands) as f64
208 / metrics.commands.total_commands as f64) * 100.0
209 } else { 0.0 },
210 network_success_rate: if metrics.network.total_requests > 0 {
211 ((metrics.network.total_requests - metrics.network.failed_requests) as f64
212 / metrics.network.total_requests as f64) * 100.0
213 } else { 0.0 },
214 average_response_time_ms: metrics.network.average_response_time,
215 total_bytes_transferred: metrics.network.bytes_uploaded + metrics.network.bytes_downloaded,
216 files_processed: metrics.storage.files_uploaded + metrics.storage.files_downloaded,
217 cache_hit_rate: if metrics.storage.cache_hits + metrics.storage.cache_misses > 0 {
218 (metrics.storage.cache_hits as f64 /
219 (metrics.storage.cache_hits + metrics.storage.cache_misses) as f64) * 100.0
220 } else { 0.0 },
221 memory_usage_mb: metrics.performance.memory_usage_mb,
222 peak_memory_mb: metrics.performance.peak_memory_mb,
223 }
224 }
225
226 pub async fn export_metrics(&self) -> Result<String, serde_json::Error> {
227 let metrics = self.get_metrics().await;
228 serde_json::to_string_pretty(&metrics)
229 }
230
231 pub async fn reset_metrics(&self) {
232 let mut metrics = self.metrics.write().await;
233 *metrics = Metrics::default();
234 }
235 }
236
237 #[derive(Debug)]
238 pub struct CommandTimer {
239 command: String,
240 start_time: Instant,
241 collector: Arc<RwLock<Metrics>>,
242 }
243
244 impl Drop for CommandTimer {
245 fn drop(&mut self) {
246 let duration = self.start_time.elapsed();
247 let duration_ms = duration.as_secs_f64() * 1000.0;
248
249 // Use a blocking task to update metrics since we're in Drop
250 tokio::task::block_in_place(|| {
251 tokio::runtime::Handle::current().block_on(async {
252 let mut metrics = self.collector.write().await;
253 metrics.commands.command_durations
254 .entry(self.command.clone())
255 .or_insert_with(Vec::new)
256 .push(duration_ms);
257 });
258 });
259 }
260 }
261
262 #[derive(Debug, Serialize, Deserialize)]
263 pub struct MetricsSummary {
264 pub uptime_seconds: u64,
265 pub total_commands: u64,
266 pub success_rate: f64,
267 pub network_success_rate: f64,
268 pub average_response_time_ms: f64,
269 pub total_bytes_transferred: u64,
270 pub files_processed: u64,
271 pub cache_hit_rate: f64,
272 pub memory_usage_mb: f64,
273 pub peak_memory_mb: f64,
274 }
275
276 // Global metrics collector instance
277 lazy_static::lazy_static! {
278 pub static ref METRICS: MetricsCollector = MetricsCollector::new();
279 }
280
281 // Convenience macros for metrics collection
282 #[macro_export]
283 macro_rules! time_command {
284 ($command:expr) => {
285 crate::metrics::METRICS.record_command_start($command).await
286 };
287 }
288
289 #[macro_export]
290 macro_rules! record_network_success {
291 ($duration:expr, $bytes:expr, $upload:expr) => {
292 crate::metrics::METRICS.record_network_request($duration, true, $bytes, $upload).await
293 };
294 }
295
296 #[macro_export]
297 macro_rules! record_network_failure {
298 ($duration:expr, $bytes:expr, $upload:expr) => {
299 crate::metrics::METRICS.record_network_request($duration, false, $bytes, $upload).await
300 };
301 }