use log::{warn, LevelFilter, Record};
use log4rs::{
append::Append,
config::{Appender, Config, Logger, Root},
encode::{pattern::PatternEncoder, Encode},
};
use std::io::{self, Write};
use std::sync::Once;
use crate::string_buffer::StringBuffer;
#[derive(Debug)]
pub struct TestConsoleAppender {
encoder: Box<dyn Encode>,
}
impl TestConsoleAppender {
pub fn new(encoder: Box<dyn Encode>) -> Self {
Self { encoder }
}
pub fn make_config<'a, 'b>(
targets: impl IntoIterator<Item = &'a str>,
level: impl Into<Option<LevelFilter>>,
pattern: impl Into<Option<&'b str>>,
) -> Config {
let level = level.into().unwrap_or(LevelFilter::Trace);
let pattern = pattern.into().unwrap_or("{l} {M}::{L} {m}{n}");
const APPENDER_NAME: &str = "appender";
let encoder = Box::new(PatternEncoder::new(pattern));
let console = Box::new(TestConsoleAppender::new(encoder));
let appender = Appender::builder().build(APPENDER_NAME, console);
let mut loggers = Vec::new();
for target in targets {
let logger = Logger::builder()
.appender(APPENDER_NAME)
.build(target, level);
loggers.push(logger);
}
if loggers.is_empty() {
let root = Root::builder().appender(APPENDER_NAME).build(level);
Config::builder().appender(appender).build(root).unwrap()
} else {
let root = Root::builder().build(LevelFilter::Off);
Config::builder()
.appender(appender)
.loggers(loggers)
.build(root)
.unwrap()
}
}
}
impl Append for TestConsoleAppender {
fn append(&self, record: &Record) -> anyhow::Result<()> {
let mut log_line = StringBuffer::new();
self.encoder.encode(&mut log_line, record)?;
print!("{}", log_line.0);
io::stdout().flush()?;
Ok(())
}
fn flush(&self) {
}
}
static INIT: Once = Once::new();
pub fn init_logging_once(config: Config) {
INIT.call_once(|| {
let result = log4rs::init_config(config);
if result.is_err() {
warn!(
"init_logging_once tried to set the logger, but it had \
already been set by someone else!"
);
}
});
}
pub fn init_logging_once_for<'a, 'b>(
targets: impl IntoIterator<Item = &'a str>,
level: impl Into<Option<LevelFilter>>,
pattern: impl Into<Option<&'b str>>,
) {
if INIT.is_completed() {
return;
}
let config = TestConsoleAppender::make_config(targets, level, pattern);
init_logging_once(config);
}