1use serde_json::{json, Value};
7use std::collections::HashMap;
8use std::fmt::{self, Display};
9use std::fs::{File, OpenOptions};
10use std::io::{BufWriter, Write};
11use std::path::Path;
12use std::str::FromStr;
13use std::sync::{Arc, Mutex, RwLock};
14use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum LogLevel {
19 Error = 0,
20 Warn = 1,
21 Info = 2,
22 Debug = 3,
23 Trace = 4,
24}
25
26impl Display for LogLevel {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 LogLevel::Error => write!(f, "ERROR"),
30 LogLevel::Warn => write!(f, "WARN"),
31 LogLevel::Info => write!(f, "INFO"),
32 LogLevel::Debug => write!(f, "DEBUG"),
33 LogLevel::Trace => write!(f, "TRACE"),
34 }
35 }
36}
37
38impl FromStr for LogLevel {
39 type Err = String;
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 match s.to_uppercase().as_str() {
43 "ERROR" => Ok(LogLevel::Error),
44 "WARN" => Ok(LogLevel::Warn),
45 "INFO" => Ok(LogLevel::Info),
46 "DEBUG" => Ok(LogLevel::Debug),
47 "TRACE" => Ok(LogLevel::Trace),
48 _ => Err(format!("Invalid log level: {s}")),
49 }
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct LogEntry {
56 pub timestamp: SystemTime,
57 pub level: LogLevel,
58 pub message: String,
59 pub module: String,
60 pub file: String,
61 pub line: u32,
62 pub thread_id: String,
63 pub fields: HashMap<String, Value>,
64}
65
66impl LogEntry {
67 pub fn new(level: LogLevel, message: String, module: String, file: String, line: u32) -> Self {
68 Self {
69 timestamp: SystemTime::now(),
70 level,
71 message,
72 module,
73 file,
74 line,
75 thread_id: format!("{:?}", std::thread::current().id()),
76 fields: HashMap::new(),
77 }
78 }
79
80 pub fn with_field<V: Into<Value>>(mut self, key: String, value: V) -> Self {
81 self.fields.insert(key, value.into());
82 self
83 }
84
85 pub fn to_json(&self) -> Value {
86 let timestamp_ms = self
87 .timestamp
88 .duration_since(UNIX_EPOCH)
89 .unwrap_or_default()
90 .as_millis();
91
92 let mut json = json!({
93 "timestamp": timestamp_ms,
94 "level": self.level.to_string(),
95 "message": self.message,
96 "module": self.module,
97 "file": self.file,
98 "line": self.line,
99 "thread_id": self.thread_id,
100 });
101
102 if let Value::Object(ref mut map) = json {
103 for (key, value) in &self.fields {
104 map.insert(key.clone(), value.clone());
105 }
106 }
107
108 json
109 }
110
111 pub fn to_text(&self) -> String {
112 let timestamp = self
113 .timestamp
114 .duration_since(UNIX_EPOCH)
115 .unwrap_or_default()
116 .as_millis();
117
118 format!(
119 "[{}] {} [{}:{}] [{}] {} {}",
120 timestamp, self.level, self.file, self.line, self.thread_id, self.module, self.message
121 )
122 }
123}
124
125pub trait LogFormatter: Send + Sync {
127 fn format(&self, entry: &LogEntry) -> String;
128}
129
130pub struct JsonFormatter;
132
133impl LogFormatter for JsonFormatter {
134 fn format(&self, entry: &LogEntry) -> String {
135 entry.to_json().to_string()
136 }
137}
138
139pub struct TextFormatter;
141
142impl LogFormatter for TextFormatter {
143 fn format(&self, entry: &LogEntry) -> String {
144 entry.to_text()
145 }
146}
147
148pub trait LogOutput: Send + Sync {
150 fn write(&mut self, formatted_log: &str) -> Result<(), std::io::Error>;
151 fn flush(&mut self) -> Result<(), std::io::Error>;
152}
153
154pub struct ConsoleOutput;
156
157impl LogOutput for ConsoleOutput {
158 fn write(&mut self, formatted_log: &str) -> Result<(), std::io::Error> {
159 println!("{formatted_log}");
160 Ok(())
161 }
162
163 fn flush(&mut self) -> Result<(), std::io::Error> {
164 std::io::stdout().flush()
165 }
166}
167
168pub struct FileOutput {
170 writer: BufWriter<File>,
171}
172
173impl FileOutput {
174 pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
175 let file = OpenOptions::new().create(true).append(true).open(path)?;
176 Ok(Self {
177 writer: BufWriter::new(file),
178 })
179 }
180}
181
182impl LogOutput for FileOutput {
183 fn write(&mut self, formatted_log: &str) -> Result<(), std::io::Error> {
184 writeln!(self.writer, "{formatted_log}")?;
185 Ok(())
186 }
187
188 fn flush(&mut self) -> Result<(), std::io::Error> {
189 self.writer.flush()
190 }
191}
192
193#[derive(Debug, Clone)]
195pub struct LoggerConfig {
196 pub level: LogLevel,
197 pub module_filters: HashMap<String, LogLevel>,
198 pub enable_performance_logging: bool,
199 pub buffer_size: usize,
200 pub auto_flush: bool,
201 pub include_caller_info: bool,
202}
203
204impl Default for LoggerConfig {
205 fn default() -> Self {
206 Self {
207 level: LogLevel::Info,
208 module_filters: HashMap::new(),
209 enable_performance_logging: false,
210 buffer_size: 1000,
211 auto_flush: true,
212 include_caller_info: true,
213 }
214 }
215}
216
217pub struct Logger {
219 config: Arc<RwLock<LoggerConfig>>,
220 outputs: Arc<Mutex<Vec<Box<dyn LogOutput>>>>,
221 formatter: Arc<dyn LogFormatter>,
222 buffer: Arc<Mutex<Vec<LogEntry>>>,
223 stats: Arc<Mutex<LogStats>>,
224}
225
226#[derive(Debug, Default)]
227pub struct LogStats {
228 pub total_logs: u64,
229 pub logs_by_level: HashMap<LogLevel, u64>,
230 pub logs_by_module: HashMap<String, u64>,
231 pub buffer_overflows: u64,
232 pub write_errors: u64,
233}
234
235impl Logger {
236 pub fn new(config: LoggerConfig) -> Self {
237 Self {
238 config: Arc::new(RwLock::new(config)),
239 outputs: Arc::new(Mutex::new(Vec::new())),
240 formatter: Arc::new(TextFormatter),
241 buffer: Arc::new(Mutex::new(Vec::new())),
242 stats: Arc::new(Mutex::new(LogStats::default())),
243 }
244 }
245
246 pub fn with_formatter(mut self, formatter: Arc<dyn LogFormatter>) -> Self {
247 self.formatter = formatter;
248 self
249 }
250
251 pub fn add_output(&self, output: Box<dyn LogOutput>) {
252 let mut outputs = self.outputs.lock().expect("operation should succeed");
253 outputs.push(output);
254 }
255
256 pub fn log(&self, entry: LogEntry) {
257 let config = self.config.read().expect("operation should succeed");
258
259 if !self.should_log(&entry.level, &entry.module, &config) {
261 return;
262 }
263
264 {
266 let mut stats = self.stats.lock().expect("operation should succeed");
267 stats.total_logs += 1;
268 *stats.logs_by_level.entry(entry.level).or_insert(0) += 1;
269 *stats
270 .logs_by_module
271 .entry(entry.module.clone())
272 .or_insert(0) += 1;
273 }
274
275 {
277 let mut buffer = self.buffer.lock().expect("operation should succeed");
278 if buffer.len() >= config.buffer_size {
279 buffer.remove(0); let mut stats = self.stats.lock().expect("operation should succeed");
281 stats.buffer_overflows += 1;
282 }
283 buffer.push(entry.clone());
284 }
285
286 if config.auto_flush {
288 self.flush_entry(&entry);
289 }
290 }
291
292 fn should_log(&self, level: &LogLevel, module: &str, config: &LoggerConfig) -> bool {
293 if let Some(module_level) = config.module_filters.get(module) {
295 return level <= module_level;
296 }
297
298 level <= &config.level
300 }
301
302 fn flush_entry(&self, entry: &LogEntry) {
303 let formatted = self.formatter.format(entry);
304 let mut outputs = self.outputs.lock().expect("operation should succeed");
305
306 for output in outputs.iter_mut() {
307 if output.write(&formatted).is_err() {
308 let mut stats = self.stats.lock().expect("operation should succeed");
309 stats.write_errors += 1;
310 }
311 }
312 }
313
314 pub fn flush(&self) {
315 let buffer = {
316 let mut buffer = self.buffer.lock().expect("operation should succeed");
317 let entries = buffer.clone();
318 buffer.clear();
319 entries
320 };
321
322 for entry in buffer {
323 self.flush_entry(&entry);
324 }
325
326 let mut outputs = self.outputs.lock().expect("operation should succeed");
328 for output in outputs.iter_mut() {
329 let _ = output.flush();
330 }
331 }
332
333 pub fn set_level(&self, level: LogLevel) {
334 let mut config = self.config.write().expect("operation should succeed");
335 config.level = level;
336 }
337
338 pub fn set_module_level(&self, module: String, level: LogLevel) {
339 let mut config = self.config.write().expect("operation should succeed");
340 config.module_filters.insert(module, level);
341 }
342
343 pub fn stats(&self) -> LogStats {
344 self.stats.lock().expect("operation should succeed").clone()
345 }
346
347 pub fn clear_stats(&self) {
348 let mut stats = self.stats.lock().expect("operation should succeed");
349 *stats = LogStats::default();
350 }
351}
352
353impl Clone for LogStats {
354 fn clone(&self) -> Self {
355 Self {
356 total_logs: self.total_logs,
357 logs_by_level: self.logs_by_level.clone(),
358 logs_by_module: self.logs_by_module.clone(),
359 buffer_overflows: self.buffer_overflows,
360 write_errors: self.write_errors,
361 }
362 }
363}
364
365pub struct PerformanceLogger {
367 logger: Arc<Logger>,
368 operations: Arc<Mutex<HashMap<String, Vec<Duration>>>>,
369}
370
371impl PerformanceLogger {
372 pub fn new(logger: Arc<Logger>) -> Self {
373 Self {
374 logger,
375 operations: Arc::new(Mutex::new(HashMap::new())),
376 }
377 }
378
379 pub fn time_operation<F, R>(&self, name: &str, operation: F) -> R
380 where
381 F: FnOnce() -> R,
382 {
383 let start = Instant::now();
384 let result = operation();
385 let duration = start.elapsed();
386
387 {
389 let mut operations = self.operations.lock().expect("operation should succeed");
390 operations
391 .entry(name.to_string())
392 .or_default()
393 .push(duration);
394 }
395
396 let entry = LogEntry::new(
398 LogLevel::Debug,
399 format!("Operation '{name}' completed"),
400 "performance".to_string(),
401 "performance_logger.rs".to_string(),
402 0,
403 )
404 .with_field("operation".to_string(), name.to_string())
405 .with_field("duration_ms".to_string(), duration.as_millis() as f64);
406
407 self.logger.log(entry);
408
409 result
410 }
411
412 pub fn get_operation_stats(&self, name: &str) -> Option<OperationStats> {
413 let operations = self.operations.lock().expect("operation should succeed");
414 if let Some(durations) = operations.get(name) {
415 if durations.is_empty() {
416 return None;
417 }
418
419 let total_ms: f64 = durations.iter().map(|d| d.as_millis() as f64).sum();
420 let count = durations.len();
421 let avg_ms = total_ms / count as f64;
422
423 let mut sorted_durations = durations.clone();
424 sorted_durations.sort();
425
426 let min_ms = sorted_durations
427 .first()
428 .expect("operation should succeed")
429 .as_millis() as f64;
430 let max_ms = sorted_durations
431 .last()
432 .expect("operation should succeed")
433 .as_millis() as f64;
434
435 let median_ms = if count % 2 == 0 {
436 let mid = count / 2;
437 (sorted_durations[mid - 1].as_millis() + sorted_durations[mid].as_millis()) as f64
438 / 2.0
439 } else {
440 sorted_durations[count / 2].as_millis() as f64
441 };
442
443 Some(OperationStats {
444 name: name.to_string(),
445 count,
446 total_ms,
447 avg_ms,
448 min_ms,
449 max_ms,
450 median_ms,
451 })
452 } else {
453 None
454 }
455 }
456
457 pub fn clear_operation_stats(&self, name: &str) {
458 let mut operations = self.operations.lock().expect("operation should succeed");
459 operations.remove(name);
460 }
461
462 pub fn get_all_operations(&self) -> Vec<String> {
463 let operations = self.operations.lock().expect("operation should succeed");
464 operations.keys().cloned().collect()
465 }
466}
467
468#[derive(Debug, Clone)]
469pub struct OperationStats {
470 pub name: String,
471 pub count: usize,
472 pub total_ms: f64,
473 pub avg_ms: f64,
474 pub min_ms: f64,
475 pub max_ms: f64,
476 pub median_ms: f64,
477}
478
479pub struct DistributedLogger {
481 local_logger: Arc<Logger>,
482 node_id: String,
483 cluster_nodes: Arc<RwLock<Vec<String>>>,
484}
485
486impl DistributedLogger {
487 pub fn new(local_logger: Arc<Logger>, node_id: String) -> Self {
488 Self {
489 local_logger,
490 node_id,
491 cluster_nodes: Arc::new(RwLock::new(Vec::new())),
492 }
493 }
494
495 pub fn add_node(&self, node_id: String) {
496 let mut nodes = self
497 .cluster_nodes
498 .write()
499 .expect("operation should succeed");
500 if !nodes.contains(&node_id) {
501 nodes.push(node_id);
502 }
503 }
504
505 pub fn remove_node(&self, node_id: &str) {
506 let mut nodes = self
507 .cluster_nodes
508 .write()
509 .expect("operation should succeed");
510 nodes.retain(|id| id != node_id);
511 }
512
513 pub fn log_distributed(&self, mut entry: LogEntry) {
514 entry = entry.with_field("node_id".to_string(), self.node_id.clone());
516
517 self.local_logger.log(entry);
519
520 }
523
524 pub fn get_cluster_nodes(&self) -> Vec<String> {
525 self.cluster_nodes
526 .read()
527 .expect("operation should succeed")
528 .clone()
529 }
530}
531
532pub struct LogAnalyzer {
534 entries: Vec<LogEntry>,
535}
536
537impl LogAnalyzer {
538 pub fn new() -> Self {
539 Self {
540 entries: Vec::new(),
541 }
542 }
543
544 pub fn add_entries(&mut self, entries: Vec<LogEntry>) {
545 self.entries.extend(entries);
546 }
547
548 pub fn analyze_patterns(&self) -> LogAnalysis {
549 let mut analysis = LogAnalysis::default();
550
551 for entry in &self.entries {
552 analysis.total_entries += 1;
553 *analysis.entries_by_level.entry(entry.level).or_insert(0) += 1;
554 *analysis
555 .entries_by_module
556 .entry(entry.module.clone())
557 .or_insert(0) += 1;
558
559 if entry.level == LogLevel::Error {
561 *analysis
562 .error_patterns
563 .entry(entry.message.clone())
564 .or_insert(0) += 1;
565 }
566 }
567
568 analysis
569 }
570
571 pub fn find_errors_in_timeframe(&self, start: SystemTime, end: SystemTime) -> Vec<LogEntry> {
572 self.entries
573 .iter()
574 .filter(|entry| {
575 entry.level == LogLevel::Error && entry.timestamp >= start && entry.timestamp <= end
576 })
577 .cloned()
578 .collect()
579 }
580
581 pub fn get_module_activity(&self, module: &str) -> Vec<LogEntry> {
582 self.entries
583 .iter()
584 .filter(|entry| entry.module == module)
585 .cloned()
586 .collect()
587 }
588}
589
590#[derive(Debug, Default)]
591pub struct LogAnalysis {
592 pub total_entries: u64,
593 pub entries_by_level: HashMap<LogLevel, u64>,
594 pub entries_by_module: HashMap<String, u64>,
595 pub error_patterns: HashMap<String, u64>,
596}
597
598impl Default for LogAnalyzer {
599 fn default() -> Self {
600 Self::new()
601 }
602}
603
604lazy_static::lazy_static! {
605 static ref GLOBAL_LOGGER: Arc<Logger> = {
607 let config = LoggerConfig::default();
608 let logger = Arc::new(Logger::new(config));
609 logger.add_output(Box::new(ConsoleOutput));
610 logger
611 };
612}
613
614#[macro_export]
616macro_rules! log_error {
617 ($($arg:tt)*) => {
618 $crate::logging::log_with_level($crate::logging::LogLevel::Error, format!($($arg)*))
619 };
620}
621
622#[macro_export]
623macro_rules! log_warn {
624 ($($arg:tt)*) => {
625 $crate::logging::log_with_level($crate::logging::LogLevel::Warn, format!($($arg)*))
626 };
627}
628
629#[macro_export]
630macro_rules! log_info {
631 ($($arg:tt)*) => {
632 $crate::logging::log_with_level($crate::logging::LogLevel::Info, format!($($arg)*))
633 };
634}
635
636#[macro_export]
637macro_rules! log_debug {
638 ($($arg:tt)*) => {
639 $crate::logging::log_with_level($crate::logging::LogLevel::Debug, format!($($arg)*))
640 };
641}
642
643#[macro_export]
644macro_rules! log_trace {
645 ($($arg:tt)*) => {
646 $crate::logging::log_with_level($crate::logging::LogLevel::Trace, format!($($arg)*))
647 };
648}
649
650pub fn log_with_level(level: LogLevel, message: String) {
651 let entry = LogEntry::new(
652 level,
653 message,
654 "global".to_string(),
655 "unknown".to_string(),
656 0,
657 );
658 GLOBAL_LOGGER.log(entry);
659}
660
661pub fn get_global_logger() -> Arc<Logger> {
662 GLOBAL_LOGGER.clone()
663}
664
665pub fn set_global_level(level: LogLevel) {
666 GLOBAL_LOGGER.set_level(level);
667}
668
669pub fn flush_global_logger() {
670 GLOBAL_LOGGER.flush();
671}
672
673#[allow(non_snake_case)]
674#[cfg(test)]
675mod tests {
676 use super::*;
677 use std::sync::Arc;
678 use tempfile::NamedTempFile;
679
680 #[test]
681 fn test_log_levels() {
682 assert!(LogLevel::Error < LogLevel::Warn);
683 assert!(LogLevel::Warn < LogLevel::Info);
684 assert!(LogLevel::Info < LogLevel::Debug);
685 assert!(LogLevel::Debug < LogLevel::Trace);
686 }
687
688 #[test]
689 fn test_log_entry_creation() {
690 let entry = LogEntry::new(
691 LogLevel::Info,
692 "Test message".to_string(),
693 "test_module".to_string(),
694 "test.rs".to_string(),
695 42,
696 );
697
698 assert_eq!(entry.level, LogLevel::Info);
699 assert_eq!(entry.message, "Test message");
700 assert_eq!(entry.module, "test_module");
701 assert_eq!(entry.file, "test.rs");
702 assert_eq!(entry.line, 42);
703 }
704
705 #[test]
706 fn test_log_entry_with_fields() {
707 let entry = LogEntry::new(
708 LogLevel::Debug,
709 "Debug message".to_string(),
710 "test".to_string(),
711 "test.rs".to_string(),
712 1,
713 )
714 .with_field("key1".to_string(), "value1".to_string())
715 .with_field("key2".to_string(), 42);
716
717 assert_eq!(entry.fields.len(), 2);
718 assert_eq!(
719 entry.fields.get("key1").expect("operation should succeed"),
720 &Value::String("value1".to_string())
721 );
722 assert_eq!(
723 entry.fields.get("key2").expect("operation should succeed"),
724 &Value::Number(42.into())
725 );
726 }
727
728 #[test]
729 fn test_logger_creation() {
730 let config = LoggerConfig::default();
731 let logger = Logger::new(config);
732
733 let stats = logger.stats();
734 assert_eq!(stats.total_logs, 0);
735 }
736
737 #[test]
738 fn test_logger_with_file_output() {
739 let temp_file = NamedTempFile::new().expect("operation should succeed");
740 let config = LoggerConfig::default();
741 let logger = Logger::new(config);
742
743 let file_output = FileOutput::new(temp_file.path()).expect("operation should succeed");
744 logger.add_output(Box::new(file_output));
745
746 let entry = LogEntry::new(
747 LogLevel::Info,
748 "Test log".to_string(),
749 "test".to_string(),
750 "test.rs".to_string(),
751 1,
752 );
753
754 logger.log(entry);
755 logger.flush();
756
757 let stats = logger.stats();
758 assert_eq!(stats.total_logs, 1);
759 }
760
761 #[test]
762 fn test_performance_logger() {
763 let config = LoggerConfig::default();
764 let logger = Arc::new(Logger::new(config));
765 let perf_logger = PerformanceLogger::new(logger);
766
767 let result = perf_logger.time_operation("test_op", || {
768 std::thread::sleep(std::time::Duration::from_millis(10));
769 42
770 });
771
772 assert_eq!(result, 42);
773
774 let stats = perf_logger
775 .get_operation_stats("test_op")
776 .expect("operation should succeed");
777 assert_eq!(stats.count, 1);
778 assert!(stats.avg_ms >= 10.0);
779 }
780
781 #[test]
782 fn test_log_analyzer() {
783 let mut analyzer = LogAnalyzer::new();
784
785 let entries = vec![
786 LogEntry::new(
787 LogLevel::Info,
788 "Info message".to_string(),
789 "module1".to_string(),
790 "test.rs".to_string(),
791 1,
792 ),
793 LogEntry::new(
794 LogLevel::Error,
795 "Error message".to_string(),
796 "module1".to_string(),
797 "test.rs".to_string(),
798 2,
799 ),
800 LogEntry::new(
801 LogLevel::Debug,
802 "Debug message".to_string(),
803 "module2".to_string(),
804 "test.rs".to_string(),
805 3,
806 ),
807 ];
808
809 analyzer.add_entries(entries);
810 let analysis = analyzer.analyze_patterns();
811
812 assert_eq!(analysis.total_entries, 3);
813 assert_eq!(
814 *analysis
815 .entries_by_level
816 .get(&LogLevel::Info)
817 .expect("operation should succeed"),
818 1
819 );
820 assert_eq!(
821 *analysis
822 .entries_by_level
823 .get(&LogLevel::Error)
824 .expect("operation should succeed"),
825 1
826 );
827 assert_eq!(
828 *analysis
829 .entries_by_module
830 .get("module1")
831 .expect("operation should succeed"),
832 2
833 );
834 assert_eq!(
835 *analysis
836 .entries_by_module
837 .get("module2")
838 .expect("operation should succeed"),
839 1
840 );
841 }
842
843 #[test]
844 fn test_distributed_logger() {
845 let config = LoggerConfig::default();
846 let local_logger = Arc::new(Logger::new(config));
847 let dist_logger = DistributedLogger::new(local_logger, "node1".to_string());
848
849 dist_logger.add_node("node2".to_string());
850 dist_logger.add_node("node3".to_string());
851
852 let nodes = dist_logger.get_cluster_nodes();
853 assert_eq!(nodes.len(), 2);
854 assert!(nodes.contains(&"node2".to_string()));
855 assert!(nodes.contains(&"node3".to_string()));
856 }
857}