1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use super::types::MemScopeError;
12use crate::core::error::{ErrorContext, ErrorSeverity};
13
14#[derive(Debug, Clone, Serialize, Deserialize, Default)]
16pub struct ErrorStats {
17 pub total_errors: u64,
18 pub by_severity: HashMap<String, u64>,
19 pub by_module: HashMap<String, u64>,
20 pub by_kind: HashMap<String, u64>,
21 pub recent_errors: Vec<ErrorRecord>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ErrorRecord {
27 pub timestamp: u64,
28 pub module: String,
29 pub error_type: String,
30 pub severity: ErrorSeverity,
31 pub message: String,
32 pub context: Option<ErrorContext>,
33}
34
35pub struct ErrorManager {
37 stats: Arc<Mutex<ErrorStats>>,
38 max_recent_errors: usize,
39 enabled: Arc<AtomicBool>,
40}
41
42use std::sync::atomic::AtomicBool;
43
44impl Default for ErrorManager {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl ErrorManager {
51 pub fn new() -> Self {
52 Self {
53 stats: Arc::new(Mutex::new(ErrorStats::default())),
54 max_recent_errors: 100,
55 enabled: Arc::new(AtomicBool::new(true)),
56 }
57 }
58
59 pub fn with_config(max_recent_errors: usize) -> Self {
60 Self {
61 stats: Arc::new(Mutex::new(ErrorStats::default())),
62 max_recent_errors,
63 enabled: Arc::new(AtomicBool::new(true)),
64 }
65 }
66
67 pub fn enable(&self) {
69 self.enabled
70 .store(true, std::sync::atomic::Ordering::SeqCst);
71 }
72
73 pub fn disable(&self) {
75 self.enabled
76 .store(false, std::sync::atomic::Ordering::SeqCst);
77 }
78
79 pub fn is_enabled(&self) -> bool {
81 self.enabled.load(std::sync::atomic::Ordering::SeqCst)
82 }
83
84 pub fn record_error(&self, module: &str, error_type: &str, error: &MemScopeError) {
86 if !self.is_enabled() {
87 return;
88 }
89
90 let timestamp = SystemTime::now()
91 .duration_since(UNIX_EPOCH)
92 .unwrap_or_default()
93 .as_millis() as u64;
94
95 let severity = error.severity();
96 let severity_str = format!("{:?}", severity);
97
98 let record = ErrorRecord {
99 timestamp,
100 module: module.to_string(),
101 error_type: error_type.to_string(),
102 severity,
103 message: error.user_message().to_string(),
104 context: None,
105 };
106
107 if let Ok(mut stats) = self.stats.lock() {
108 stats.total_errors += 1;
109 *stats.by_severity.entry(severity_str).or_insert(0) += 1;
110 *stats.by_module.entry(module.to_string()).or_insert(0) += 1;
111 *stats.by_kind.entry(error_type.to_string()).or_insert(0) += 1;
112
113 stats.recent_errors.push(record);
115 if stats.recent_errors.len() > self.max_recent_errors {
116 stats.recent_errors.drain(0..1);
117 }
118 }
119 }
120
121 pub fn get_stats(&self) -> ErrorStats {
123 if let Ok(stats) = self.stats.lock() {
124 stats.clone()
125 } else {
126 ErrorStats::default()
127 }
128 }
129
130 pub fn clear_stats(&self) {
132 if let Ok(mut stats) = self.stats.lock() {
133 *stats = ErrorStats::default();
134 }
135 }
136
137 pub fn generate_report(&self) -> ErrorReport {
139 let stats = self.get_stats();
140
141 let summary = if stats.total_errors == 0 {
142 "No errors recorded".to_string()
143 } else {
144 format!(
145 "Total: {} errors, Severity: {:?} most common",
146 stats.total_errors,
147 stats
148 .by_severity
149 .iter()
150 .max_by_key(|(_, &a)| a)
151 .map(|(k, _)| k)
152 )
153 };
154
155 ErrorReport {
156 summary,
157 stats,
158 generated_at: SystemTime::now()
159 .duration_since(UNIX_EPOCH)
160 .unwrap_or_default()
161 .as_millis() as u64,
162 }
163 }
164
165 pub fn convert_to_memscope<T: IntoMemScopeError>(error: T, module: &str) -> MemScopeError {
167 error.into_memscope_error(module)
168 }
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ErrorReport {
174 pub summary: String,
175 pub stats: ErrorStats,
176 pub generated_at: u64,
177}
178
179pub trait IntoMemScopeError: Sized {
181 fn into_memscope_error(self, module: &str) -> MemScopeError;
182}
183
184static GLOBAL_ERROR_MANAGER: std::sync::OnceLock<ErrorManager> = std::sync::OnceLock::new();
186
187pub fn global_error_manager() -> &'static ErrorManager {
189 GLOBAL_ERROR_MANAGER.get_or_init(ErrorManager::new)
190}
191
192pub fn record_error(module: &str, error_type: &str, error: &MemScopeError) {
194 global_error_manager().record_error(module, error_type, error);
195}
196
197pub fn get_error_stats() -> ErrorStats {
199 global_error_manager().get_stats()
200}
201
202pub fn generate_error_report() -> ErrorReport {
204 global_error_manager().generate_report()
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_error_manager_creation() {
213 let manager = ErrorManager::new();
214 assert!(manager.is_enabled());
215 }
216
217 #[test]
218 fn test_error_recording() {
219 let manager = ErrorManager::with_config(10);
220 let error = MemScopeError::memory(
221 crate::core::error::MemoryOperation::Allocation,
222 "test error",
223 );
224
225 manager.record_error("test_module", "test_error_type", &error);
226
227 let stats = manager.get_stats();
228 assert_eq!(stats.total_errors, 1);
229 assert_eq!(stats.by_module.get("test_module"), Some(&1));
230 assert_eq!(stats.by_kind.get("test_error_type"), Some(&1));
231 }
232
233 #[test]
234 fn test_error_stats_limits() {
235 let manager = ErrorManager::with_config(3);
236 let error = MemScopeError::memory(crate::core::error::MemoryOperation::Allocation, "test");
237
238 for i in 0..10 {
240 manager.record_error("test", &format!("error_{}", i), &error);
241 }
242
243 let stats = manager.get_stats();
244 assert_eq!(stats.total_errors, 10);
245 assert_eq!(stats.recent_errors.len(), 3); }
247
248 #[test]
249 fn test_error_enable_disable() {
250 let manager = ErrorManager::new();
251 assert!(manager.is_enabled());
252
253 manager.disable();
254 assert!(!manager.is_enabled());
255
256 manager.enable();
257 assert!(manager.is_enabled());
258 }
259
260 #[test]
261 fn test_error_clear_stats() {
262 let manager = ErrorManager::new();
263 let error = MemScopeError::memory(crate::core::error::MemoryOperation::Allocation, "test");
264
265 manager.record_error("test", "test", &error);
266 assert_eq!(manager.get_stats().total_errors, 1);
267
268 manager.clear_stats();
269 assert_eq!(manager.get_stats().total_errors, 0);
270 }
271
272 #[test]
273 fn test_error_report_generation() {
274 let manager = ErrorManager::new();
275 let report = manager.generate_report();
276
277 assert_eq!(report.summary, "No errors recorded");
278 assert_eq!(report.stats.total_errors, 0);
279 }
280
281 #[test]
282 fn test_global_error_manager() {
283 let manager = global_error_manager();
284 assert!(manager.is_enabled());
285
286 let error = MemScopeError::internal("test");
287 record_error("global_test", "test_type", &error);
288
289 let stats = get_error_stats();
290 assert!(stats.total_errors >= 1);
291 }
292
293 #[test]
294 fn test_error_manager_thread_safety() {
295 use std::thread;
296
297 let manager = Arc::new(ErrorManager::new());
298 let error = MemScopeError::memory(crate::core::error::MemoryOperation::Allocation, "test");
299
300 let mut handles = vec![];
301 for i in 0..10 {
302 let manager_clone = Arc::clone(&manager);
303 let error_clone = error.clone();
304 let handle = thread::spawn(move || {
305 for j in 0..10 {
306 manager_clone.record_error(
307 "thread_test",
308 &format!("error_{}_{}", i, j),
309 &error_clone,
310 );
311 }
312 });
313 handles.push(handle);
314 }
315
316 for handle in handles {
317 handle.join().unwrap();
318 }
319
320 let stats = manager.get_stats();
321 assert_eq!(stats.total_errors, 100);
322 }
323}