memscope_rs/error/
handler.rs

1use super::{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.clone(), 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.clone(), *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                    let duration = newest.duration_since(oldest).as_secs_f64();
123                    if duration > 0.0 {
124                        recent.len() as f64 / duration
125                    } else {
126                        0.0
127                    }
128                } else {
129                    0.0
130                }
131            }
132        } else {
133            0.0
134        };
135
136        ErrorFrequencyAnalysis {
137            total_errors,
138            most_common_error: most_common,
139            errors_per_second: error_rate,
140            error_distribution: counts,
141        }
142    }
143
144    fn update_statistics(&mut self, error: &MemScopeError) {
145        let counter = self
146            .error_counts
147            .entry(error.kind.clone())
148            .or_insert_with(|| AtomicUsize::new(0));
149        counter.fetch_add(1, Ordering::Relaxed);
150    }
151
152    fn store_recent_error(&self, error: MemScopeError) {
153        if let Ok(mut recent) = self.recent_errors.lock() {
154            recent.push(error);
155
156            // Keep only the most recent errors
157            if recent.len() > self.max_recent_errors {
158                let excess = recent.len() - self.max_recent_errors;
159                recent.drain(0..excess);
160            }
161        }
162    }
163
164    fn determine_response(&self, error: &MemScopeError) -> ErrorResponse {
165        match error.severity {
166            ErrorSeverity::Warning => ErrorResponse::Continue,
167            ErrorSeverity::Error => {
168                if self.is_frequent_error(&error.kind) {
169                    ErrorResponse::Throttle
170                } else {
171                    ErrorResponse::Retry
172                }
173            }
174            ErrorSeverity::Critical => ErrorResponse::Fallback,
175            ErrorSeverity::Fatal => ErrorResponse::Abort,
176        }
177    }
178
179    fn is_frequent_error(&self, kind: &ErrorKind) -> bool {
180        self.error_counts
181            .get(kind)
182            .map(|count| count.load(Ordering::Relaxed) > 10)
183            .unwrap_or(false)
184    }
185
186    fn log_response(&self, error: &MemScopeError, response: &ErrorResponse) {
187        if self.reporter.log_to_stderr {
188            eprintln!(
189                "ErrorHandler: {} -> Response: {:?}",
190                error.description(),
191                response
192            );
193        }
194    }
195}
196
197impl ErrorReporter {
198    /// Create new reporter with default settings
199    pub fn new() -> Self {
200        Self {
201            log_to_stderr: true,
202            store_errors: true,
203            min_severity: ErrorSeverity::Warning,
204            custom_handlers: HashMap::new(),
205        }
206    }
207
208    /// Configure error logging
209    pub fn with_logging(mut self, enabled: bool) -> Self {
210        self.log_to_stderr = enabled;
211        self
212    }
213
214    /// Configure error storage
215    pub fn with_storage(mut self, enabled: bool) -> Self {
216        self.store_errors = enabled;
217        self
218    }
219
220    /// Set minimum severity level for reporting
221    pub fn with_min_severity(mut self, severity: ErrorSeverity) -> Self {
222        self.min_severity = severity;
223        self
224    }
225
226    /// Add custom handler for specific severity level
227    pub fn with_custom_handler<F>(mut self, severity: ErrorSeverity, handler: F) -> Self
228    where
229        F: Fn(&MemScopeError) + Send + Sync + 'static,
230    {
231        self.custom_handlers.insert(severity, Box::new(handler));
232        self
233    }
234
235    /// Report error if it meets severity threshold
236    pub fn report_error(&self, error: &MemScopeError) -> bool {
237        if error.severity < self.min_severity {
238            return false;
239        }
240
241        // Call custom handler if registered
242        if let Some(handler) = self.custom_handlers.get(&error.severity) {
243            handler(error);
244        }
245
246        // Log to stderr if configured
247        if self.log_to_stderr {
248            eprintln!("MemScope Error: {}", error);
249        }
250
251        true
252    }
253}
254
255/// Response strategy for handling errors
256#[derive(Debug, Clone, PartialEq)]
257pub enum ErrorResponse {
258    /// Continue operation, error is non-critical
259    Continue,
260    /// Retry operation with backoff
261    Retry,
262    /// Throttle operations to reduce error rate
263    Throttle,
264    /// Fall back to alternative approach
265    Fallback,
266    /// Abort current operation
267    Abort,
268}
269
270impl ErrorResponse {
271    /// Check if response should be logged
272    pub fn should_log(&self) -> bool {
273        !matches!(self, ErrorResponse::Continue)
274    }
275
276    /// Check if operation should be retried
277    pub fn should_retry(&self) -> bool {
278        matches!(self, ErrorResponse::Retry)
279    }
280
281    /// Check if operation should be aborted
282    pub fn should_abort(&self) -> bool {
283        matches!(self, ErrorResponse::Abort)
284    }
285}
286
287/// Analysis of error frequency patterns
288#[derive(Debug, Clone)]
289pub struct ErrorFrequencyAnalysis {
290    /// Total number of errors recorded
291    pub total_errors: usize,
292    /// Most frequently occurring error type
293    pub most_common_error: Option<(ErrorKind, usize)>,
294    /// Average errors per second
295    pub errors_per_second: f64,
296    /// Distribution of errors by kind
297    pub error_distribution: HashMap<ErrorKind, usize>,
298}
299
300impl ErrorFrequencyAnalysis {
301    /// Check if error rate is concerning
302    pub fn is_error_rate_high(&self) -> bool {
303        self.errors_per_second > 1.0 || self.total_errors > 100
304    }
305
306    /// Get the most problematic error type
307    pub fn get_primary_concern(&self) -> Option<ErrorKind> {
308        self.most_common_error
309            .as_ref()
310            .map(|(kind, _)| kind.clone())
311    }
312
313    /// Generate summary report
314    pub fn summary(&self) -> String {
315        format!(
316            "Error Analysis: {} total errors, {:.2} errors/sec, primary concern: {:?}",
317            self.total_errors,
318            self.errors_per_second,
319            self.get_primary_concern()
320        )
321    }
322}
323
324impl Default for ErrorHandler {
325    fn default() -> Self {
326        Self::new()
327    }
328}
329
330impl Default for ErrorReporter {
331    fn default() -> Self {
332        Self::new()
333    }
334}
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    #[test]
341    fn test_error_handler_basic() {
342        let mut handler = ErrorHandler::new();
343        let error = MemScopeError::new(ErrorKind::MemoryError, "test error");
344
345        let response = handler.handle_error(error);
346        assert_eq!(response, ErrorResponse::Retry);
347
348        let counts = handler.get_error_counts();
349        assert_eq!(counts.get(&ErrorKind::MemoryError), Some(&1));
350    }
351
352    #[test]
353    fn test_error_frequency_analysis() {
354        let mut handler = ErrorHandler::new();
355
356        // Generate multiple errors
357        for _ in 0..5 {
358            let error = MemScopeError::new(ErrorKind::CacheError, "cache miss");
359            handler.handle_error(error);
360        }
361
362        for _ in 0..3 {
363            let error = MemScopeError::new(ErrorKind::MemoryError, "allocation failed");
364            handler.handle_error(error);
365        }
366
367        let analysis = handler.get_error_frequency();
368        assert_eq!(analysis.total_errors, 8);
369        assert_eq!(analysis.get_primary_concern(), Some(ErrorKind::CacheError));
370    }
371
372    #[test]
373    fn test_error_reporter_configuration() {
374        let reporter = ErrorReporter::new()
375            .with_logging(false)
376            .with_storage(true)
377            .with_min_severity(ErrorSeverity::Error);
378
379        // Warning should not be reported
380        let warning = MemScopeError::with_context(
381            ErrorKind::ValidationError,
382            ErrorSeverity::Warning,
383            "test_warning",
384            "test",
385        );
386        assert!(!reporter.report_error(&warning));
387
388        // Error should be reported
389        let error = MemScopeError::with_context(
390            ErrorKind::ValidationError,
391            ErrorSeverity::Error,
392            "test_error",
393            "test",
394        );
395        assert!(reporter.report_error(&error));
396    }
397
398    #[test]
399    fn test_error_response_strategies() {
400        let handler = ErrorHandler::new();
401
402        let warning = MemScopeError::with_context(
403            ErrorKind::ValidationError,
404            ErrorSeverity::Warning,
405            "test",
406            "test",
407        );
408        let response = handler.determine_response(&warning);
409        assert_eq!(response, ErrorResponse::Continue);
410        assert!(!response.should_abort());
411
412        let fatal = MemScopeError::with_context(
413            ErrorKind::InternalError,
414            ErrorSeverity::Fatal,
415            "test",
416            "test",
417        );
418        let response = handler.determine_response(&fatal);
419        assert_eq!(response, ErrorResponse::Abort);
420        assert!(response.should_abort());
421    }
422}