#[macro_export]
macro_rules! log_error {
($($arg:tt)*) => {
$crate::logger::__log_with_location(
$crate::config::LogLevel::Error,
format_args!($($arg)*),
file!(),
line!(),
Some(module_path!())
)
};
}
#[macro_export]
macro_rules! log_warning {
($($arg:tt)*) => {
$crate::logger::__log_with_location(
$crate::config::LogLevel::Warning,
format_args!($($arg)*),
file!(),
line!(),
Some(module_path!())
)
};
}
#[macro_export]
macro_rules! log_info {
($($arg:tt)*) => {
$crate::logger::__log_with_location(
$crate::config::LogLevel::Info,
format_args!($($arg)*),
file!(),
line!(),
Some(module_path!())
)
};
}
#[macro_export]
macro_rules! log_success {
($($arg:tt)*) => {
$crate::logger::__log_with_location(
$crate::config::LogLevel::Success,
format_args!($($arg)*),
file!(),
line!(),
Some(module_path!())
)
};
}
#[macro_export]
macro_rules! log_debug {
($($arg:tt)*) => {
$crate::logger::__log_with_location(
$crate::config::LogLevel::Debug,
format_args!($($arg)*),
file!(),
line!(),
Some(module_path!())
)
};
}
#[macro_export]
macro_rules! log {
($level:expr, $($arg:tt)*) => {
$crate::logger::__log_with_location(
$level,
format_args!($($arg)*),
file!(),
line!(),
Some(module_path!())
)
};
}
#[macro_export]
macro_rules! log_with_metadata {
($level:expr, $message:expr, $($key:expr => $value:expr),+ $(,)?) => {
{
let mut metadata_parts = Vec::new();
$(
metadata_parts.push(format!("{}={}", $key, $value));
)+
let metadata_str = metadata_parts.join(" ");
$crate::logger::__log_with_location(
$level,
format_args!("{} [{}]", $message, metadata_str),
file!(),
line!(),
Some(module_path!())
)
}
};
}
#[macro_export]
macro_rules! log_if {
($condition:expr, $level:expr, $($arg:tt)*) => {
if $condition {
let _ = $crate::log!($level, $($arg)*);
}
};
}
#[macro_export]
macro_rules! log_error_and_return {
($($arg:tt)*) => {
{
let _ = $crate::log_error!($($arg)*);
std::io::Error::new(std::io::ErrorKind::Other, format!($($arg)*))
}
};
}
#[macro_export]
macro_rules! time_block {
($level:expr, $name:expr, $block:block) => {{
let start = std::time::Instant::now();
let result = $block;
let duration = start.elapsed();
let _ = $crate::log!($level, "{} completed in {:?}", $name, duration);
result
}};
}
#[macro_export]
macro_rules! trace_function {
($func_name:expr) => {
let _ = $crate::log_debug!("Entering {}", $func_name);
let _guard = $crate::__FunctionTraceGuard::new($func_name);
};
($func_name:expr, $($arg:expr),+ $(,)?) => {
let _ = $crate::log_debug!("Entering {} with args: {:?}", $func_name, ($($arg,)+));
let _guard = $crate::__FunctionTraceGuard::new($func_name);
};
}
#[doc(hidden)]
pub struct __FunctionTraceGuard {
func_name: &'static str,
}
impl __FunctionTraceGuard {
#[doc(hidden)]
pub fn new(func_name: &'static str) -> Self {
Self { func_name }
}
}
impl Drop for __FunctionTraceGuard {
fn drop(&mut self) {
let _ = crate::log_debug!("Exiting {}", self.func_name);
}
}
#[macro_export]
macro_rules! log_once {
($level:expr, $($arg:tt)*) => {
{
use std::sync::Once;
static ONCE: Once = Once::new();
ONCE.call_once(|| {
let _ = $crate::log!($level, $($arg)*);
});
}
};
}
#[macro_export]
macro_rules! log_at_most {
($max_times:expr, $level:expr, $($arg:tt)*) => {
{
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let count = COUNTER.fetch_add(1, Ordering::Relaxed);
if count < $max_times {
let _ = $crate::log!($level, $($arg)*);
}
}
};
}
#[macro_export]
macro_rules! log_rate_limited {
($duration:expr, $level:expr, $($arg:tt)*) => {
{
use std::sync::Mutex;
use std::time::{Instant, Duration};
static LAST_LOG: Mutex<Option<Instant>> = Mutex::new(None);
let now = Instant::now();
let mut last_log = LAST_LOG.lock().unwrap();
let should_log = match *last_log {
Some(last) => now.duration_since(last) >= $duration,
None => true,
};
if should_log {
*last_log = Some(now);
let _ = $crate::log!($level, $($arg)*);
}
}
};
}
#[macro_export]
macro_rules! log_assert {
($condition:expr) => {
if !$condition {
let _ = $crate::log_error!("Assertion failed: {}", stringify!($condition));
panic!("Assertion failed: {}", stringify!($condition));
}
};
($condition:expr, $($arg:tt)*) => {
if !$condition {
let _ = $crate::log_error!("Assertion failed: {} - {}", stringify!($condition), format!($($arg)*));
panic!("Assertion failed: {} - {}", stringify!($condition), format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! log_debug_assert {
($condition:expr) => {
#[cfg(debug_assertions)]
$crate::log_assert!($condition);
};
($condition:expr, $($arg:tt)*) => {
#[cfg(debug_assertions)]
$crate::log_assert!($condition, $($arg)*);
};
}
#[cfg(test)]
mod tests {
use crate::config::{LogLevel, LoggerConfig};
use crate::logger;
use std::time::Duration;
#[test]
fn test_basic_logging_macros() {
let config = LoggerConfig::builder().console(true).colors(false).build();
let _ = logger::init(config);
assert!(log_error!("Test error message").is_ok());
assert!(log_warning!("Test warning message").is_ok());
assert!(log_info!("Test info message").is_ok());
assert!(log_success!("Test success message").is_ok());
assert!(log_debug!("Test debug message").is_ok());
}
#[test]
fn test_log_macro_with_level() {
let config = LoggerConfig::builder().console(true).colors(false).build();
let _ = logger::init(config);
assert!(log!(LogLevel::Error, "Custom level message").is_ok());
assert!(log!(LogLevel::Info, "User {} logged in", "alice").is_ok());
}
#[test]
fn test_conditional_logging() {
let config = LoggerConfig::builder().console(true).colors(false).build();
let _ = logger::init(config);
let debug_enabled = true;
let debug_disabled = false;
log_if!(debug_enabled, LogLevel::Debug, "Debug is enabled");
log_if!(debug_disabled, LogLevel::Debug, "Debug is disabled");
}
#[test]
fn test_time_block_macro() {
let config = LoggerConfig::builder().console(true).colors(false).build();
let _ = logger::init(config);
let result = time_block!(LogLevel::Info, "Test operation", {
std::thread::sleep(Duration::from_millis(10));
42
});
assert_eq!(result, 42);
}
#[test]
fn test_log_once_macro() {
let config = LoggerConfig::builder().console(true).colors(false).build();
let _ = logger::init(config);
for _ in 0..5 {
log_once!(LogLevel::Warning, "This should only appear once");
}
}
#[test]
fn test_log_at_most_macro() {
let config = LoggerConfig::builder().console(true).colors(false).build();
let _ = logger::init(config);
for i in 0..10 {
log_at_most!(3, LogLevel::Info, "Message {}", i);
}
}
#[test]
fn test_trace_function_macro() {
let config = LoggerConfig::builder()
.console(true)
.colors(false)
.level(LogLevel::Debug)
.build();
let _ = logger::init(config);
fn test_function(x: i32, y: i32) -> i32 {
trace_function!("test_function", x, y);
x + y
}
let result = test_function(5, 10);
assert_eq!(result, 15);
}
}