Skip to main content

scirs2_stats/
error_diagnostics.rs

1//! Error diagnostics and monitoring system
2//!
3//! This module provides comprehensive error diagnostics, monitoring, and intelligent
4//! recovery strategies for production statistical computing environments.
5
6use 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/// Error pattern detection and analysis
15#[derive(Debug, Clone)]
16pub struct ErrorPattern {
17    /// Pattern identifier
18    pub id: String,
19    /// Error codes that form this pattern
20    pub error_codes: Vec<ErrorCode>,
21    /// Frequency threshold for detection
22    pub frequency_threshold: usize,
23    /// Time window for pattern detection
24    pub time_window: Duration,
25    /// Confidence score (0.0 - 1.0)
26    pub confidence: f64,
27    /// Description of what this pattern indicates
28    pub description: String,
29    /// Suggested mitigation strategy
30    pub mitigation: String,
31}
32
33impl ErrorPattern {
34    /// Create a new error pattern
35    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/// Error occurrence record
56#[derive(Debug, Clone)]
57pub struct ErrorOccurrence {
58    /// Error code
59    pub code: ErrorCode,
60    /// When the error occurred
61    pub timestamp: Instant,
62    /// Operation context
63    pub operation: String,
64    /// Frequency count
65    pub count: usize,
66    /// Resolution status
67    pub resolved: bool,
68    /// Recovery action taken
69    pub recovery_action: Option<String>,
70}
71
72/// Comprehensive error monitoring and analytics
73pub struct ErrorMonitor {
74    /// Recent error occurrences
75    error_history: Arc<Mutex<VecDeque<ErrorOccurrence>>>,
76    /// Error frequency counters
77    error_counts: Arc<Mutex<HashMap<ErrorCode, AtomicUsize>>>,
78    /// Known error patterns
79    patterns: Vec<ErrorPattern>,
80    /// Maximum history size
81    max_historysize: usize,
82    /// Pattern detection enabled
83    pattern_detection_enabled: bool,
84    /// Error rate thresholds
85    error_rate_thresholds: HashMap<ErrorCode, f64>,
86    /// Monitoring start time
87    start_time: Instant,
88}
89
90impl ErrorMonitor {
91    /// Create a new error monitor
92    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    /// Initialize default error patterns
109    fn initialize_default_patterns(&mut self) {
110        // Memory pressure pattern
111        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        // Numerical instability pattern
121        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        // Convergence issues pattern
136        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        // Data quality pattern
146        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    /// Initialize default error rate thresholds
162    fn initialize_default_thresholds(&mut self) {
163        self.error_rate_thresholds.insert(ErrorCode::E5001, 0.01); // Memory errors - very low tolerance
164        self.error_rate_thresholds.insert(ErrorCode::E3001, 0.05); // Overflow - low tolerance
165        self.error_rate_thresholds.insert(ErrorCode::E3005, 0.10); // NaN - moderate tolerance
166        self.error_rate_thresholds.insert(ErrorCode::E4001, 0.20); // Max iterations - higher tolerance
167    }
168
169    /// Record an error occurrence
170    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        // Update history
181        {
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        // Update counters
190        {
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        // Check for patterns if enabled
199        if self.pattern_detection_enabled {
200            self.check_patterns();
201        }
202    }
203
204    /// Check for error patterns in recent history
205    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    /// Get error statistics
228    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        // Calculate error distribution
241        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        // Find most frequent errors
250        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        // Calculate recent error rate (last hour)
255        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        // NOTE: reuse the `history` guard already held above instead of calling
263        // `self.detect_active_patterns()` (which would try to re-lock the same
264        // non-reentrant `std::sync::Mutex` from this same thread and deadlock
265        // permanently -- this previously made every call to `get_statistics`
266        // (and therefore `generate_health_report`) hang forever).
267        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    /// Detect currently active error patterns
281    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    /// Scan the given (already-locked) history for currently active patterns.
288    ///
289    /// Factored out of `detect_active_patterns` so that callers which already
290    /// hold the `error_history` lock (such as `get_statistics`) can reuse it
291    /// directly rather than re-locking the same non-reentrant `Mutex`, which
292    /// would deadlock.
293    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    /// Generate comprehensive health report
318    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        // Calculate health score (0-100)
323        let health_score = self.calculate_health_score(&stats);
324
325        // Identify critical issues
326        let critical_issues = self.identify_critical_issues(&stats);
327
328        // Generate recommendations
329        let recommendations = self.generate_recommendations(&stats, &critical_issues);
330
331        // Calculate trend information
332        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    /// Calculate overall system health score
345    fn calculate_health_score(&self, stats: &ErrorStatistics) -> u8 {
346        let mut score = 100.0;
347
348        // Penalty for high error rates
349        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        // Penalty for active patterns
358        score -= stats.active_patterns.len() as f64 * 15.0;
359
360        // Penalty for critical errors
361        for (code, count) in &stats.top_errors {
362            if code.severity() <= 2 {
363                score -= *count as f64 * 5.0;
364            }
365        }
366
367        // Penalty for recent error spike
368        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    /// Identify critical issues requiring immediate attention
376    fn identify_critical_issues(&self, stats: &ErrorStatistics) -> Vec<CriticalIssue> {
377        let mut issues = Vec::new();
378
379        // Check for severe error patterns
380        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        // Check for high critical error rates
397        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        // Check for error rate spikes
410        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    /// Generate actionable recommendations
424    fn generate_recommendations(
425        &self,
426        stats: &ErrorStatistics,
427        issues: &[CriticalIssue],
428    ) -> Vec<Recommendation> {
429        let mut recommendations = Vec::new();
430
431        // Recommendations based on error patterns
432        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        // Recommendations based on frequent errors
451        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        // General recommendations based on health score
488        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    /// Calculate error trend over time
507    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); // 30 minutes
519        let older_window = Duration::from_secs(3600); // 1 hour
520
521        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(); // Same window size for comparison
536
537        let change_ratio = if older_rate > 0.0 {
538            recent_rate / older_rate
539        } else if recent_rate > 0.0 {
540            2.0 // Arbitrary large value indicating increase from zero
541        } else {
542            1.0 // No change
543        };
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/// Error statistics summary
581#[derive(Debug)]
582pub struct ErrorStatistics {
583    /// Total number of errors
584    pub total_errors: usize,
585    /// Overall error rate (errors per second)
586    pub error_rate: f64,
587    /// Recent error rate (last hour)
588    pub recent_error_rate: f64,
589    /// System uptime
590    pub uptime: Duration,
591    /// Error distribution by type
592    pub error_distribution: HashMap<ErrorCode, usize>,
593    /// Top 5 most frequent errors
594    pub top_errors: Vec<(ErrorCode, usize)>,
595    /// Currently active error patterns
596    pub active_patterns: Vec<String>,
597}
598
599/// Critical issue requiring immediate attention
600#[derive(Debug)]
601pub struct CriticalIssue {
602    /// Severity level (1 = most critical)
603    pub severity: u8,
604    /// Issue title
605    pub title: String,
606    /// Detailed description
607    pub description: String,
608    /// Potential impact
609    pub impact: String,
610    /// Required action
611    pub action_required: String,
612}
613
614/// Actionable recommendation
615#[derive(Debug)]
616pub struct Recommendation {
617    /// Priority level (1 = highest)
618    pub priority: u8,
619    /// Category of recommendation
620    pub category: String,
621    /// Recommendation title
622    pub title: String,
623    /// Description
624    pub description: String,
625    /// Step-by-step actions
626    pub steps: Vec<String>,
627    /// Expected impact
628    pub expected_impact: String,
629}
630
631/// Error trend analysis
632#[derive(Debug)]
633pub struct ErrorTrend {
634    /// Trend direction
635    pub direction: TrendDirection,
636    /// Magnitude of change
637    pub magnitude: f64,
638    /// Confidence in the trend (0.0-1.0)
639    pub confidence: f64,
640    /// Trend description
641    pub description: String,
642}
643
644/// Trend direction enumeration
645#[derive(Debug)]
646pub enum TrendDirection {
647    Increasing,
648    Decreasing,
649    Stable,
650}
651
652/// Comprehensive health report
653#[derive(Debug)]
654pub struct HealthReport {
655    /// Overall health score (0-100)
656    pub health_score: u8,
657    /// Critical issues requiring attention
658    pub critical_issues: Vec<CriticalIssue>,
659    /// Actionable recommendations
660    pub recommendations: Vec<Recommendation>,
661    /// Detailed statistics
662    pub statistics: ErrorStatistics,
663    /// Error trend analysis
664    pub trend: ErrorTrend,
665    /// Report generation timestamp
666    pub timestamp: SystemTime,
667}
668
669impl HealthReport {
670    /// Generate a formatted text report
671    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        // Health indicator
682        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        // Critical Issues
692        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        // Statistics Summary
708        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        // Trend Analysis
734        report.push_str(&format!("\nšŸ“Š TREND: {}\n", self.trend.description));
735
736        // Recommendations
737        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    /// Check if immediate action is required
762    pub fn requires_immediate_action(&self) -> bool {
763        self.health_score < 50 || self.critical_issues.iter().any(|issue| issue.severity <= 2)
764    }
765}
766
767/// Global error monitor instance
768static GLOBAL_MONITOR: std::sync::OnceLock<ErrorMonitor> = std::sync::OnceLock::new();
769
770/// Get the global error monitor instance
771#[allow(dead_code)]
772pub fn global_monitor() -> &'static ErrorMonitor {
773    GLOBAL_MONITOR.get_or_init(ErrorMonitor::new)
774}
775
776/// Convenience function to record an error globally
777#[allow(dead_code)]
778pub fn record_global_error(code: ErrorCode, operation: impl Into<String>) {
779    global_monitor().record_error(code, operation);
780}
781
782/// Convenience function to get global error statistics
783#[allow(dead_code)]
784pub fn get_global_statistics() -> ErrorStatistics {
785    global_monitor().get_statistics()
786}
787
788/// Convenience function to generate global health report
789#[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        // Record multiple memory errors to trigger pattern
814        for _ in 0..5 {
815            monitor.record_error(ErrorCode::E5001, "memory_test");
816            // Remove sleep - not needed for testing functionality
817        }
818
819        let stats = monitor.get_statistics();
820        // Pattern detection should identify memory pressure: 5 >= the
821        // "memory_pressure" pattern's frequency_threshold of 3 within its 60s
822        // window (see initialize_default_patterns).
823        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        // Fresh monitor should have perfect health
837        let health_report = monitor.generate_health_report();
838        assert_eq!(health_report.health_score, 100);
839
840        // Record some errors and check health degrades
841        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}