1use crate::error_handling_v2::ErrorCode;
7use std::collections::{HashMap, VecDeque};
8use std::sync::{
9 atomic::{AtomicUsize, Ordering},
10 Arc, Mutex,
11};
12use std::time::{Duration, Instant, SystemTime};
13
14#[derive(Debug, Clone)]
16pub struct ErrorPattern {
17 pub id: String,
19 pub error_codes: Vec<ErrorCode>,
21 pub frequency_threshold: usize,
23 pub time_window: Duration,
25 pub confidence: f64,
27 pub description: String,
29 pub mitigation: String,
31}
32
33impl ErrorPattern {
34 pub fn new(
36 id: impl Into<String>,
37 error_codes: Vec<ErrorCode>,
38 frequency_threshold: usize,
39 time_window: Duration,
40 description: impl Into<String>,
41 mitigation: impl Into<String>,
42 ) -> Self {
43 Self {
44 id: id.into(),
45 error_codes,
46 frequency_threshold,
47 time_window,
48 confidence: 0.0,
49 description: description.into(),
50 mitigation: mitigation.into(),
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
57pub struct ErrorOccurrence {
58 pub code: ErrorCode,
60 pub timestamp: Instant,
62 pub operation: String,
64 pub count: usize,
66 pub resolved: bool,
68 pub recovery_action: Option<String>,
70}
71
72pub struct ErrorMonitor {
74 error_history: Arc<Mutex<VecDeque<ErrorOccurrence>>>,
76 error_counts: Arc<Mutex<HashMap<ErrorCode, AtomicUsize>>>,
78 patterns: Vec<ErrorPattern>,
80 max_historysize: usize,
82 pattern_detection_enabled: bool,
84 error_rate_thresholds: HashMap<ErrorCode, f64>,
86 start_time: Instant,
88}
89
90impl ErrorMonitor {
91 pub fn new() -> Self {
93 let mut monitor = Self {
94 error_history: Arc::new(Mutex::new(VecDeque::new())),
95 error_counts: Arc::new(Mutex::new(HashMap::new())),
96 patterns: Vec::new(),
97 max_historysize: 1000,
98 pattern_detection_enabled: true,
99 error_rate_thresholds: HashMap::new(),
100 start_time: Instant::now(),
101 };
102
103 monitor.initialize_default_patterns();
104 monitor.initialize_default_thresholds();
105 monitor
106 }
107
108 fn initialize_default_patterns(&mut self) {
110 self.patterns.push(ErrorPattern::new(
112 "memory_pressure",
113 vec![ErrorCode::E5001, ErrorCode::E5002],
114 3,
115 Duration::from_secs(60),
116 "High memory allocation failures indicating memory pressure",
117 "Reduce data size, enable streaming processing, or increase available memory",
118 ));
119
120 self.patterns.push(ErrorPattern::new(
122 "numerical_instability",
123 vec![
124 ErrorCode::E3001,
125 ErrorCode::E3002,
126 ErrorCode::E3005,
127 ErrorCode::E3006,
128 ],
129 5,
130 Duration::from_secs(30),
131 "Frequent numerical errors indicating data quality or algorithm issues",
132 "Check data preprocessing, scaling, and consider more stable algorithms",
133 ));
134
135 self.patterns.push(ErrorPattern::new(
137 "convergence_issues",
138 vec![ErrorCode::E3003, ErrorCode::E4001, ErrorCode::E4002],
139 3,
140 Duration::from_secs(120),
141 "Repeated convergence failures in iterative algorithms",
142 "Adjust algorithm parameters, improve initial conditions, or use different methods",
143 ));
144
145 self.patterns.push(ErrorPattern::new(
147 "data_quality_issues",
148 vec![
149 ErrorCode::E2003,
150 ErrorCode::E2004,
151 ErrorCode::E1001,
152 ErrorCode::E1002,
153 ],
154 4,
155 Duration::from_secs(60),
156 "Frequent data validation errors indicating poor data quality",
157 "Implement comprehensive data validation and cleaning pipeline",
158 ));
159 }
160
161 fn initialize_default_thresholds(&mut self) {
163 self.error_rate_thresholds.insert(ErrorCode::E5001, 0.01); self.error_rate_thresholds.insert(ErrorCode::E3001, 0.05); self.error_rate_thresholds.insert(ErrorCode::E3005, 0.10); self.error_rate_thresholds.insert(ErrorCode::E4001, 0.20); }
168
169 pub fn record_error(&self, code: ErrorCode, operation: impl Into<String>) {
171 let occurrence = ErrorOccurrence {
172 code,
173 timestamp: Instant::now(),
174 operation: operation.into(),
175 count: 1,
176 resolved: false,
177 recovery_action: None,
178 };
179
180 {
182 let mut history = self.error_history.lock().expect("Operation failed");
183 if history.len() >= self.max_historysize {
184 history.pop_front();
185 }
186 history.push_back(occurrence);
187 }
188
189 {
191 let mut counts = self.error_counts.lock().expect("Operation failed");
192 counts
193 .entry(code)
194 .or_insert_with(|| AtomicUsize::new(0))
195 .fetch_add(1, Ordering::Relaxed);
196 }
197
198 if self.pattern_detection_enabled {
200 self.check_patterns();
201 }
202 }
203
204 fn check_patterns(&self) {
206 let history = self.error_history.lock().expect("Operation failed");
207 let now = Instant::now();
208
209 for pattern in &self.patterns {
210 let relevant_errors: Vec<_> = history
211 .iter()
212 .filter(|err| {
213 pattern.error_codes.contains(&err.code)
214 && now.duration_since(err.timestamp) <= pattern.time_window
215 })
216 .collect();
217
218 if relevant_errors.len() >= pattern.frequency_threshold {
219 eprintln!(
220 "ā ļø ERROR PATTERN DETECTED: {} - {} ({})",
221 pattern.id, pattern.description, pattern.mitigation
222 );
223 }
224 }
225 }
226
227 pub fn get_statistics(&self) -> ErrorStatistics {
229 let counts = self.error_counts.lock().expect("Operation failed");
230 let history = self.error_history.lock().expect("Operation failed");
231
232 let total_errors: usize = counts
233 .values()
234 .map(|counter| counter.load(Ordering::Relaxed))
235 .sum();
236
237 let uptime = self.start_time.elapsed();
238 let error_rate = total_errors as f64 / uptime.as_secs_f64();
239
240 let mut error_distribution = HashMap::new();
242 for (code, counter) in counts.iter() {
243 let count = counter.load(Ordering::Relaxed);
244 if count > 0 {
245 error_distribution.insert(*code, count);
246 }
247 }
248
249 let mut frequent_errors: Vec<_> = error_distribution.clone().into_iter().collect();
251 frequent_errors.sort_by(|a, b| b.1.cmp(&a.1));
252 let top_errors: Vec<_> = frequent_errors.into_iter().take(5).collect();
253
254 let one_hour_ago = Instant::now() - Duration::from_secs(3600);
256 let recent_errors = history
257 .iter()
258 .filter(|err| err.timestamp > one_hour_ago)
259 .count();
260 let recent_error_rate = recent_errors as f64 / 3600.0;
261
262 let active_patterns = Self::scan_active_patterns(&self.patterns, &history, Instant::now());
268
269 ErrorStatistics {
270 total_errors,
271 error_rate,
272 recent_error_rate,
273 uptime,
274 error_distribution,
275 top_errors: top_errors.into_iter().collect(),
276 active_patterns,
277 }
278 }
279
280 fn detect_active_patterns(&self) -> Vec<String> {
282 let history = self.error_history.lock().expect("Operation failed");
283 let now = Instant::now();
284 Self::scan_active_patterns(&self.patterns, &history, now)
285 }
286
287 fn scan_active_patterns(
294 patterns: &[ErrorPattern],
295 history: &VecDeque<ErrorOccurrence>,
296 now: Instant,
297 ) -> Vec<String> {
298 let mut active_patterns = Vec::new();
299
300 for pattern in patterns {
301 let recent_errors: Vec<_> = history
302 .iter()
303 .filter(|err| {
304 pattern.error_codes.contains(&err.code)
305 && now.duration_since(err.timestamp) <= pattern.time_window
306 })
307 .collect();
308
309 if recent_errors.len() >= pattern.frequency_threshold {
310 active_patterns.push(pattern.id.clone());
311 }
312 }
313
314 active_patterns
315 }
316
317 pub fn generate_health_report(&self) -> HealthReport {
319 let stats = self.get_statistics();
320 let history = self.error_history.lock().expect("Operation failed");
321
322 let health_score = self.calculate_health_score(&stats);
324
325 let critical_issues = self.identify_critical_issues(&stats);
327
328 let recommendations = self.generate_recommendations(&stats, &critical_issues);
330
331 let trend = self.calculate_error_trend(&history);
333
334 HealthReport {
335 health_score,
336 critical_issues,
337 recommendations,
338 statistics: stats,
339 trend,
340 timestamp: SystemTime::now(),
341 }
342 }
343
344 fn calculate_health_score(&self, stats: &ErrorStatistics) -> u8 {
346 let mut score = 100.0;
347
348 if stats.error_rate > 1.0 {
350 score -= 30.0;
351 } else if stats.error_rate > 0.1 {
352 score -= 20.0;
353 } else if stats.error_rate > 0.01 {
354 score -= 10.0;
355 }
356
357 score -= stats.active_patterns.len() as f64 * 15.0;
359
360 for (code, count) in &stats.top_errors {
362 if code.severity() <= 2 {
363 score -= *count as f64 * 5.0;
364 }
365 }
366
367 if stats.recent_error_rate > stats.error_rate * 2.0 {
369 score -= 20.0;
370 }
371
372 score.max(0.0).min(100.0) as u8
373 }
374
375 fn identify_critical_issues(&self, stats: &ErrorStatistics) -> Vec<CriticalIssue> {
377 let mut issues = Vec::new();
378
379 if stats
381 .active_patterns
382 .contains(&"memory_pressure".to_string())
383 {
384 issues.push(CriticalIssue {
385 severity: 1,
386 title: "Memory Pressure Detected".to_string(),
387 description: "High memory allocation failures indicate system memory pressure"
388 .to_string(),
389 impact: "May cause application crashes or severe performance degradation"
390 .to_string(),
391 action_required: "Immediate memory optimization or resource scaling required"
392 .to_string(),
393 });
394 }
395
396 for (code, count) in &stats.top_errors {
398 if code.severity() <= 2 && *count > 10 {
399 issues.push(CriticalIssue {
400 severity: code.severity(),
401 title: format!("High {} Error Rate", code),
402 description: format!("Frequent {} errors detected", code.description()),
403 impact: "May indicate fundamental data or algorithm issues".to_string(),
404 action_required: "Investigate root cause and implement fixes".to_string(),
405 });
406 }
407 }
408
409 if stats.recent_error_rate > stats.error_rate * 3.0 {
411 issues.push(CriticalIssue {
412 severity: 2,
413 title: "Error Rate Spike".to_string(),
414 description: "Recent error rate significantly higher than baseline".to_string(),
415 impact: "Indicates potential system instability or new issues".to_string(),
416 action_required: "Monitor closely and investigate recent changes".to_string(),
417 });
418 }
419
420 issues
421 }
422
423 fn generate_recommendations(
425 &self,
426 stats: &ErrorStatistics,
427 issues: &[CriticalIssue],
428 ) -> Vec<Recommendation> {
429 let mut recommendations = Vec::new();
430
431 if stats
433 .active_patterns
434 .contains(&"numerical_instability".to_string())
435 {
436 recommendations.push(Recommendation {
437 priority: 1,
438 category: "Data Quality".to_string(),
439 title: "Improve Numerical Stability".to_string(),
440 description: "Implement data preprocessing and normalization".to_string(),
441 steps: vec![
442 "Check for extreme values in input data".to_string(),
443 "Apply appropriate data scaling or normalization".to_string(),
444 "Consider using more numerically stable algorithms".to_string(),
445 ],
446 expected_impact: "Reduce numerical errors by 70-90%".to_string(),
447 });
448 }
449
450 for (code, count) in &stats.top_errors {
452 match code {
453 ErrorCode::E3005 => {
454 recommendations.push(Recommendation {
455 priority: 2,
456 category: "Data Validation".to_string(),
457 title: "Handle NaN Values".to_string(),
458 description: "Implement comprehensive NaN handling strategy".to_string(),
459 steps: vec![
460 "Add data validation checks before processing".to_string(),
461 "Implement NaN filtering or imputation".to_string(),
462 "Use statistical methods that handle missing data".to_string(),
463 ],
464 expected_impact: "Eliminate NaN-related errors".to_string(),
465 });
466 }
467 ErrorCode::E3003 => {
468 recommendations.push(Recommendation {
469 priority: 2,
470 category: "Algorithm Tuning".to_string(),
471 title: "Optimize Convergence Parameters".to_string(),
472 description: "Adjust algorithm parameters for better convergence"
473 .to_string(),
474 steps: vec![
475 "Increase maximum iterations for iterative algorithms".to_string(),
476 "Adjust convergence tolerance based on data characteristics"
477 .to_string(),
478 "Consider using different initialization strategies".to_string(),
479 ],
480 expected_impact: "Improve convergence rate by 50-80%".to_string(),
481 });
482 }
483 _ => {}
484 }
485 }
486
487 if stats.error_rate > 0.1 {
489 recommendations.push(Recommendation {
490 priority: 1,
491 category: "System Health".to_string(),
492 title: "Reduce Overall Error Rate".to_string(),
493 description: "Implement comprehensive error prevention strategy".to_string(),
494 steps: vec![
495 "Add input validation at system boundaries".to_string(),
496 "Implement data quality checks".to_string(),
497 "Use defensive programming practices".to_string(),
498 ],
499 expected_impact: "Reduce overall error rate significantly".to_string(),
500 });
501 }
502
503 recommendations
504 }
505
506 fn calculate_error_trend(&self, history: &VecDeque<ErrorOccurrence>) -> ErrorTrend {
508 if history.len() < 10 {
509 return ErrorTrend {
510 direction: TrendDirection::Stable,
511 magnitude: 0.0,
512 confidence: 0.0,
513 description: "Insufficient data for trend analysis".to_string(),
514 };
515 }
516
517 let now = Instant::now();
518 let recent_window = Duration::from_secs(1800); let older_window = Duration::from_secs(3600); let recent_errors = history
522 .iter()
523 .filter(|err| now.duration_since(err.timestamp) <= recent_window)
524 .count();
525
526 let older_errors = history
527 .iter()
528 .filter(|err| {
529 let age = now.duration_since(err.timestamp);
530 age > recent_window && age <= older_window
531 })
532 .count();
533
534 let recent_rate = recent_errors as f64 / recent_window.as_secs_f64();
535 let older_rate = older_errors as f64 / recent_window.as_secs_f64(); let change_ratio = if older_rate > 0.0 {
538 recent_rate / older_rate
539 } else if recent_rate > 0.0 {
540 2.0 } else {
542 1.0 };
544
545 let (direction, description) = if change_ratio > 1.5 {
546 (
547 TrendDirection::Increasing,
548 "Error rate is increasing significantly".to_string(),
549 )
550 } else if change_ratio < 0.5 {
551 (
552 TrendDirection::Decreasing,
553 "Error rate is decreasing significantly".to_string(),
554 )
555 } else {
556 (
557 TrendDirection::Stable,
558 "Error rate is relatively stable".to_string(),
559 )
560 };
561
562 let magnitude = (change_ratio - 1.0).abs();
563 let confidence = if history.len() > 50 { 0.8 } else { 0.5 };
564
565 ErrorTrend {
566 direction,
567 magnitude,
568 confidence,
569 description,
570 }
571 }
572}
573
574impl Default for ErrorMonitor {
575 fn default() -> Self {
576 Self::new()
577 }
578}
579
580#[derive(Debug)]
582pub struct ErrorStatistics {
583 pub total_errors: usize,
585 pub error_rate: f64,
587 pub recent_error_rate: f64,
589 pub uptime: Duration,
591 pub error_distribution: HashMap<ErrorCode, usize>,
593 pub top_errors: Vec<(ErrorCode, usize)>,
595 pub active_patterns: Vec<String>,
597}
598
599#[derive(Debug)]
601pub struct CriticalIssue {
602 pub severity: u8,
604 pub title: String,
606 pub description: String,
608 pub impact: String,
610 pub action_required: String,
612}
613
614#[derive(Debug)]
616pub struct Recommendation {
617 pub priority: u8,
619 pub category: String,
621 pub title: String,
623 pub description: String,
625 pub steps: Vec<String>,
627 pub expected_impact: String,
629}
630
631#[derive(Debug)]
633pub struct ErrorTrend {
634 pub direction: TrendDirection,
636 pub magnitude: f64,
638 pub confidence: f64,
640 pub description: String,
642}
643
644#[derive(Debug)]
646pub enum TrendDirection {
647 Increasing,
648 Decreasing,
649 Stable,
650}
651
652#[derive(Debug)]
654pub struct HealthReport {
655 pub health_score: u8,
657 pub critical_issues: Vec<CriticalIssue>,
659 pub recommendations: Vec<Recommendation>,
661 pub statistics: ErrorStatistics,
663 pub trend: ErrorTrend,
665 pub timestamp: SystemTime,
667}
668
669impl HealthReport {
670 pub fn to_formatted_string(&self) -> String {
672 let mut report = String::new();
673
674 report.push_str("=== STATISTICAL COMPUTING HEALTH REPORT ===\n\n");
675 report.push_str(&format!(
676 "š Overall Health Score: {}/100\n",
677 self.health_score
678 ));
679 report.push_str(&format!("ā±ļø Report Generated: {:?}\n\n", self.timestamp));
680
681 let health_indicator = match self.health_score {
683 90..=100 => "š¢ EXCELLENT",
684 70..=89 => "š” GOOD",
685 50..=69 => "š FAIR",
686 30..=49 => "š“ POOR",
687 _ => "šØ CRITICAL",
688 };
689 report.push_str(&format!("Status: {}\n\n", health_indicator));
690
691 if !self.critical_issues.is_empty() {
693 report.push_str("šØ CRITICAL ISSUES:\n");
694 for (i, issue) in self.critical_issues.iter().enumerate() {
695 report.push_str(&format!(
696 "{}. {} (Severity: {})\n {}\n Impact: {}\n Action: {}\n\n",
697 i + 1,
698 issue.title,
699 issue.severity,
700 issue.description,
701 issue.impact,
702 issue.action_required
703 ));
704 }
705 }
706
707 report.push_str("š STATISTICS SUMMARY:\n");
709 report.push_str(&format!(
710 "⢠Total Errors: {}\n",
711 self.statistics.total_errors
712 ));
713 report.push_str(&format!(
714 "⢠Error Rate: {:.4} errors/sec\n",
715 self.statistics.error_rate
716 ));
717 report.push_str(&format!(
718 "⢠Recent Rate: {:.4} errors/sec\n",
719 self.statistics.recent_error_rate
720 ));
721 report.push_str(&format!(
722 "⢠Uptime: {:.2} hours\n",
723 self.statistics.uptime.as_secs_f64() / 3600.0
724 ));
725
726 if !self.statistics.top_errors.is_empty() {
727 report.push_str("\nš TOP ERRORS:\n");
728 for (i, (code, count)) in self.statistics.top_errors.iter().enumerate() {
729 report.push_str(&format!(" {}. {}: {} occurrences\n", i + 1, code, count));
730 }
731 }
732
733 report.push_str(&format!("\nš TREND: {}\n", self.trend.description));
735
736 if !self.recommendations.is_empty() {
738 report.push_str("\nš” RECOMMENDATIONS:\n");
739 for (i, rec) in self.recommendations.iter().enumerate() {
740 report.push_str(&format!(
741 "{}. {} (Priority: {})\n {}\n Expected Impact: {}\n",
742 i + 1,
743 rec.title,
744 rec.priority,
745 rec.description,
746 rec.expected_impact
747 ));
748 if !rec.steps.is_empty() {
749 report.push_str(" Steps:\n");
750 for step in &rec.steps {
751 report.push_str(&format!(" ⢠{}\n", step));
752 }
753 }
754 report.push('\n');
755 }
756 }
757
758 report
759 }
760
761 pub fn requires_immediate_action(&self) -> bool {
763 self.health_score < 50 || self.critical_issues.iter().any(|issue| issue.severity <= 2)
764 }
765}
766
767static GLOBAL_MONITOR: std::sync::OnceLock<ErrorMonitor> = std::sync::OnceLock::new();
769
770#[allow(dead_code)]
772pub fn global_monitor() -> &'static ErrorMonitor {
773 GLOBAL_MONITOR.get_or_init(ErrorMonitor::new)
774}
775
776#[allow(dead_code)]
778pub fn record_global_error(code: ErrorCode, operation: impl Into<String>) {
779 global_monitor().record_error(code, operation);
780}
781
782#[allow(dead_code)]
784pub fn get_global_statistics() -> ErrorStatistics {
785 global_monitor().get_statistics()
786}
787
788#[allow(dead_code)]
790pub fn generate_global_health_report() -> HealthReport {
791 global_monitor().generate_health_report()
792}
793
794#[cfg(test)]
795mod tests {
796 use super::*;
797 use std::thread;
798
799 #[test]
800 fn test_error_monitor_basic() {
801 let monitor = ErrorMonitor::new();
802 monitor.record_error(ErrorCode::E3005, "test_operation");
803
804 let stats = monitor.get_statistics();
805 assert_eq!(stats.total_errors, 1);
806 assert!(stats.error_distribution.contains_key(&ErrorCode::E3005));
807 }
808
809 #[test]
810 fn test_pattern_detection() {
811 let monitor = ErrorMonitor::new();
812
813 for _ in 0..5 {
815 monitor.record_error(ErrorCode::E5001, "memory_test");
816 }
818
819 let stats = monitor.get_statistics();
820 assert!(
824 stats
825 .active_patterns
826 .contains(&"memory_pressure".to_string()),
827 "expected memory_pressure pattern to be active, got {:?}",
828 stats.active_patterns
829 );
830 }
831
832 #[test]
833 fn test_health_score_calculation() {
834 let monitor = ErrorMonitor::new();
835
836 let health_report = monitor.generate_health_report();
838 assert_eq!(health_report.health_score, 100);
839
840 monitor.record_error(ErrorCode::E3001, "overflow_test");
842 monitor.record_error(ErrorCode::E5001, "memory_test");
843
844 let health_report = monitor.generate_health_report();
845 assert!(health_report.health_score < 100);
846 }
847}