#![warn(missing_docs)]
extern crate log;
#[cfg(feature = "timestamp")]
extern crate chrono;
#[cfg(feature = "catch-panic")]
extern crate log_panics;
#[cfg(test)]
mod tests;
use std::env;
use std::io::{self, Write};
use log::{Log, LogLevelFilter, LogMetadata, LogRecord};
pub const REQUEST_TARGET: &'static str = "request";
pub fn init() {
log::set_logger(|max_level| {
let filter = get_max_level();
max_level.set(filter);
Box::new(Logger { filter: filter })
}).unwrap_or_else(|_| panic!("failed to initialize the logger"));
#[cfg(feature = "catch-panic")]
log_panics::init();
}
fn get_max_level() -> LogLevelFilter {
let vars = ["LOG", "LOG_LEVEL"];
for var in &vars {
if let Ok(level) = env::var(var) {
if let Ok(level) = level.parse() {
return level;
}
}
}
if env::var("TRACE").is_ok() {
LogLevelFilter::Trace
} else if env::var("DEBUG").is_ok() {
LogLevelFilter::Debug
} else {
LogLevelFilter::Warn
}
}
struct Logger {
filter: LogLevelFilter,
}
impl Log for Logger {
fn enabled(&self, metadata: &LogMetadata) -> bool {
self.filter >= metadata.level()
}
fn log(&self, record: &LogRecord) {
if self.enabled(record.metadata()) {
log(record);
}
}
}
#[cfg(feature = "timestamp")]
fn log(record: &LogRecord) {
let timestamp = chrono::Utc::now();
match record.target() {
REQUEST_TARGET => {
write!(&mut stdout(), "{} [REQUEST]: {}\n",
timestamp, record.args()
).unwrap_or_else(log_failure)
},
target => {
write!(&mut stderr(), "{} [{}] {}: {}\n",
timestamp, record.level(), target, record.args()
).unwrap_or_else(log_failure)
},
}
}
#[cfg(not(feature = "timestamp"))]
fn log(record: &LogRecord) {
match record.target() {
REQUEST_TARGET => {
write!(&mut stdout(), "[REQUEST]: {}\n", record.args())
.unwrap_or_else(log_failure)
},
target => {
write!(&mut stderr(), "[{}] {}: {}\n",
record.level(), target, record.args()
).unwrap_or_else(log_failure)
},
}
}
#[inline(never)]
#[cold]
fn log_failure(err: io::Error) {
panic!("unexpected error logging message: {}", err)
}
#[cfg(not(test))]
#[inline(always)]
fn stdout() -> io::Stdout {
io::stdout()
}
#[cfg(not(test))]
#[inline(always)]
fn stderr() -> io::Stderr {
io::stderr()
}
#[cfg(test)]
#[inline(always)]
fn stdout() -> Vec<u8> {
Vec::new()
}
#[cfg(test)]
#[inline(always)]
fn stderr() -> Vec<u8> {
Vec::new()
}