use log::{Level, LevelFilter, Log};
use std::sync::OnceLock;
static LOGGER: OnceLock<Logger> = OnceLock::new();
pub struct Logger {
pub max_level: LevelFilter,
}
impl Logger {
pub fn new() -> Self {
Self {
max_level: LevelFilter::Info,
}
}
}
impl Default for Logger {
fn default() -> Self {
Self::new()
}
}
impl Log for Logger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level().to_level_filter() <= self.max_level
}
fn log(&self, record: &log::Record) {
if !self.enabled(record.metadata()) {
return;
}
let level_string = match record.level() {
Level::Error => "ERROR",
Level::Warn => "WARN",
Level::Info => "INFO",
Level::Debug => "DEBUG",
Level::Trace => "TRACE",
};
let target_string = match (record.file(), record.line()) {
(Some(file), Some(line)) => format!(" [{} {file}:{line}]", record.target()),
_ => "".to_owned(),
};
let msg = format!("[{level_string:5}]{} {}", target_string, record.args());
eprintln!("{msg}");
}
fn flush(&self) {}
}
pub fn init(logger: Logger) {
if LOGGER.get().is_some() {
return;
}
let l = LOGGER.get_or_init(|| {
log::set_max_level(logger.max_level);
logger
});
log::set_logger(l).unwrap();
}
pub struct Builder {
logger: Logger,
}
impl Builder {
pub fn new() -> Self {
Self {
logger: Logger::new(),
}
}
pub fn build(self) -> Logger {
self.logger
}
pub fn max_level(mut self, max_level: LevelFilter) -> Self {
self.logger.max_level = max_level;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic() {
init(Builder::new().max_level(LevelFilter::Trace).build());
log::logger();
log::info!("hello world");
log::warn!("this is cool");
log::error!("this is an error");
log::trace!("trace");
}
}