1use anyhow::Result;
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9use std::time::{Duration, Instant};
10use sysinfo::System;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct OperationMetrics {
15 pub operation_name: String,
16 pub duration: Duration,
17 pub timestamp: DateTime<Utc>,
18 pub success: bool,
19 pub error_message: Option<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct PerformanceStats {
25 pub operation_name: String,
26 pub total_calls: u64,
27 pub successful_calls: u64,
28 pub failed_calls: u64,
29 pub total_duration: Duration,
30 pub average_duration: Duration,
31 pub min_duration: Duration,
32 pub max_duration: Duration,
33 pub success_rate: f64,
34 pub last_called: Option<DateTime<Utc>>,
35}
36
37impl PerformanceStats {
38 pub fn new(operation_name: String) -> Self {
39 Self {
40 operation_name,
41 total_calls: 0,
42 successful_calls: 0,
43 failed_calls: 0,
44 total_duration: Duration::ZERO,
45 average_duration: Duration::ZERO,
46 min_duration: Duration::MAX,
47 max_duration: Duration::ZERO,
48 success_rate: 0.0,
49 last_called: None,
50 }
51 }
52
53 pub fn add_metric(&mut self, metric: &OperationMetrics) {
54 self.total_calls += 1;
55 self.total_duration += metric.duration;
56 self.last_called = Some(metric.timestamp);
57
58 if metric.success {
59 self.successful_calls += 1;
60 } else {
61 self.failed_calls += 1;
62 }
63
64 if metric.duration < self.min_duration {
65 self.min_duration = metric.duration;
66 }
67 if metric.duration > self.max_duration {
68 self.max_duration = metric.duration;
69 }
70
71 self.average_duration =
72 Duration::from_nanos(self.total_duration.as_nanos() as u64 / self.total_calls);
73
74 self.success_rate = if self.total_calls > 0 {
75 self.successful_calls as f64 / self.total_calls as f64
76 } else {
77 0.0
78 };
79 }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct SystemMetrics {
85 pub timestamp: DateTime<Utc>,
86 pub memory_usage_mb: f64,
87 pub cpu_usage_percent: f64,
88 pub available_memory_mb: f64,
89 pub total_memory_mb: f64,
90}
91
92pub struct PerformanceMonitor {
94 metrics: Arc<RwLock<Vec<OperationMetrics>>>,
96 stats: Arc<RwLock<HashMap<String, PerformanceStats>>>,
98 system: Arc<RwLock<System>>,
100 max_metrics: usize,
102}
103
104impl PerformanceMonitor {
105 pub fn new(max_metrics: usize) -> Self {
107 Self {
108 metrics: Arc::new(RwLock::new(Vec::new())),
109 stats: Arc::new(RwLock::new(HashMap::new())),
110 system: Arc::new(RwLock::new(System::new_all())),
111 max_metrics,
112 }
113 }
114
115 pub fn new_default() -> Self {
117 Self::new(10000) }
119
120 pub fn start_operation(&self, operation_name: &str) -> OperationTimer {
122 OperationTimer {
123 monitor: self.clone(),
124 operation_name: operation_name.to_string(),
125 start_time: Instant::now(),
126 }
127 }
128
129 pub fn record_operation(&self, metric: OperationMetrics) {
131 {
133 let mut metrics = self.metrics.write();
134 metrics.push(metric.clone());
135
136 if metrics.len() > self.max_metrics {
138 let excess = metrics.len() - self.max_metrics;
139 metrics.drain(0..excess);
140 }
141 }
142
143 {
145 let mut stats = self.stats.write();
146 let operation_stats = stats
147 .entry(metric.operation_name.clone())
148 .or_insert_with(|| PerformanceStats::new(metric.operation_name.clone()));
149 operation_stats.add_metric(&metric);
150 }
151 }
152
153 pub fn get_metrics(&self) -> Vec<OperationMetrics> {
155 self.metrics.read().clone()
156 }
157
158 pub fn get_all_stats(&self) -> HashMap<String, PerformanceStats> {
160 self.stats.read().clone()
161 }
162
163 pub fn get_operation_stats(&self, operation_name: &str) -> Option<PerformanceStats> {
165 self.stats.read().get(operation_name).cloned()
166 }
167
168 pub fn get_system_metrics(&self) -> Result<SystemMetrics> {
170 let mut system = self.system.write();
171 system.refresh_all();
172
173 Ok(SystemMetrics {
174 timestamp: Utc::now(),
175 memory_usage_mb: system.used_memory() as f64 / 1024.0 / 1024.0,
176 cpu_usage_percent: system
177 .cpus()
178 .iter()
179 .map(|cpu| cpu.cpu_usage() as f64)
180 .sum::<f64>()
181 / system.cpus().len() as f64,
182 available_memory_mb: system.available_memory() as f64 / 1024.0 / 1024.0,
183 total_memory_mb: system.total_memory() as f64 / 1024.0 / 1024.0,
184 })
185 }
186
187 pub fn clear(&self) {
189 self.metrics.write().clear();
190 self.stats.write().clear();
191 }
192
193 pub fn get_summary(&self) -> PerformanceSummary {
195 let stats = self.get_all_stats();
196 let total_operations: u64 = stats.values().map(|s| s.total_calls).sum();
197 let total_successful: u64 = stats.values().map(|s| s.successful_calls).sum();
198 let total_duration: Duration = stats.values().map(|s| s.total_duration).sum();
199
200 PerformanceSummary {
201 total_operations,
202 total_successful,
203 total_failed: total_operations - total_successful,
204 overall_success_rate: if total_operations > 0 {
205 total_successful as f64 / total_operations as f64
206 } else {
207 0.0
208 },
209 total_duration,
210 average_operation_duration: if total_operations > 0 {
211 Duration::from_nanos(total_duration.as_nanos() as u64 / total_operations)
212 } else {
213 Duration::ZERO
214 },
215 operation_count: stats.len(),
216 }
217 }
218}
219
220impl Clone for PerformanceMonitor {
221 fn clone(&self) -> Self {
222 Self {
223 metrics: Arc::clone(&self.metrics),
224 stats: Arc::clone(&self.stats),
225 system: Arc::clone(&self.system),
226 max_metrics: self.max_metrics,
227 }
228 }
229}
230
231pub struct OperationTimer {
233 monitor: PerformanceMonitor,
234 operation_name: String,
235 start_time: Instant,
236}
237
238impl OperationTimer {
239 pub fn success(self) {
241 let duration = self.start_time.elapsed();
242 let metric = OperationMetrics {
243 operation_name: self.operation_name,
244 duration,
245 timestamp: Utc::now(),
246 success: true,
247 error_message: None,
248 };
249 self.monitor.record_operation(metric);
250 }
251
252 pub fn error(self, error_message: String) {
254 let duration = self.start_time.elapsed();
255 let metric = OperationMetrics {
256 operation_name: self.operation_name,
257 duration,
258 timestamp: Utc::now(),
259 success: false,
260 error_message: Some(error_message),
261 };
262 self.monitor.record_operation(metric);
263 }
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct PerformanceSummary {
269 pub total_operations: u64,
270 pub total_successful: u64,
271 pub total_failed: u64,
272 pub overall_success_rate: f64,
273 pub total_duration: Duration,
274 pub average_operation_duration: Duration,
275 pub operation_count: usize,
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use std::thread;
282
283 #[test]
284 fn test_performance_monitor() {
285 let monitor = PerformanceMonitor::new_default();
286
287 let metric1 = OperationMetrics {
289 operation_name: "test_op".to_string(),
290 duration: Duration::from_millis(100),
291 timestamp: Utc::now(),
292 success: true,
293 error_message: None,
294 };
295
296 monitor.record_operation(metric1);
297
298 let stats = monitor.get_operation_stats("test_op");
299 assert!(stats.is_some());
300 let stats = stats.unwrap();
301 assert_eq!(stats.total_calls, 1);
302 assert_eq!(stats.successful_calls, 1);
303 assert_eq!(stats.failed_calls, 0);
304 }
305
306 #[test]
307 fn test_operation_timer() {
308 let monitor = PerformanceMonitor::new_default();
309
310 let timer = monitor.start_operation("test_timer");
312 thread::sleep(Duration::from_millis(10));
313 timer.success();
314
315 let stats = monitor.get_operation_stats("test_timer");
316 assert!(stats.is_some());
317 let stats = stats.unwrap();
318 assert_eq!(stats.total_calls, 1);
319 assert!(stats.successful_calls > 0);
320 }
321
322 #[test]
323 fn test_performance_monitor_failed_operation() {
324 let monitor = PerformanceMonitor::new_default();
325
326 let metric = OperationMetrics {
328 operation_name: "failed_op".to_string(),
329 duration: Duration::from_millis(50),
330 timestamp: Utc::now(),
331 success: false,
332 error_message: Some("Test error".to_string()),
333 };
334
335 monitor.record_operation(metric);
336
337 let stats = monitor.get_operation_stats("failed_op");
338 assert!(stats.is_some());
339 let stats = stats.unwrap();
340 assert_eq!(stats.total_calls, 1);
341 assert_eq!(stats.successful_calls, 0);
342 assert_eq!(stats.failed_calls, 1);
343 }
344
345 #[test]
346 fn test_performance_monitor_multiple_operations() {
347 let monitor = PerformanceMonitor::new_default();
348
349 for i in 0..5 {
351 let metric = OperationMetrics {
352 operation_name: "multi_op".to_string(),
353 duration: Duration::from_millis(i * 10),
354 timestamp: Utc::now(),
355 success: i % 2 == 0,
356 error_message: if i % 2 == 0 {
357 None
358 } else {
359 Some("Error".to_string())
360 },
361 };
362 monitor.record_operation(metric);
363 }
364
365 let stats = monitor.get_operation_stats("multi_op");
366 assert!(stats.is_some());
367 let stats = stats.unwrap();
368 assert_eq!(stats.total_calls, 5);
369 assert_eq!(stats.successful_calls, 3);
370 assert_eq!(stats.failed_calls, 2);
371 }
372
373 #[test]
374 fn test_performance_monitor_get_all_stats() {
375 let monitor = PerformanceMonitor::new_default();
376
377 let operations = vec![("op1", true), ("op1", false), ("op2", true), ("op2", true)];
379
380 for (name, success) in operations {
381 let metric = OperationMetrics {
382 operation_name: name.to_string(),
383 duration: Duration::from_millis(100),
384 timestamp: Utc::now(),
385 success,
386 error_message: if success {
387 None
388 } else {
389 Some("Error".to_string())
390 },
391 };
392 monitor.record_operation(metric);
393 }
394
395 let all_stats = monitor.get_all_stats();
396 assert_eq!(all_stats.len(), 2);
397 assert!(all_stats.contains_key("op1"));
398 assert!(all_stats.contains_key("op2"));
399
400 let op1_stats = &all_stats["op1"];
401 assert_eq!(op1_stats.total_calls, 2);
402 assert_eq!(op1_stats.successful_calls, 1);
403 assert_eq!(op1_stats.failed_calls, 1);
404
405 let op2_stats = &all_stats["op2"];
406 assert_eq!(op2_stats.total_calls, 2);
407 assert_eq!(op2_stats.successful_calls, 2);
408 assert_eq!(op2_stats.failed_calls, 0);
409 }
410
411 #[test]
412 fn test_performance_monitor_get_summary() {
413 let monitor = PerformanceMonitor::new_default();
414
415 let operations = vec![("op1", true, 100), ("op1", false, 200), ("op2", true, 150)];
417
418 for (name, success, duration_ms) in operations {
419 let metric = OperationMetrics {
420 operation_name: name.to_string(),
421 duration: Duration::from_millis(duration_ms),
422 timestamp: Utc::now(),
423 success,
424 error_message: if success {
425 None
426 } else {
427 Some("Error".to_string())
428 },
429 };
430 monitor.record_operation(metric);
431 }
432
433 let summary = monitor.get_summary();
434 assert_eq!(summary.total_operations, 3);
435 assert_eq!(summary.total_successful, 2);
436 assert_eq!(summary.total_failed, 1);
437 assert!((summary.overall_success_rate - 2.0 / 3.0).abs() < 0.001);
438 assert_eq!(summary.operation_count, 2);
439 }
440
441 #[test]
442 fn test_performance_monitor_get_summary_empty() {
443 let monitor = PerformanceMonitor::new_default();
444 let summary = monitor.get_summary();
445
446 assert_eq!(summary.total_operations, 0);
447 assert_eq!(summary.total_successful, 0);
448 assert_eq!(summary.total_failed, 0);
449 assert_eq!(summary.overall_success_rate, 0.0);
450 assert_eq!(summary.operation_count, 0);
451 }
452
453 #[test]
454 fn test_operation_timer_failure() {
455 let monitor = PerformanceMonitor::new_default();
456
457 let metric = OperationMetrics {
459 operation_name: "test_failure".to_string(),
460 duration: Duration::from_millis(5),
461 timestamp: Utc::now(),
462 success: false,
463 error_message: Some("Test failure".to_string()),
464 };
465 monitor.record_operation(metric);
466
467 let stats = monitor.get_operation_stats("test_failure");
468 assert!(stats.is_some());
469 let stats = stats.unwrap();
470 assert_eq!(stats.total_calls, 1);
471 assert_eq!(stats.successful_calls, 0);
472 assert_eq!(stats.failed_calls, 1);
473 }
474
475 #[test]
476 fn test_operation_timer_drop() {
477 let monitor = PerformanceMonitor::new_default();
478
479 {
481 let timer = monitor.start_operation("test_drop");
482 thread::sleep(Duration::from_millis(5));
483 timer.success();
485 }
486
487 let stats = monitor.get_operation_stats("test_drop");
488 assert!(stats.is_some());
489 let stats = stats.unwrap();
490 assert_eq!(stats.total_calls, 1);
491 assert_eq!(stats.successful_calls, 1);
492 assert_eq!(stats.failed_calls, 0);
493 }
494
495 #[test]
496 fn test_performance_monitor_clone() {
497 let monitor1 = PerformanceMonitor::new_default();
498
499 let metric = OperationMetrics {
501 operation_name: "clone_test".to_string(),
502 duration: Duration::from_millis(100),
503 timestamp: Utc::now(),
504 success: true,
505 error_message: None,
506 };
507 monitor1.record_operation(metric);
508
509 let monitor2 = monitor1.clone();
511
512 let stats1 = monitor1.get_operation_stats("clone_test");
514 let stats2 = monitor2.get_operation_stats("clone_test");
515
516 assert!(stats1.is_some());
517 assert!(stats2.is_some());
518 assert_eq!(stats1.unwrap().total_calls, stats2.unwrap().total_calls);
519 }
520}