use aspect_core::{Aspect, AspectError, JoinPoint};
use std::any::Any;
#[derive(Clone)]
pub struct LoggingAspect {
level: LogLevel,
log_args: bool,
log_result: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
impl LoggingAspect {
pub fn new() -> Self {
Self {
level: LogLevel::Info,
log_args: false,
log_result: false,
}
}
pub fn with_level(mut self, level: LogLevel) -> Self {
self.level = level;
self
}
pub fn log_args(mut self) -> Self {
self.log_args = true;
self
}
pub fn log_result(mut self) -> Self {
self.log_result = true;
self
}
fn log(&self, level: LogLevel, message: &str) {
if level as u8 >= self.level as u8 {
match level {
LogLevel::Trace => log::trace!("{}", message),
LogLevel::Debug => log::debug!("{}", message),
LogLevel::Info => log::info!("{}", message),
LogLevel::Warn => log::warn!("{}", message),
LogLevel::Error => log::error!("{}", message),
}
}
}
}
impl Default for LoggingAspect {
fn default() -> Self {
Self::new()
}
}
impl Aspect for LoggingAspect {
fn before(&self, ctx: &JoinPoint) {
let message = format!(
"[ENTRY] {} ({}:{})",
ctx.function_name, ctx.location.file, ctx.location.line
);
self.log(self.level, &message);
}
fn after(&self, ctx: &JoinPoint, result: &dyn Any) {
let mut message = format!("[EXIT] {}", ctx.function_name);
if self.log_result {
message.push_str(&format!(" (result: {:?})", std::any::type_name_of_val(result)));
}
self.log(self.level, &message);
}
fn after_error(&self, ctx: &JoinPoint, error: &AspectError) {
let message = format!("[ERROR] {} failed: {:?}", ctx.function_name, error);
self.log(LogLevel::Error, &message);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logging_aspect_creation() {
let aspect = LoggingAspect::new();
assert_eq!(aspect.level, LogLevel::Info);
assert!(!aspect.log_args);
assert!(!aspect.log_result);
}
#[test]
fn test_logging_aspect_builder() {
let aspect = LoggingAspect::new()
.with_level(LogLevel::Debug)
.log_args()
.log_result();
assert_eq!(aspect.level, LogLevel::Debug);
assert!(aspect.log_args);
assert!(aspect.log_result);
}
#[test]
fn test_logging_aspect_before() {
let aspect = LoggingAspect::new();
let ctx = JoinPoint {
function_name: "test_function",
module_path: "test::module",
location: aspect_core::Location {
file: "test.rs",
line: 42,
},
};
aspect.before(&ctx);
}
}