use log::LevelFilter;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ModuleFilters {
filters: HashMap<String, LevelFilter>,
default_level: LevelFilter,
}
impl Default for ModuleFilters {
fn default() -> Self {
Self {
filters: HashMap::new(),
default_level: LevelFilter::Info,
}
}
}
impl ModuleFilters {
pub fn new(default_level: LevelFilter) -> Self {
Self {
filters: HashMap::new(),
default_level,
}
}
pub fn from_env() -> Self {
let rust_log = std::env::var("RUST_LOG").unwrap_or_default();
Self::parse(&rust_log)
}
pub fn parse(s: &str) -> Self {
let mut filters = HashMap::new();
let mut default_level = LevelFilter::Info;
if s.is_empty() {
return Self {
filters,
default_level,
};
}
for part in s.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
if let Some((module, level_str)) = part.split_once('=') {
let module = module.trim();
let level_str = level_str.trim();
if let Some(level) = parse_level_filter(level_str) {
filters.insert(module.to_string(), level);
}
} else {
if let Some(level) = parse_level_filter(part) {
default_level = level;
}
}
}
Self {
filters,
default_level,
}
}
pub fn add_filter(&mut self, module: impl Into<String>, level: LevelFilter) {
self.filters.insert(module.into(), level);
}
pub fn level_for(&self, module_path: &str) -> LevelFilter {
if let Some(&level) = self.filters.get(module_path) {
return level;
}
let mut matching_filters: Vec<_> = self
.filters
.iter()
.filter(|(path, _)| module_path.starts_with(path.as_str()))
.collect();
matching_filters.sort_by_key(|(path, _)| std::cmp::Reverse(path.len()));
if let Some(&(_, &level)) = matching_filters.first() {
return level;
}
self.default_level
}
pub fn default_level(&self) -> LevelFilter {
self.default_level
}
pub fn set_default_level(&mut self, level: LevelFilter) {
self.default_level = level;
}
}
fn parse_level_filter(s: &str) -> Option<LevelFilter> {
match s.to_lowercase().as_str() {
"off" => Some(LevelFilter::Off),
"error" => Some(LevelFilter::Error),
"warn" => Some(LevelFilter::Warn),
"info" => Some(LevelFilter::Info),
"debug" => Some(LevelFilter::Debug),
"trace" => Some(LevelFilter::Trace),
_ => None,
}
}
fn parse_log_level_env() -> Option<LevelFilter> {
let level_str = std::env::var("LOG_LEVEL").ok()?;
let level = level_str.parse::<u8>().ok()?;
Some(match level {
0 => LevelFilter::Off,
1 => LevelFilter::Error,
2 => LevelFilter::Warn,
3 => LevelFilter::Info,
4 => LevelFilter::Debug,
_ => LevelFilter::Trace,
})
}
#[derive(Debug, Clone)]
pub struct LoggerConfig {
pub show_file_info: bool,
pub show_date_in_stdout: bool,
pub use_colors: bool,
pub level: LevelFilter,
pub module_filters: ModuleFilters,
}
impl Default for LoggerConfig {
fn default() -> Self {
Self {
show_file_info: true,
show_date_in_stdout: false,
use_colors: true,
level: LevelFilter::Info,
module_filters: ModuleFilters::new(LevelFilter::Info),
}
}
}
impl LoggerConfig {
pub fn new() -> Self {
Self::default()
}
pub fn builder() -> LoggerConfigBuilder {
LoggerConfigBuilder::default()
}
pub fn from_env() -> Self {
let module_filters = ModuleFilters::from_env();
let level = if let Some(log_level) = parse_log_level_env() {
log_level
} else {
module_filters.default_level()
};
Self {
show_file_info: true,
show_date_in_stdout: false,
use_colors: true,
level,
module_filters,
}
}
pub fn production() -> Self {
Self {
show_file_info: false,
show_date_in_stdout: false,
use_colors: true,
level: LevelFilter::Info,
module_filters: ModuleFilters::new(LevelFilter::Info),
}
}
pub fn development() -> Self {
Self {
show_file_info: true,
show_date_in_stdout: false,
use_colors: true,
level: LevelFilter::Debug,
module_filters: ModuleFilters::new(LevelFilter::Debug),
}
}
}
#[derive(Debug, Default)]
pub struct LoggerConfigBuilder {
config: LoggerConfig,
}
impl LoggerConfigBuilder {
pub fn show_file_info(mut self, show: bool) -> Self {
self.config.show_file_info = show;
self
}
pub fn show_date_in_stdout(mut self, show: bool) -> Self {
self.config.show_date_in_stdout = show;
self
}
pub fn use_colors(mut self, use_colors: bool) -> Self {
self.config.use_colors = use_colors;
self
}
pub fn level(mut self, level: LevelFilter) -> Self {
self.config.level = level;
self.config.module_filters.set_default_level(level);
self
}
pub fn module_filters(mut self, filters: ModuleFilters) -> Self {
self.config.module_filters = filters;
self
}
pub fn filter_module(mut self, module: impl Into<String>, level: LevelFilter) -> Self {
self.config.module_filters.add_filter(module, level);
self
}
pub fn build(self) -> LoggerConfig {
self.config
}
}