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