Skip to main content

memscope_rs/error/
handler.rs

1use crate::core::error::{ErrorKind, ErrorSeverity, MemScopeError};
2use std::collections::HashMap;
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::sync::{Arc, Mutex};
5
6/// Type alias for error handler function
7type ErrorHandlerFn = Box<dyn Fn(&MemScopeError) + Send + Sync>;
8
9/// Central error handling and reporting system
10pub struct ErrorHandler {
11    /// Error statistics by kind
12    error_counts: HashMap<ErrorKind, AtomicUsize>,
13    /// Recent errors for analysis
14    recent_errors: Arc<Mutex<Vec<MemScopeError>>>,
15    /// Maximum number of recent errors to keep
16    max_recent_errors: usize,
17    /// Error reporting configuration
18    reporter: ErrorReporter,
19}
20
21/// Configurable error reporting system
22pub struct ErrorReporter {
23    /// Whether to log errors to stderr
24    log_to_stderr: bool,
25    /// Whether to store errors for later analysis
26    store_errors: bool,
27    /// Minimum severity level to report
28    min_severity: ErrorSeverity,
29    /// Custom error handlers by severity
30    custom_handlers: HashMap<ErrorSeverity, ErrorHandlerFn>,
31}
32
33impl ErrorHandler {
34    /// Create new error handler with default configuration
35    pub fn new() -> Self {
36        Self {
37            error_counts: HashMap::new(),
38            recent_errors: Arc::new(Mutex::new(Vec::new())),
39            max_recent_errors: 100,
40            reporter: ErrorReporter::new(),
41        }
42    }
43
44    /// Create error handler with custom configuration
45    pub fn with_config(max_recent_errors: usize, reporter: ErrorReporter) -> Self {
46        Self {
47            error_counts: HashMap::new(),
48            recent_errors: Arc::new(Mutex::new(Vec::new())),
49            max_recent_errors,
50            reporter,
51        }
52    }
53
54    /// Handle an error with appropriate response
55    pub fn handle_error(&mut self, error: MemScopeError) -> ErrorResponse {
56        // Update statistics
57        self.update_statistics(&error);
58
59        // Store recent error if configured
60        if self.reporter.store_errors {
61            self.store_recent_error(error.clone());
62        }
63
64        // Report error based on configuration
65        let reported = self.reporter.report_error(&error);
66
67        // Determine response based on error severity
68        let response = self.determine_response(&error);
69
70        if reported && response.should_log() {
71            self.log_response(&error, &response);
72        }
73
74        response
75    }
76
77    /// Get error statistics by kind
78    pub fn get_error_counts(&self) -> HashMap<ErrorKind, usize> {
79        self.error_counts
80            .iter()
81            .map(|(kind, count)| (*kind, count.load(Ordering::Relaxed)))
82            .collect()
83    }
84
85    /// Get recent errors for analysis
86    pub fn get_recent_errors(&self) -> Vec<MemScopeError> {
87        self.recent_errors
88            .lock()
89            .map(|errors| errors.clone())
90            .unwrap_or_default()
91    }
92
93    /// Clear error statistics and history
94    pub fn clear_statistics(&mut self) {
95        for count in self.error_counts.values() {
96            count.store(0, Ordering::Relaxed);
97        }
98
99        if let Ok(mut recent) = self.recent_errors.lock() {
100            recent.clear();
101        }
102    }
103
104    /// Get error frequency analysis
105    pub fn get_error_frequency(&self) -> ErrorFrequencyAnalysis {
106        let counts = self.get_error_counts();
107        let total_errors: usize = counts.values().sum();
108
109        let most_common = counts
110            .iter()
111            .max_by_key(|(_, count)| *count)
112            .map(|(kind, count)| (*kind, *count));
113
114        let error_rate = if let Ok(recent) = self.recent_errors.lock() {
115            if recent.is_empty() {
116                0.0
117            } else {
118                let oldest_time = recent.first().map(|e| e.context().timestamp);
119                let newest_time = recent.last().map(|e| e.context().timestamp);
120
121                if let (Some(oldest), Some(newest)) = (oldest_time, newest_time) {
122                    match newest.duration_since(oldest) {
123                        Ok(duration) => {
124                            let duration_secs = duration.as_secs_f64();
125                            if duration_secs > 0.0 {
126                                recent.len() as f64 / duration_secs
127                            } else {
128                                0.0
129                            }
130                        }
131                        Err(_) => 0.0,
132                    }
133                } else {
134                    0.0
135                }
136            }
137        } else {
138            0.0
139        };
140
141        ErrorFrequencyAnalysis {
142            total_errors,
143            most_common_error: most_common,
144            errors_per_second: error_rate,
145            error_distribution: counts,
146        }
147    }
148
149    fn update_statistics(&mut self, error: &MemScopeError) {
150        let counter = self
151            .error_counts
152            .entry(error.kind())
153            .or_insert_with(|| AtomicUsize::new(0));
154        counter.fetch_add(1, Ordering::Relaxed);
155    }
156
157    fn store_recent_error(&self, error: MemScopeError) {
158        if let Ok(mut recent) = self.recent_errors.lock() {
159            recent.push(error);
160
161            // Keep only the most recent errors
162            if recent.len() > self.max_recent_errors {
163                let excess = recent.len() - self.max_recent_errors;
164                recent.drain(0..excess);
165            }
166        }
167    }
168
169    fn determine_response(&self, error: &MemScopeError) -> ErrorResponse {
170        match error.severity() {
171            ErrorSeverity::Warning => ErrorResponse::Continue,
172            ErrorSeverity::Error => {
173                if self.is_frequent_error(&error.kind()) {
174                    ErrorResponse::Throttle
175                } else {
176                    ErrorResponse::Retry
177                }
178            }
179            ErrorSeverity::Critical => ErrorResponse::Fallback,
180            ErrorSeverity::Fatal => ErrorResponse::Abort,
181        }
182    }
183
184    fn is_frequent_error(&self, kind: &ErrorKind) -> bool {
185        self.error_counts
186            .get(kind)
187            .map(|count| count.load(Ordering::Relaxed) > 10)
188            .unwrap_or(false)
189    }
190
191    fn log_response(&self, error: &MemScopeError, response: &ErrorResponse) {
192        if self.reporter.log_to_stderr {
193            eprintln!(
194                "ErrorHandler: {} -> Response: {:?}",
195                error.description(),
196                response
197            );
198        }
199    }
200}
201
202impl ErrorReporter {
203    /// Create new reporter with default settings
204    pub fn new() -> Self {
205        Self {
206            log_to_stderr: true,
207            store_errors: true,
208            min_severity: ErrorSeverity::Warning,
209            custom_handlers: HashMap::new(),
210        }
211    }
212
213    /// Configure error logging
214    pub fn with_logging(mut self, enabled: bool) -> Self {
215        self.log_to_stderr = enabled;
216        self
217    }
218
219    /// Configure error storage
220    pub fn with_storage(mut self, enabled: bool) -> Self {
221        self.store_errors = enabled;
222        self
223    }
224
225    /// Set minimum severity level for reporting
226    pub fn with_min_severity(mut self, severity: ErrorSeverity) -> Self {
227        self.min_severity = severity;
228        self
229    }
230
231    /// Add custom handler for specific severity level
232    pub fn with_custom_handler<F>(mut self, severity: ErrorSeverity, handler: F) -> Self
233    where
234        F: Fn(&MemScopeError) + Send + Sync + 'static,
235    {
236        self.custom_handlers.insert(severity, Box::new(handler));
237        self
238    }
239
240    /// Report error if it meets severity threshold
241    pub fn report_error(&self, error: &MemScopeError) -> bool {
242        if error.severity() < self.min_severity {
243            return false;
244        }
245
246        // Call custom handler if registered
247        if let Some(handler) = self.custom_handlers.get(&error.severity()) {
248            handler(error);
249        }
250
251        // Log to stderr if configured
252        if self.log_to_stderr {
253            eprintln!("MemScope Error: {}", error);
254        }
255
256        true
257    }
258}
259
260/// Response strategy for handling errors
261#[derive(Debug, Clone, PartialEq)]
262pub enum ErrorResponse {
263    /// Continue operation, error is non-critical
264    Continue,
265    /// Retry operation with backoff
266    Retry,
267    /// Throttle operations to reduce error rate
268    Throttle,
269    /// Fall back to alternative approach
270    Fallback,
271    /// Abort current operation
272    Abort,
273}
274
275impl ErrorResponse {
276    /// Check if response should be logged
277    pub fn should_log(&self) -> bool {
278        !matches!(self, ErrorResponse::Continue)
279    }
280
281    /// Check if operation should be retried
282    pub fn should_retry(&self) -> bool {
283        matches!(self, ErrorResponse::Retry)
284    }
285
286    /// Check if operation should be aborted
287    pub fn should_abort(&self) -> bool {
288        matches!(self, ErrorResponse::Abort)
289    }
290}
291
292/// Analysis of error frequency patterns
293#[derive(Debug, Clone)]
294pub struct ErrorFrequencyAnalysis {
295    /// Total number of errors recorded
296    pub total_errors: usize,
297    /// Most frequently occurring error type
298    pub most_common_error: Option<(ErrorKind, usize)>,
299    /// Average errors per second
300    pub errors_per_second: f64,
301    /// Distribution of errors by kind
302    pub error_distribution: HashMap<ErrorKind, usize>,
303}
304
305impl ErrorFrequencyAnalysis {
306    /// Check if error rate is concerning
307    pub fn is_error_rate_high(&self) -> bool {
308        self.errors_per_second > 1.0 || self.total_errors > 100
309    }
310
311    /// Get the most problematic error type
312    pub fn get_primary_concern(&self) -> Option<ErrorKind> {
313        self.most_common_error.as_ref().map(|(kind, _)| *kind)
314    }
315
316    /// Generate summary report
317    pub fn summary(&self) -> String {
318        format!(
319            "Error Analysis: {} total errors, {:.2} errors/sec, primary concern: {:?}",
320            self.total_errors,
321            self.errors_per_second,
322            self.get_primary_concern()
323        )
324    }
325}
326
327impl Default for ErrorHandler {
328    fn default() -> Self {
329        Self::new()
330    }
331}
332
333impl Default for ErrorReporter {
334    fn default() -> Self {
335        Self::new()
336    }
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    #[test]
344    fn test_error_handler_basic() {
345        let mut handler = ErrorHandler::new();
346        let error = MemScopeError::new(ErrorKind::MemoryError, "test error");
347
348        let response = handler.handle_error(error);
349        assert_eq!(response, ErrorResponse::Retry);
350
351        let counts = handler.get_error_counts();
352        assert_eq!(counts.get(&ErrorKind::MemoryError), Some(&1));
353    }
354
355    #[test]
356    fn test_error_frequency_analysis() {
357        let mut handler = ErrorHandler::new();
358
359        // Generate multiple errors
360        for _ in 0..5 {
361            let error = MemScopeError::new(ErrorKind::CacheError, "cache miss");
362            handler.handle_error(error);
363        }
364
365        for _ in 0..3 {
366            let error = MemScopeError::new(ErrorKind::MemoryError, "allocation failed");
367            handler.handle_error(error);
368        }
369
370        let analysis = handler.get_error_frequency();
371        assert_eq!(analysis.total_errors, 8);
372        assert_eq!(analysis.get_primary_concern(), Some(ErrorKind::CacheError));
373    }
374
375    #[test]
376    fn test_error_reporter_configuration() {
377        let reporter = ErrorReporter::new()
378            .with_logging(false)
379            .with_storage(true)
380            .with_min_severity(ErrorSeverity::Error);
381
382        // ValidationError creates Memory variant with Error severity
383        // Since min_severity is Error, it should be reported
384        let validation_err = MemScopeError::with_context(
385            ErrorKind::ValidationError,
386            ErrorSeverity::Warning,
387            "test_warning",
388            "test",
389        );
390        // ValidationError -> Memory variant -> Error severity (>= min_severity Error)
391        assert!(reporter.report_error(&validation_err));
392
393        // Error should be reported
394        let error = MemScopeError::with_context(
395            ErrorKind::MemoryError,
396            ErrorSeverity::Error,
397            "test_error",
398            "test",
399        );
400        assert!(reporter.report_error(&error));
401    }
402
403    #[test]
404    fn test_error_response_strategies() {
405        let handler = ErrorHandler::new();
406
407        // ValidationError creates Memory variant with Error severity
408        let validation_err = MemScopeError::with_context(
409            ErrorKind::ValidationError,
410            ErrorSeverity::Warning,
411            "test",
412            "test",
413        );
414        let response = handler.determine_response(&validation_err);
415        // Memory variant -> Error severity -> Retry response
416        assert_eq!(response, ErrorResponse::Retry);
417        assert!(!response.should_abort());
418
419        let fatal = MemScopeError::with_context(
420            ErrorKind::InternalError,
421            ErrorSeverity::Fatal,
422            "test",
423            "test",
424        );
425        let response = handler.determine_response(&fatal);
426        // Internal variant -> Fatal severity -> Abort response
427        assert_eq!(response, ErrorResponse::Abort);
428        assert!(response.should_abort());
429    }
430}