#[macro_use]
extern crate log;
extern crate thread_local;
use log::{LogRecord, LogLocation, LogLevel, LogLevelFilter, LogMetadata};
use std::{fmt, thread};
use std::io::Write;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use thread_local::ThreadLocal;
#[derive(Debug)]
pub struct LogMessage {
pub level: LogLevel,
pub target: String,
pub msg: String,
pub location: LogLocation,
}
impl fmt::Display for LogMessage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}] {}: {}", self.level, self.target, self.msg)
}
}
pub struct AsyncLogger {
failures: AtomicUsize,
sender: Mutex<SyncSender<LogMessage>>,
local_sender: thread_local::ThreadLocal<SyncSender<LogMessage>>,
}
impl AsyncLogger {
pub fn new(bufsize: usize) -> (AsyncLogger, Receiver<LogMessage>) {
let (tx, rx) = sync_channel(bufsize);
(AsyncLogger {
failures: AtomicUsize::new(0),
sender: Mutex::new(tx),
local_sender: ThreadLocal::new(),
},
rx)
}
#[inline]
pub fn failures(&self) -> usize {
self.failures.load(Ordering::Relaxed)
}
#[inline]
fn sender(&self) -> &SyncSender<LogMessage> {
self.local_sender.get_or(|| {
let guard = self.sender.lock().expect("lock poisoned while
trying to clone new receiver for thread");
let local_tx = guard.clone();
Box::new(local_tx)
})
}
}
impl log::Log for AsyncLogger {
fn enabled(&self, metadata: &LogMetadata) -> bool {
metadata.level() <= LogLevel::Debug
}
fn log(&self, record: &LogRecord) {
let msg = LogMessage {
level: record.metadata().level(),
target: record.metadata().target().to_owned(),
msg: format!("{}", record.args()),
location: record.location().clone(),
};
if let Err(_) = self.sender().try_send(msg) {
self.failures.fetch_add(1, Ordering::Relaxed);
}
}
}
pub fn init<F>(bufsize: usize, mut handle: F) -> Result<(), log::SetLoggerError>
where F: FnMut(LogMessage) -> (),
F: Send + 'static
{
let (logger, rx) = AsyncLogger::new(bufsize);
thread::spawn(move || {
loop {
let msg = match rx.recv() {
Ok(m) => m,
Err(_) => break,
};
handle(msg);
}
});
log::set_logger(move |max_log_level| {
max_log_level.set(LogLevelFilter::Debug);
Box::new(logger)
})
}
pub fn init_stderr() -> Result<(), log::SetLoggerError> {
init(128, move |msg| {
writeln!(&mut std::io::stderr(), "{}", msg).expect("Error writing
log to stderr");
})
}