1use chrono::Local;
2use std::fmt::Debug;
3use std::sync::{Arc, Mutex};
4
5pub trait DebugProvider {
7 fn component_name(&self) -> &str;
9
10 fn debug_info(&self) -> String;
12
13 fn debug_summary(&self) -> Option<String> {
15 None
16 }
17}
18
19pub struct DebugService {
21 entries: Arc<Mutex<Vec<DebugEntry>>>,
23
24 max_entries: usize,
26
27 enabled: Arc<Mutex<bool>>,
29}
30
31#[derive(Clone, Debug)]
32pub struct DebugEntry {
33 pub timestamp: String,
34 pub component: String,
35 pub level: DebugLevel,
36 pub message: String,
37 pub context: Option<String>,
38}
39
40#[derive(Clone, Debug, PartialEq)]
41pub enum DebugLevel {
42 Info,
43 Warning,
44 Error,
45 Trace,
46}
47
48impl DebugService {
49 pub fn new(max_entries: usize) -> Self {
50 Self {
51 entries: Arc::new(Mutex::new(Vec::new())),
52 max_entries,
53 enabled: Arc::new(Mutex::new(false)),
54 }
55 }
56
57 pub fn clone_service(&self) -> Self {
59 Self {
60 entries: Arc::clone(&self.entries),
61 max_entries: self.max_entries,
62 enabled: Arc::clone(&self.enabled),
63 }
64 }
65
66 pub fn set_enabled(&self, enabled: bool) {
68 if let Ok(mut e) = self.enabled.lock() {
69 *e = enabled;
70 }
71 }
72
73 pub fn is_enabled(&self) -> bool {
75 self.enabled.lock().map(|e| *e).unwrap_or(false)
76 }
77
78 pub fn log(
80 &self,
81 component: &str,
82 level: DebugLevel,
83 message: String,
84 context: Option<String>,
85 ) {
86 if !self.is_enabled() {
87 return;
88 }
89
90 let entry = DebugEntry {
91 timestamp: Local::now().format("%H:%M:%S%.3f").to_string(),
92 component: component.to_string(),
93 level,
94 message,
95 context,
96 };
97
98 if let Ok(mut entries) = self.entries.lock() {
99 entries.push(entry);
100
101 if entries.len() > self.max_entries {
103 let remove_count = entries.len() - self.max_entries;
104 entries.drain(0..remove_count);
105 }
106 }
107 }
108
109 pub fn info(&self, component: &str, message: String) {
111 self.log(component, DebugLevel::Info, message, None);
112 }
113
114 pub fn warn(&self, component: &str, message: String) {
116 self.log(component, DebugLevel::Warning, message, None);
117 }
118
119 pub fn error(&self, component: &str, message: String) {
121 self.log(component, DebugLevel::Error, message, None);
122 }
123
124 pub fn trace(&self, component: &str, message: String, context: String) {
126 self.log(component, DebugLevel::Trace, message, Some(context));
127 }
128
129 pub fn get_entries(&self) -> Vec<DebugEntry> {
131 self.entries.lock().map(|e| e.clone()).unwrap_or_default()
132 }
133
134 pub fn get_recent_entries(&self, count: usize) -> Vec<DebugEntry> {
136 if let Ok(entries) = self.entries.lock() {
137 let start = entries.len().saturating_sub(count);
138 entries[start..].to_vec()
139 } else {
140 Vec::new()
141 }
142 }
143
144 pub fn clear(&self) {
146 if let Ok(mut entries) = self.entries.lock() {
147 entries.clear();
148 }
149 }
150
151 pub fn generate_dump(&self) -> String {
153 let mut dump = String::new();
154 dump.push_str("=== DEBUG SERVICE LOG ===\n\n");
155
156 if let Ok(entries) = self.entries.lock() {
157 if entries.is_empty() {
158 dump.push_str("No debug entries collected.\n");
159 } else {
160 dump.push_str(&format!(
161 "Total entries: {} (max: {})\n\n",
162 entries.len(),
163 self.max_entries
164 ));
165
166 for entry in entries.iter() {
167 let level_str = match entry.level {
168 DebugLevel::Info => "INFO ",
169 DebugLevel::Warning => "WARN ",
170 DebugLevel::Error => "ERROR",
171 DebugLevel::Trace => "TRACE",
172 };
173
174 dump.push_str(&format!(
175 "[{}] {} [{}] {}\n",
176 entry.timestamp, level_str, entry.component, entry.message
177 ));
178
179 if let Some(ref ctx) = entry.context {
180 dump.push_str(&format!(" Context: {}\n", ctx));
181 }
182 }
183 }
184 }
185
186 dump.push_str("\n=== END DEBUG LOG ===\n");
187 dump
188 }
189
190 pub fn generate_summary(&self) -> String {
192 let mut summary = String::new();
193 summary.push_str("=== DEBUG SUMMARY ===\n\n");
194
195 if let Ok(entries) = self.entries.lock() {
196 use std::collections::HashMap;
197 let mut component_counts: HashMap<String, (usize, usize, usize)> = HashMap::new();
198
199 for entry in entries.iter() {
200 let counts = component_counts
201 .entry(entry.component.clone())
202 .or_insert((0, 0, 0));
203 match entry.level {
204 DebugLevel::Error => counts.0 += 1,
205 DebugLevel::Warning => counts.1 += 1,
206 _ => counts.2 += 1,
207 }
208 }
209
210 for (component, (errors, warnings, others)) in component_counts {
211 summary.push_str(&format!(
212 "{}: {} errors, {} warnings, {} info/trace\n",
213 component, errors, warnings, others
214 ));
215 }
216 }
217
218 summary
219 }
220}
221
222#[macro_export]
232macro_rules! debug_trace {
233 ($service:expr, $component:expr, $msg:expr, $ctx:expr) => {
234 $service.trace($component, $msg.to_string(), $ctx.to_string())
235 };
236}