use std::sync::Arc;
use std::{cell::RefCell, io};
use arc_swap::{ArcSwap, ArcSwapAny};
use env_filter::{self, Filter};
use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
pub mod fmt;
pub mod platform;
use self::fmt::writer::{self, Writer};
use self::fmt::{FormatFn, Formatter};
pub struct Logger {
writer: Writer,
filter: Arc<ArcSwapAny<Arc<Filter>>>,
format: FormatFn,
}
#[derive(Default)]
pub struct Builder {
filter: env_filter::Builder,
writer: writer::Builder,
format: fmt::Builder,
built: bool,
}
impl Builder {
pub fn new() -> Builder {
Default::default()
}
pub fn format<F>(&mut self, format: F) -> &mut Self
where
F: 'static + Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send,
{
self.format.custom_format = Some(Box::new(format));
self
}
pub fn default_format(&mut self) -> &mut Self {
self.format = Default::default();
self
}
pub fn format_level(&mut self, write: bool) -> &mut Self {
self.format.format_level = write;
self
}
pub fn format_module_path(&mut self, write: bool) -> &mut Self {
self.format.format_module_path = write;
self
}
pub fn format_target(&mut self, write: bool) -> &mut Self {
self.format.format_target = write;
self
}
pub fn format_indent(&mut self, indent: Option<usize>) -> &mut Self {
self.format.format_indent = indent;
self
}
pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self {
self.format.format_suffix = suffix;
self
}
pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
self.filter.filter_module(module, level);
self
}
pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
self.filter.filter_level(level);
self
}
pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
self.filter.filter(module, level);
self
}
pub fn parse_filters(&mut self, filters: &str) -> &mut Self {
self.filter.parse(filters);
self
}
pub fn try_init(&mut self) -> Result<LoggerConfig, SetLoggerError> {
let (logger, filter) = self.build();
let max_level = logger.filter();
log::set_boxed_logger(Box::new(logger))?;
log::set_max_level(max_level);
Ok(filter)
}
pub fn init(&mut self) {
self.try_init()
.expect("Builder::init should not be called after logger initialized");
}
pub fn build(&mut self) -> (Logger, LoggerConfig) {
assert!(!self.built, "attempt to re-use consumed builder");
self.built = true;
let filter = Arc::new(ArcSwap::from_pointee(self.filter.build()));
(
Logger {
writer: self.writer.build(),
filter: filter.clone(),
format: self.format.build(),
},
LoggerConfig { filter },
)
}
}
pub struct LoggerConfig {
filter: Arc<ArcSwapAny<Arc<Filter>>>,
}
impl LoggerConfig {
pub fn update_filters(&self, filters: &str) {
let new_filter = env_filter::Builder::default().parse(filters).build();
let max_level = new_filter.filter();
self.filter.swap(Arc::new(new_filter));
log::set_max_level(max_level);
}
}
impl Logger {
pub fn filter(&self) -> LevelFilter {
self.filter.load().filter()
}
pub fn matches(&self, record: &Record) -> bool {
self.filter.load().matches(record)
}
}
impl Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
self.filter.load().enabled(metadata)
}
fn log(&self, record: &Record) {
if self.matches(record) {
thread_local! {
static FORMATTER: RefCell<Option<Formatter>> = const { RefCell::new(None) };
}
let print = |formatter: &mut Formatter, record: &Record| {
let _ =
(self.format)(formatter, record).and_then(|_| formatter.print(&self.writer));
formatter.clear();
};
let printed = FORMATTER
.try_with(|tl_buf| {
match tl_buf.try_borrow_mut() {
Ok(mut tl_buf) => match *tl_buf {
Some(ref mut formatter) => {
print(formatter, record);
}
None => {
let mut formatter = Formatter::new(&self.writer);
print(&mut formatter, record);
*tl_buf = Some(formatter);
}
},
Err(_) => {
print(&mut Formatter::new(&self.writer), record);
}
}
})
.is_ok();
if !printed {
print(&mut Formatter::new(&self.writer), record);
}
}
}
fn flush(&self) {}
}
mod std_fmt_impls {
use super::*;
use std::fmt;
impl fmt::Debug for Logger {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Logger")
.field("filter", &self.filter)
.finish()
}
}
impl fmt::Debug for Builder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.built {
f.debug_struct("Logger").field("built", &true).finish()
} else {
f.debug_struct("Logger")
.field("filter", &self.filter)
.field("writer", &self.writer)
.finish()
}
}
}
}
#[cfg(test)]
mod tests {
use log::*;
use super::*;
#[test]
fn update_filter_at_runtime() {
let config = Builder::default()
.filter_level(LevelFilter::Debug)
.try_init()
.unwrap();
debug!("This one should be printed");
info!("This one should be printed");
config.update_filters("error");
debug!("This one should NOT be printed");
info!("This one should NOT be printed");
config.update_filters("info");
debug!("This one should NOT be printed");
info!("This one should be printed");
}
}