#[macro_use]
extern crate log;
pub use log::LogLevel;
use std::collections::BTreeSet;
use std::sync::{Mutex, MutexGuard, PoisonError};
#[doc(hidden)]
pub struct __MessagesSet {
inner: Mutex<BTreeSet<String>>
}
impl __MessagesSet {
pub fn new() -> __MessagesSet {
__MessagesSet {
inner: Mutex::new(BTreeSet::new())
}
}
pub fn lock(&self) -> Result<MutexGuard<BTreeSet<String>>, PoisonError<MutexGuard<BTreeSet<String>>>> {
self.inner.lock()
}
}
#[macro_export]
macro_rules! log_once {
(@CREATE STATIC) => ({
use ::std::sync::{Once, ONCE_INIT};
static mut __SEEN_MESSAGES: *const $crate::__MessagesSet = 0 as *const _;
static ONCE: Once = ONCE_INIT;
unsafe {
ONCE.call_once(|| {
let singleton = $crate::__MessagesSet::new();
__SEEN_MESSAGES = ::std::mem::transmute(Box::new(singleton));
});
&(*__SEEN_MESSAGES)
}
});
(target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
#[allow(non_snake_case)]
let __SEEN_MESSAGES = log_once!(@CREATE STATIC);
let mut seen_messages = __SEEN_MESSAGES.lock().expect("Mutex was poisonned");
let formatted = format!($($arg)+);
let message = String::from(stringify!($target)) + stringify!($lvl) + &formatted;
if !seen_messages.contains(&message) {
seen_messages.insert(message);
log!(target: $target, $lvl, "{}", formatted);
}
});
($lvl:expr, $($arg:tt)+) => (log_once!(target: module_path!(), $lvl, $($arg)+));
}
#[macro_export]
macro_rules! error_once {
(target: $target:expr, $($arg:tt)*) => (
log_once!(target: $target, $crate::LogLevel::Error, $($arg)*);
);
($($arg:tt)*) => (
log_once!($crate::LogLevel::Error, $($arg)*);
)
}
#[macro_export]
macro_rules! warn_once {
(target: $target:expr, $($arg:tt)*) => (
log_once!(target: $target, $crate::LogLevel::Warn, $($arg)*);
);
($($arg:tt)*) => (
log_once!($crate::LogLevel::Warn, $($arg)*);
)
}
#[macro_export]
macro_rules! info_once {
(target: $target:expr, $($arg:tt)*) => (
log_once!(target: $target, $crate::LogLevel::Info, $($arg)*);
);
($($arg:tt)*) => (
log_once!($crate::LogLevel::Info, $($arg)*);
)
}
#[macro_export]
macro_rules! debug_once {
(target: $target:expr, $($arg:tt)*) => (
log_once!(target: $target, $crate::LogLevel::Debug, $($arg)*);
);
($($arg:tt)*) => (
log_once!($crate::LogLevel::Debug, $($arg)*);
)
}
#[macro_export]
macro_rules! trace_once {
(target: $target:expr, $($arg:tt)*) => (
log_once!(target: $target, $crate::LogLevel::Trace, $($arg)*);
);
($($arg:tt)*) => (
log_once!($crate::LogLevel::Trace, $($arg)*);
)
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use std::sync::{Once, ONCE_INIT};
use log::{Log, LogRecord, LogMetadata, LogLevelFilter};
struct SimpleLogger;
impl Log for SimpleLogger {
fn enabled(&self, _: &LogMetadata) -> bool {true}
fn log(&self, record: &LogRecord) {
println!("{}", record.args());
}
}
#[test]
fn called_once() {
static START: Once = ONCE_INIT;
START.call_once(|| {
::log::set_logger(|max_log_level| {
max_log_level.set(LogLevelFilter::Trace);
Box::new(SimpleLogger)
}).expect("Could not set the logger");
});
let counter = Cell::new(0);
let function = || {
counter.set(counter.get() + 1);
counter.get()
};
info_once!("Counter is: {}", function());
assert_eq!(counter.get(), 1);
}
}