1use crate::error::{Result, SaferRingError};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub enum LogLevel {
14 Trace = 0,
16 Debug = 1,
18 Info = 2,
20 Warn = 3,
22 Error = 4,
24}
25
26impl std::fmt::Display for LogLevel {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 LogLevel::Trace => write!(f, "TRACE"),
30 LogLevel::Debug => write!(f, "DEBUG"),
31 LogLevel::Info => write!(f, "INFO"),
32 LogLevel::Warn => write!(f, "WARN"),
33 LogLevel::Error => write!(f, "ERROR"),
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
40pub struct LogEntry {
41 pub timestamp: SystemTime,
43 pub level: LogLevel,
45 pub component: String,
47 pub operation_id: Option<u64>,
49 pub fd: Option<i32>,
51 pub message: String,
53 pub metadata: HashMap<String, String>,
55 pub duration: Option<Duration>,
57}
58
59impl LogEntry {
60 pub fn new(level: LogLevel, component: &str, message: &str) -> Self {
62 Self {
63 timestamp: SystemTime::now(),
64 level,
65 component: component.to_string(),
66 operation_id: None,
67 fd: None,
68 message: message.to_string(),
69 metadata: HashMap::new(),
70 duration: None,
71 }
72 }
73
74 pub fn with_operation_id(mut self, operation_id: u64) -> Self {
76 self.operation_id = Some(operation_id);
77 self
78 }
79
80 pub fn with_fd(mut self, fd: i32) -> Self {
82 self.fd = Some(fd);
83 self
84 }
85
86 pub fn with_metadata(mut self, key: &str, value: &str) -> Self {
88 self.metadata.insert(key.to_string(), value.to_string());
89 self
90 }
91
92 pub fn with_duration(mut self, duration: Duration) -> Self {
94 self.duration = Some(duration);
95 self
96 }
97
98 pub fn format(&self) -> String {
100 let timestamp = self
101 .timestamp
102 .duration_since(UNIX_EPOCH)
103 .unwrap_or_default()
104 .as_millis();
105
106 let mut parts = vec![
107 format!("[{}]", timestamp),
108 format!("{}", self.level),
109 format!("{}", self.component),
110 ];
111
112 if let Some(op_id) = self.operation_id {
113 parts.push(format!("op:{op_id}"));
114 }
115
116 if let Some(fd) = self.fd {
117 parts.push(format!("fd:{fd}"));
118 }
119
120 parts.push(self.message.clone());
121
122 if let Some(duration) = self.duration {
123 parts.push(format!("duration:{}μs", duration.as_micros()));
124 }
125
126 if !self.metadata.is_empty() {
127 let metadata_str = self
128 .metadata
129 .iter()
130 .map(|(k, v)| format!("{k}:{v}"))
131 .collect::<Vec<_>>()
132 .join(",");
133 parts.push(format!("metadata:{{{metadata_str}}}"));
134 }
135
136 parts.join(" ")
137 }
138
139 pub fn format_json(&self) -> String {
141 let timestamp = self
142 .timestamp
143 .duration_since(UNIX_EPOCH)
144 .unwrap_or_default()
145 .as_millis();
146
147 let mut json_parts = vec![
148 format!("\"timestamp\":{}", timestamp),
149 format!("\"level\":\"{}\"", self.level),
150 format!("\"component\":\"{}\"", self.component),
151 format!("\"message\":\"{}\"", self.message.replace('"', "\\\"")),
152 ];
153
154 if let Some(op_id) = self.operation_id {
155 json_parts.push(format!("\"operation_id\":{op_id}"));
156 }
157
158 if let Some(fd) = self.fd {
159 json_parts.push(format!("\"fd\":{fd}"));
160 }
161
162 if let Some(duration) = self.duration {
163 json_parts.push(format!("\"duration_us\":{}", duration.as_micros()));
164 }
165
166 if !self.metadata.is_empty() {
167 let metadata_json = self
168 .metadata
169 .iter()
170 .map(|(k, v)| format!("\"{}\":\"{}\"", k, v.replace('"', "\\\"")))
171 .collect::<Vec<_>>()
172 .join(",");
173 json_parts.push(format!("\"metadata\":{{{metadata_json}}}"));
174 }
175
176 format!("{{{}}}", json_parts.join(","))
177 }
178}
179
180pub trait LogOutput: Send + Sync {
182 fn write(&self, entry: &LogEntry) -> Result<()>;
184
185 fn flush(&self) -> Result<()>;
187}
188
189#[derive(Debug)]
191pub struct ConsoleOutput {
192 json_format: bool,
194}
195
196impl ConsoleOutput {
197 pub fn new() -> Self {
199 Self { json_format: false }
200 }
201
202 pub fn new_json() -> Self {
204 Self { json_format: true }
205 }
206}
207
208impl Default for ConsoleOutput {
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214impl LogOutput for ConsoleOutput {
215 fn write(&self, entry: &LogEntry) -> Result<()> {
216 let formatted = if self.json_format {
217 entry.format_json()
218 } else {
219 entry.format()
220 };
221
222 eprintln!("{formatted}");
223 Ok(())
224 }
225
226 fn flush(&self) -> Result<()> {
227 use std::io::Write;
228 std::io::stderr().flush().map_err(SaferRingError::Io)?;
229 Ok(())
230 }
231}
232
233#[derive(Debug)]
235pub struct FileOutput {
236 path: std::path::PathBuf,
238 json_format: bool,
240}
241
242impl FileOutput {
243 pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
245 Self {
246 path: path.as_ref().to_path_buf(),
247 json_format: false,
248 }
249 }
250
251 pub fn new_json<P: AsRef<std::path::Path>>(path: P) -> Self {
253 Self {
254 path: path.as_ref().to_path_buf(),
255 json_format: true,
256 }
257 }
258}
259
260impl LogOutput for FileOutput {
261 fn write(&self, entry: &LogEntry) -> Result<()> {
262 use std::io::Write;
263
264 let formatted = if self.json_format {
265 entry.format_json()
266 } else {
267 entry.format()
268 };
269
270 let mut file = std::fs::OpenOptions::new()
271 .create(true)
272 .append(true)
273 .open(&self.path)
274 .map_err(SaferRingError::Io)?;
275
276 writeln!(file, "{formatted}").map_err(SaferRingError::Io)?;
277 Ok(())
278 }
279
280 fn flush(&self) -> Result<()> {
281 Ok(())
283 }
284}
285
286pub struct Logger {
288 min_level: LogLevel,
290 outputs: Vec<Box<dyn LogOutput>>,
292 metrics: Arc<Mutex<PerformanceMetrics>>,
294}
295
296impl Logger {
297 pub fn new() -> Self {
299 Self {
300 min_level: LogLevel::Info,
301 outputs: vec![Box::new(ConsoleOutput::new())],
302 metrics: Arc::new(Mutex::new(PerformanceMetrics::new())),
303 }
304 }
305
306 pub fn set_level(&mut self, level: LogLevel) {
308 self.min_level = level;
309 }
310
311 pub fn add_output(&mut self, output: Box<dyn LogOutput>) {
313 self.outputs.push(output);
314 }
315
316 pub fn log(&self, level: LogLevel, component: &str, message: &str) {
318 if level >= self.min_level {
319 let entry = LogEntry::new(level, component, message);
320 self.write_entry(&entry);
321 }
322 }
323
324 pub fn log_operation(
326 &self,
327 level: LogLevel,
328 component: &str,
329 operation_id: u64,
330 fd: Option<i32>,
331 message: &str,
332 ) {
333 if level >= self.min_level {
334 let mut entry =
335 LogEntry::new(level, component, message).with_operation_id(operation_id);
336
337 if let Some(fd) = fd {
338 entry = entry.with_fd(fd);
339 }
340
341 self.write_entry(&entry);
342 }
343 }
344
345 pub fn log_timing(&self, component: &str, operation: &str, duration: Duration) {
347 if LogLevel::Debug >= self.min_level {
348 let entry = LogEntry::new(
349 LogLevel::Debug,
350 component,
351 &format!("{operation} completed"),
352 )
353 .with_duration(duration);
354 self.write_entry(&entry);
355
356 if let Ok(mut metrics) = self.metrics.lock() {
358 metrics.record_operation(operation, duration);
359 }
360 }
361 }
362
363 pub fn log_error(&self, component: &str, error: &SaferRingError, context: &str) {
365 let message = format!("{context}: {error}");
366 let entry = LogEntry::new(LogLevel::Error, component, &message);
367 self.write_entry(&entry);
368 }
369
370 fn write_entry(&self, entry: &LogEntry) {
372 for output in &self.outputs {
373 if let Err(e) = output.write(entry) {
374 eprintln!("Failed to write log entry: {e}");
375 }
376 }
377 }
378
379 pub fn flush(&self) {
381 for output in &self.outputs {
382 if let Err(e) = output.flush() {
383 eprintln!("Failed to flush log output: {e}");
384 }
385 }
386 }
387
388 pub fn get_metrics(&self) -> Result<PerformanceMetrics> {
390 self.metrics.lock().map(|m| m.clone()).map_err(|_| {
391 SaferRingError::Io(std::io::Error::other("Failed to acquire metrics lock"))
392 })
393 }
394
395 pub fn reset_metrics(&self) -> Result<()> {
397 self.metrics.lock().map(|mut m| m.reset()).map_err(|_| {
398 SaferRingError::Io(std::io::Error::other("Failed to acquire metrics lock"))
399 })
400 }
401}
402
403impl Default for Logger {
404 fn default() -> Self {
405 Self::new()
406 }
407}
408
409#[derive(Debug, Clone)]
411pub struct PerformanceMetrics {
412 operation_counts: HashMap<String, u64>,
414 operation_durations: HashMap<String, Duration>,
416 min_durations: HashMap<String, Duration>,
418 max_durations: HashMap<String, Duration>,
420 start_time: Instant,
422}
423
424impl PerformanceMetrics {
425 pub fn new() -> Self {
427 Self {
428 operation_counts: HashMap::new(),
429 operation_durations: HashMap::new(),
430 min_durations: HashMap::new(),
431 max_durations: HashMap::new(),
432 start_time: Instant::now(),
433 }
434 }
435
436 pub fn record_operation(&mut self, operation: &str, duration: Duration) {
438 let count = self
439 .operation_counts
440 .entry(operation.to_string())
441 .or_insert(0);
442 *count += 1;
443
444 let total_duration = self
445 .operation_durations
446 .entry(operation.to_string())
447 .or_insert(Duration::ZERO);
448 *total_duration += duration;
449
450 let min_duration = self
451 .min_durations
452 .entry(operation.to_string())
453 .or_insert(duration);
454 if duration < *min_duration {
455 *min_duration = duration;
456 }
457
458 let max_duration = self
459 .max_durations
460 .entry(operation.to_string())
461 .or_insert(duration);
462 if duration > *max_duration {
463 *max_duration = duration;
464 }
465 }
466
467 pub fn get_count(&self, operation: &str) -> u64 {
469 self.operation_counts.get(operation).copied().unwrap_or(0)
470 }
471
472 pub fn get_average_duration(&self, operation: &str) -> Option<Duration> {
474 let count = self.get_count(operation);
475 if count == 0 {
476 return None;
477 }
478
479 self.operation_durations
480 .get(operation)
481 .map(|total| *total / count as u32)
482 }
483
484 pub fn get_min_duration(&self, operation: &str) -> Option<Duration> {
486 self.min_durations.get(operation).copied()
487 }
488
489 pub fn get_max_duration(&self, operation: &str) -> Option<Duration> {
491 self.max_durations.get(operation).copied()
492 }
493
494 pub fn get_operation_types(&self) -> Vec<String> {
496 self.operation_counts.keys().cloned().collect()
497 }
498
499 pub fn get_total_operations(&self) -> u64 {
501 self.operation_counts.values().sum()
502 }
503
504 pub fn get_collection_duration(&self) -> Duration {
506 self.start_time.elapsed()
507 }
508
509 pub fn reset(&mut self) {
511 self.operation_counts.clear();
512 self.operation_durations.clear();
513 self.min_durations.clear();
514 self.max_durations.clear();
515 self.start_time = Instant::now();
516 }
517
518 pub fn generate_report(&self) -> String {
520 let mut report = String::new();
521 report.push_str("=== Safer-Ring Performance Metrics ===\n");
522 report.push_str(&format!(
523 "Collection Duration: {:?}\n",
524 self.get_collection_duration()
525 ));
526 report.push_str(&format!(
527 "Total Operations: {}\n\n",
528 self.get_total_operations()
529 ));
530
531 for operation in self.get_operation_types() {
532 report.push_str(&format!("Operation: {operation}\n"));
533 report.push_str(&format!(" Count: {}\n", self.get_count(&operation)));
534
535 if let Some(avg) = self.get_average_duration(&operation) {
536 report.push_str(&format!(" Average Duration: {avg:?}\n"));
537 }
538
539 if let Some(min) = self.get_min_duration(&operation) {
540 report.push_str(&format!(" Min Duration: {min:?}\n"));
541 }
542
543 if let Some(max) = self.get_max_duration(&operation) {
544 report.push_str(&format!(" Max Duration: {max:?}\n"));
545 }
546
547 report.push('\n');
548 }
549
550 report
551 }
552}
553
554impl Default for PerformanceMetrics {
555 fn default() -> Self {
556 Self::new()
557 }
558}
559
560static GLOBAL_LOGGER: std::sync::OnceLock<Arc<Mutex<Logger>>> = std::sync::OnceLock::new();
562
563pub fn init_logger() -> Arc<Mutex<Logger>> {
565 GLOBAL_LOGGER
566 .get_or_init(|| Arc::new(Mutex::new(Logger::new())))
567 .clone()
568}
569
570pub fn log(level: LogLevel, component: &str, message: &str) {
572 if let Some(logger) = GLOBAL_LOGGER.get() {
573 if let Ok(logger) = logger.lock() {
574 logger.log(level, component, message);
575 }
576 }
577}
578
579pub fn log_operation(
581 level: LogLevel,
582 component: &str,
583 operation_id: u64,
584 fd: Option<i32>,
585 message: &str,
586) {
587 if let Some(logger) = GLOBAL_LOGGER.get() {
588 if let Ok(logger) = logger.lock() {
589 logger.log_operation(level, component, operation_id, fd, message);
590 }
591 }
592}
593
594pub fn log_timing(component: &str, operation: &str, duration: Duration) {
596 if let Some(logger) = GLOBAL_LOGGER.get() {
597 if let Ok(logger) = logger.lock() {
598 logger.log_timing(component, operation, duration);
599 }
600 }
601}
602
603#[macro_export]
607macro_rules! log_trace {
608 ($component:expr, $($arg:tt)*) => {
609 $crate::logging::log($crate::logging::LogLevel::Trace, $component, &format!($($arg)*))
610 };
611}
612
613#[macro_export]
615macro_rules! log_debug {
616 ($component:expr, $($arg:tt)*) => {
617 $crate::logging::log($crate::logging::LogLevel::Debug, $component, &format!($($arg)*))
618 };
619}
620
621#[macro_export]
623macro_rules! log_info {
624 ($component:expr, $($arg:tt)*) => {
625 $crate::logging::log($crate::logging::LogLevel::Info, $component, &format!($($arg)*))
626 };
627}
628
629#[macro_export]
631macro_rules! log_warn {
632 ($component:expr, $($arg:tt)*) => {
633 $crate::logging::log($crate::logging::LogLevel::Warn, $component, &format!($($arg)*))
634 };
635}
636
637#[macro_export]
639macro_rules! log_error {
640 ($component:expr, $($arg:tt)*) => {
641 $crate::logging::log($crate::logging::LogLevel::Error, $component, &format!($($arg)*))
642 };
643}
644
645#[cfg(test)]
646mod tests {
647 use super::*;
648 #[test]
651 fn test_log_entry_creation() {
652 let entry = LogEntry::new(LogLevel::Info, "test", "test message")
653 .with_operation_id(123)
654 .with_fd(4)
655 .with_metadata("key", "value")
656 .with_duration(Duration::from_millis(10));
657
658 assert_eq!(entry.level, LogLevel::Info);
659 assert_eq!(entry.component, "test");
660 assert_eq!(entry.message, "test message");
661 assert_eq!(entry.operation_id, Some(123));
662 assert_eq!(entry.fd, Some(4));
663 assert_eq!(entry.metadata.get("key"), Some(&"value".to_string()));
664 assert_eq!(entry.duration, Some(Duration::from_millis(10)));
665 }
666
667 #[test]
668 fn test_log_entry_formatting() {
669 let entry = LogEntry::new(LogLevel::Info, "test", "test message");
670 let formatted = entry.format();
671
672 assert!(formatted.contains("INFO"));
673 assert!(formatted.contains("test"));
674 assert!(formatted.contains("test message"));
675 }
676
677 #[test]
678 fn test_log_entry_json_formatting() {
679 let entry = LogEntry::new(LogLevel::Info, "test", "test message");
680 let json = entry.format_json();
681
682 assert!(json.contains("\"level\":\"INFO\""));
683 assert!(json.contains("\"component\":\"test\""));
684 assert!(json.contains("\"message\":\"test message\""));
685 }
686
687 #[test]
688 fn test_logger_creation() {
689 let logger = Logger::new();
690 assert_eq!(logger.min_level, LogLevel::Info);
691 assert_eq!(logger.outputs.len(), 1);
692 }
693
694 #[test]
695 fn test_performance_metrics() {
696 let mut metrics = PerformanceMetrics::new();
697
698 metrics.record_operation("read", Duration::from_millis(10));
699 metrics.record_operation("read", Duration::from_millis(20));
700 metrics.record_operation("write", Duration::from_millis(15));
701
702 assert_eq!(metrics.get_count("read"), 2);
703 assert_eq!(metrics.get_count("write"), 1);
704 assert_eq!(metrics.get_total_operations(), 3);
705
706 assert_eq!(
707 metrics.get_average_duration("read"),
708 Some(Duration::from_millis(15))
709 );
710 assert_eq!(
711 metrics.get_min_duration("read"),
712 Some(Duration::from_millis(10))
713 );
714 assert_eq!(
715 metrics.get_max_duration("read"),
716 Some(Duration::from_millis(20))
717 );
718 }
719
720 #[test]
721 fn test_global_logger() {
722 let _logger = init_logger();
723
724 log(LogLevel::Info, "test", "test message");
725 log_operation(LogLevel::Debug, "test", 123, Some(4), "operation message");
726 log_timing("test", "read", Duration::from_millis(10));
727 }
728}