#![doc(html_root_url = "https://docs.rs/print_logger/")]
use log::{Level, Log, Metadata, Record};
use regex::Regex;
use std::{
fmt::{self, Debug},
io::Write,
};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
pub use log::LevelFilter;
pub struct PrintLogger {
level_filter: LevelFilter,
targets_by_name: Option<Vec<String>>,
targets_by_regex: Option<Vec<Regex>>,
print_targets_filter: LevelFilter,
}
impl Debug for PrintLogger {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut builder = f.debug_struct("PrintLogger");
builder.field("level_filter", &self.level_filter);
builder
.field("targets_by_name", &self.targets_by_name)
.field("targets_by_regex", &self.targets_by_regex)
.field("print_targets_filter", &self.print_targets_filter)
.finish()
}
}
impl Clone for PrintLogger {
fn clone(&self) -> PrintLogger {
PrintLogger {
targets_by_name: self.targets_by_name.clone(),
targets_by_regex: self.targets_by_regex.clone(),
..*self
}
}
}
impl Default for PrintLogger {
fn default() -> Self {
Self::new()
}
}
impl Log for PrintLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
if metadata.level() > self.level_filter {
return false;
};
if let Some(targets_by_name) = &self.targets_by_name {
return targets_by_name
.binary_search(&metadata.target().to_string())
.is_ok();
};
if let Some(targets_by_regex) = &self.targets_by_regex {
return targets_by_regex
.iter()
.any(|r| r.is_match(metadata.target()));
};
self.targets_by_name.is_none() || self.targets_by_regex.is_none()
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let text = format!(
"{}{}",
if self.print_targets_filter != LevelFilter::Off
&& self.level_filter >= self.print_targets_filter
{
format!("[{}] ", record.metadata().target())
} else {
"".to_string()
},
record.args()
);
let mut out = match record.metadata().level() {
Level::Error => StandardStream::stderr(ColorChoice::Auto),
_ => StandardStream::stdout(ColorChoice::Auto),
};
let mut spec = ColorSpec::new();
out.set_color(match record.metadata().level() {
Level::Error => spec.set_fg(Some(Color::Red)).set_bold(true),
Level::Warn => spec.set_fg(Some(Color::Yellow)).set_bold(true),
Level::Info => spec.set_fg(Some(Color::White)).set_bold(true),
Level::Debug => spec.set_fg(Some(Color::White)),
Level::Trace => spec.set_fg(Some(Color::Cyan)),
})
.unwrap_or_else(|_| panic!("Cannot set text color"));
_ = writeln!(&mut out, "{}", text);
}
fn flush(&self) {}
}
impl PrintLogger {
fn new() -> PrintLogger {
PrintLogger {
level_filter: LevelFilter::Error,
targets_by_name: None,
targets_by_regex: None,
print_targets_filter: LevelFilter::Off,
}
}
pub fn init(&mut self) -> Result<(), log::SetLoggerError> {
log::set_max_level(self.level_filter);
log::set_boxed_logger(Box::new(self.clone()))
}
pub fn level_filter<L>(&mut self, level_filter: L) -> &mut PrintLogger
where
L: Into<LevelFilter>,
{
self.level_filter = level_filter.into();
self
}
pub fn print_targets_filter<L>(&mut self, print_targets_filter: L) -> &mut PrintLogger
where
L: Into<LevelFilter>,
{
self.print_targets_filter = print_targets_filter.into();
self
}
pub fn targets_by_name(&mut self, names: &[String]) -> &mut PrintLogger {
if names.is_empty() {
return self;
}
if self.targets_by_name.is_none() {
self.targets_by_name = Some(Vec::new());
}
let targets_by_name = self.targets_by_name.as_mut().unwrap();
for n in names {
if let Err(i) = targets_by_name.binary_search(n) {
targets_by_name.insert(i, n.to_string());
}
}
self
}
pub fn targets_by_regex(&mut self, rs: &[Regex]) -> &mut PrintLogger {
if rs.is_empty() {
return self;
}
if self.targets_by_regex.is_none() {
self.targets_by_regex = Some(Vec::new());
}
self.targets_by_regex
.as_mut()
.unwrap()
.extend_from_slice(rs);
self
}
}
pub fn new() -> PrintLogger {
PrintLogger::new()
}