use log::{LevelFilter, SetLoggerError};
use std::collections::HashMap;
use std::sync::Arc;
use crate::Target;
use crate::writer::create_writer;
use crate::backend::AsyncLogBackend;
use crate::logger::GrLogger;
pub struct LoggerBuilder {
target: Target,
level: LevelFilter,
module_levels: HashMap<String, LevelFilter>,
buffer_size: usize,
}
impl LoggerBuilder {
pub fn new() -> Self {
Self {
target: Target::default(),
level: LevelFilter::Info,
module_levels: HashMap::new(),
buffer_size: 1024,
}
}
pub fn from_env(env_var: &str) -> Self {
let mut builder = Self::new();
if let Ok(spec) = std::env::var(env_var) {
builder.parse_spec(&spec);
}
builder
}
pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
self.level = level;
self
}
pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
self.module_levels.insert(module.to_string(), level);
self
}
pub fn target(&mut self, target: Target) -> &mut Self {
self.target = target;
self
}
pub fn buffer_size(&mut self, size: usize) -> &mut Self {
self.buffer_size = size;
self
}
fn parse_spec(&mut self, spec: &str) {
for part in spec.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
if let Some((module, level)) = part.split_once('=') {
if let Some(level) = parse_level(level) {
self.filter_module(module, level);
}
} else if let Some(level) = parse_level(part) {
self.filter_level(level);
}
}
}
pub fn init(&mut self) -> Result<(), SetLoggerError> {
let writer = create_writer(&self.target);
let backend = Arc::new(AsyncLogBackend::new(writer, self.buffer_size));
let logger = GrLogger::new(
self.level,
self.module_levels.clone(),
backend,
);
log::set_boxed_logger(Box::new(logger))?;
log::set_max_level(self.level);
Ok(())
}
pub fn try_init(&mut self) {
let _ = self.init();
}
}
impl Default for LoggerBuilder {
fn default() -> Self {
Self::new()
}
}
fn parse_level(level: &str) -> Option<LevelFilter> {
match level.to_lowercase().as_str() {
"trace" => Some(LevelFilter::Trace),
"debug" => Some(LevelFilter::Debug),
"info" => Some(LevelFilter::Info),
"warn" => Some(LevelFilter::Warn),
"error" => Some(LevelFilter::Error),
"off" => Some(LevelFilter::Off),
_ => None,
}
}
pub fn init() {
LoggerBuilder::new().try_init();
}
pub fn init_from_env() {
LoggerBuilder::from_env("RUST_LOG").try_init();
}
pub fn builder() -> LoggerBuilder {
LoggerBuilder::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_level() {
assert_eq!(parse_level("trace"), Some(LevelFilter::Trace));
assert_eq!(parse_level("debug"), Some(LevelFilter::Debug));
assert_eq!(parse_level("info"), Some(LevelFilter::Info));
assert_eq!(parse_level("warn"), Some(LevelFilter::Warn));
assert_eq!(parse_level("error"), Some(LevelFilter::Error));
assert_eq!(parse_level("off"), Some(LevelFilter::Off));
assert_eq!(parse_level("invalid"), None);
assert_eq!(parse_level("INFO"), Some(LevelFilter::Info)); }
#[test]
fn test_builder_default() {
let builder = LoggerBuilder::new();
assert_eq!(builder.level, LevelFilter::Info);
assert_eq!(builder.buffer_size, 1024);
assert!(builder.module_levels.is_empty());
}
#[test]
fn test_builder_filter_level() {
let mut builder = LoggerBuilder::new();
builder.filter_level(LevelFilter::Debug);
assert_eq!(builder.level, LevelFilter::Debug);
}
#[test]
fn test_builder_filter_module() {
let mut builder = LoggerBuilder::new();
builder.filter_module("my_module", LevelFilter::Debug);
assert_eq!(builder.module_levels.get("my_module"), Some(&LevelFilter::Debug));
}
#[test]
fn test_builder_parse_spec() {
let mut builder = LoggerBuilder::new();
builder.parse_spec("info,my_module=debug,other=warn");
assert_eq!(builder.level, LevelFilter::Info);
assert_eq!(builder.module_levels.get("my_module"), Some(&LevelFilter::Debug));
assert_eq!(builder.module_levels.get("other"), Some(&LevelFilter::Warn));
}
#[test]
fn test_builder_from_env() {
std::env::set_var("GRLOG_TEST_LOG", "debug,my_module=trace");
let builder = LoggerBuilder::from_env("GRLOG_TEST_LOG");
assert_eq!(builder.level, LevelFilter::Debug);
assert_eq!(builder.module_levels.get("my_module"), Some(&LevelFilter::Trace));
std::env::remove_var("GRLOG_TEST_LOG");
}
#[test]
fn test_builder_functions() {
init(); init_from_env(); let _builder = builder();
}
}