Skip to main content

aspect_std/
logging.rs

1//! Structured logging aspect with configurable levels.
2
3use aspect_core::{Aspect, AspectError, JoinPoint};
4use std::any::Any;
5
6/// Logging aspect with configurable log levels and output.
7///
8/// Provides structured logging for function entry, exit, and errors.
9///
10/// # Example
11///
12/// ```rust,ignore
13/// use aspect_std::LoggingAspect;
14/// use aspect_macros::aspect;
15///
16/// #[aspect(LoggingAspect::new())]
17/// fn my_function(x: i32) -> Result<i32, String> {
18///     Ok(x * 2)
19/// }
20/// ```
21#[derive(Clone)]
22pub struct LoggingAspect {
23    level: LogLevel,
24    log_args: bool,
25    log_result: bool,
26}
27
28/// Log level for the logging aspect.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum LogLevel {
31    /// Trace level (most verbose)
32    Trace,
33    /// Debug level
34    Debug,
35    /// Info level (default)
36    Info,
37    /// Warn level
38    Warn,
39    /// Error level
40    Error,
41}
42
43impl LoggingAspect {
44    /// Create a new logging aspect with Info level.
45    pub fn new() -> Self {
46        Self {
47            level: LogLevel::Info,
48            log_args: false,
49            log_result: false,
50        }
51    }
52
53    /// Set the log level.
54    pub fn with_level(mut self, level: LogLevel) -> Self {
55        self.level = level;
56        self
57    }
58
59    /// Enable logging of function arguments (disabled by default).
60    pub fn log_args(mut self) -> Self {
61        self.log_args = true;
62        self
63    }
64
65    /// Enable logging of function results (disabled by default).
66    pub fn log_result(mut self) -> Self {
67        self.log_result = true;
68        self
69    }
70
71    fn log(&self, level: LogLevel, message: &str) {
72        if level as u8 >= self.level as u8 {
73            match level {
74                LogLevel::Trace => log::trace!("{}", message),
75                LogLevel::Debug => log::debug!("{}", message),
76                LogLevel::Info => log::info!("{}", message),
77                LogLevel::Warn => log::warn!("{}", message),
78                LogLevel::Error => log::error!("{}", message),
79            }
80        }
81    }
82}
83
84impl Default for LoggingAspect {
85    fn default() -> Self {
86        Self::new()
87    }
88}
89
90impl Aspect for LoggingAspect {
91    fn before(&self, ctx: &JoinPoint) {
92        let message = format!(
93            "[ENTRY] {} ({}:{})",
94            ctx.function_name, ctx.location.file, ctx.location.line
95        );
96        self.log(self.level, &message);
97    }
98
99    fn after(&self, ctx: &JoinPoint, result: &dyn Any) {
100        let mut message = format!("[EXIT] {}", ctx.function_name);
101
102        if self.log_result {
103            message.push_str(&format!(" (result: {:?})", std::any::type_name_of_val(result)));
104        }
105
106        self.log(self.level, &message);
107    }
108
109    fn after_error(&self, ctx: &JoinPoint, error: &AspectError) {
110        let message = format!("[ERROR] {} failed: {:?}", ctx.function_name, error);
111        self.log(LogLevel::Error, &message);
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_logging_aspect_creation() {
121        let aspect = LoggingAspect::new();
122        assert_eq!(aspect.level, LogLevel::Info);
123        assert!(!aspect.log_args);
124        assert!(!aspect.log_result);
125    }
126
127    #[test]
128    fn test_logging_aspect_builder() {
129        let aspect = LoggingAspect::new()
130            .with_level(LogLevel::Debug)
131            .log_args()
132            .log_result();
133
134        assert_eq!(aspect.level, LogLevel::Debug);
135        assert!(aspect.log_args);
136        assert!(aspect.log_result);
137    }
138
139    #[test]
140    fn test_logging_aspect_before() {
141        let aspect = LoggingAspect::new();
142        let ctx = JoinPoint {
143            function_name: "test_function",
144            module_path: "test::module",
145            location: aspect_core::Location {
146                file: "test.rs",
147                line: 42,
148            },
149        };
150
151        // Should not panic
152        aspect.before(&ctx);
153    }
154}