Merge pull request #2 from lucodear/feat/logger-overhaul
feat(logger): ✨ add: context ignoring + builder
This commit is contained in:
commit
a9dc3faf4c
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -21,5 +21,8 @@
|
||||
".gitignore": true,
|
||||
".vscode": true,
|
||||
".git": true,
|
||||
},
|
||||
"[rust]": {
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,18 @@
|
||||
use eyre::Result;
|
||||
use lool::logger::{debug, error, info, trace, warn, ConsoleLogger, Level};
|
||||
|
||||
fn main() {
|
||||
ConsoleLogger::default_setup(Level::Trace, "test").unwrap();
|
||||
fn main() -> Result<()> {
|
||||
ConsoleLogger::builder()
|
||||
.with_name("test")
|
||||
.with_level(Level::Trace)
|
||||
.ignore("examples/logger.rs")
|
||||
.install()?;
|
||||
|
||||
info!("log line");
|
||||
warn!("log line");
|
||||
error!("log line");
|
||||
debug!("log line");
|
||||
trace!("log line");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -14,7 +14,10 @@ struct StyledRecord {
|
||||
time: String,
|
||||
level: String,
|
||||
message: String,
|
||||
file: String,
|
||||
/// relative path if local file or <crate_name>::<..>::<mod_name> if
|
||||
/// it's a record created by a dependency
|
||||
ctx: String,
|
||||
raw_ctx: String,
|
||||
line: String,
|
||||
}
|
||||
|
||||
@ -51,7 +54,8 @@ impl StyledRecord {
|
||||
Self {
|
||||
level: format!("{}{:<5}{}", ansi_style_level, record.level(), RESET),
|
||||
message: format!("{}", record.args()),
|
||||
file: format!("{}{}{}", file_ansi_color, file.replace('\\', "/"), RESET),
|
||||
ctx: format!("{}{}{}", file_ansi_color, file.replace('\\', "/"), RESET),
|
||||
raw_ctx: file.replace('\\', "/"),
|
||||
line,
|
||||
time,
|
||||
}
|
||||
@ -63,23 +67,25 @@ impl StyledRecord {
|
||||
///
|
||||
/// this is a simple logger implementation (mounted on top of the `log` crate) that logs to stdout
|
||||
/// with ANSI colors and datetime stamps.
|
||||
pub struct ConsoleLogger<'a> {
|
||||
context: &'a str,
|
||||
pub struct ConsoleLogger {
|
||||
name: String,
|
||||
time_fn: fn() -> String,
|
||||
ignored_ctx: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> ConsoleLogger<'a> {
|
||||
impl ConsoleLogger {
|
||||
/// **🧉 » sets up the logger with the default settings**
|
||||
///
|
||||
/// sets the logger to use the `datetime::utc_current_time` function to get the current time.
|
||||
pub fn default_setup(max_level: Level, context: &'static str) -> Result<()> {
|
||||
pub fn default_setup<S: AsRef<str>>(max_level: Level, context: S) -> Result<()> {
|
||||
let logger = Box::new(ConsoleLogger {
|
||||
context,
|
||||
name: context.as_ref().to_string(),
|
||||
time_fn: datetime::utc_current_time,
|
||||
ignored_ctx: vec![],
|
||||
});
|
||||
log::set_logger(Box::leak(logger) as &'static ConsoleLogger)
|
||||
log::set_logger(Box::leak(logger) as &'static dyn Log)
|
||||
.map(|()| log::set_max_level(max_level.to_level_filter()))
|
||||
.map_err(|err| eyre!("failed to set logger: {}", err))
|
||||
.map_err(move |err| eyre!("failed to set logger: {}", err))
|
||||
}
|
||||
|
||||
/// **🧉 » sets up the logger with the given settings**
|
||||
@ -88,42 +94,68 @@ impl<'a> ConsoleLogger<'a> {
|
||||
/// current time.
|
||||
///
|
||||
/// the default `time_fn` is `datetime::utc_current_time` and it doesn't take TZ into account.
|
||||
pub fn setup(max_level: Level, context: &'static str, time_fn: fn() -> String) -> Result<()> {
|
||||
let logger = Box::new(ConsoleLogger { context, time_fn });
|
||||
log::set_logger(Box::leak(logger) as &'static ConsoleLogger)
|
||||
pub fn setup<S: AsRef<str>>(
|
||||
max_level: Level,
|
||||
context: S,
|
||||
time_fn: fn() -> String,
|
||||
) -> Result<()> {
|
||||
let logger = Box::new(ConsoleLogger {
|
||||
name: context.as_ref().to_string(),
|
||||
time_fn,
|
||||
ignored_ctx: vec![],
|
||||
});
|
||||
log::set_logger(Box::leak(logger) as &'static dyn Log)
|
||||
.map(|()| log::set_max_level(max_level.to_level_filter()))
|
||||
.map_err(|err| eyre!("failed to set logger: {}", err))
|
||||
.map_err(move |err| eyre!("failed to set logger: {}", err))
|
||||
}
|
||||
|
||||
/// 🧉 » sets the logger's context
|
||||
pub fn set_context(&mut self, context: &'a str) {
|
||||
self.context = context;
|
||||
/// **🧉 » creates a new setup builder**
|
||||
///
|
||||
/// this is the preferred way to set up the logger as it allows for more flexibility.
|
||||
///
|
||||
/// See the `SetupBuilder` struct for more information.
|
||||
pub fn builder() -> SetupBuilder {
|
||||
SetupBuilder::default()
|
||||
}
|
||||
|
||||
/// returns true if the context should be ignored
|
||||
fn should_ignore(&self, ctx: &String) -> bool {
|
||||
self.ignored_ctx.contains(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/// simple implementation of a themed logger
|
||||
impl<'a> log::Log for ConsoleLogger<'a> {
|
||||
impl Log for ConsoleLogger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.level() <= Level::Info
|
||||
}
|
||||
|
||||
/// log the record
|
||||
fn log(&self, record: &Record) {
|
||||
let styled_record = StyledRecord::from(record, self.time_fn);
|
||||
|
||||
// ignore the record if the context is in the ignore list
|
||||
if self.should_ignore(&styled_record.raw_ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
let time = if styled_record.time.is_empty() {
|
||||
" ".to_string()
|
||||
} else {
|
||||
format!(" {} ", styled_record.time)
|
||||
};
|
||||
|
||||
let ctx = if self.name.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!("{} »", self.name)
|
||||
};
|
||||
|
||||
// print to stdout
|
||||
println!(
|
||||
"[{}]{}| {} | {}:{} - {}",
|
||||
self.context,
|
||||
"{}{}| {} | {}:{} - {}",
|
||||
ctx,
|
||||
time,
|
||||
styled_record.level,
|
||||
styled_record.file,
|
||||
styled_record.ctx,
|
||||
styled_record.line,
|
||||
styled_record.message
|
||||
);
|
||||
@ -131,3 +163,107 @@ impl<'a> log::Log for ConsoleLogger<'a> {
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
/// **🧉 » setup builder**
|
||||
///
|
||||
/// this struct is used to set up the logger with more flexibility.
|
||||
///
|
||||
/// It allows for setting the logger name, the time function, the ignored contexts, and the max
|
||||
/// level.
|
||||
///
|
||||
/// The `install` method is used to build and install the logger. Should be called at the end of the
|
||||
/// builder chain.
|
||||
///
|
||||
/// **Example**
|
||||
///
|
||||
/// ```rust
|
||||
/// use eyre::Result;
|
||||
/// use lool::logger::{ConsoleLogger, Level, info};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// ConsoleLogger::builder()
|
||||
/// .with_name("test")
|
||||
/// .with_level(Level::Trace)
|
||||
/// .ignore("crate::module::function") // ignore a specific context
|
||||
/// .install()?;
|
||||
///
|
||||
/// info!("log line");
|
||||
/// }
|
||||
/// ```
|
||||
pub struct SetupBuilder {
|
||||
name: Option<String>,
|
||||
time_fn: Option<fn() -> String>,
|
||||
ignored_ctx: Option<Vec<String>>,
|
||||
max_level: Option<Level>,
|
||||
}
|
||||
|
||||
impl Default for SetupBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: Some("".to_string()),
|
||||
time_fn: Some(datetime::utc_current_time),
|
||||
ignored_ctx: Some(vec![]),
|
||||
max_level: Some(Level::Info),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SetupBuilder {
|
||||
/// **🧉 » `with_name`**
|
||||
///
|
||||
/// Sets the logger name
|
||||
pub fn with_name<S: AsRef<str>>(mut self, name: S) -> Self {
|
||||
self.name = Some(name.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// **🧉 » `with_level`**
|
||||
///
|
||||
/// Sets the max log level
|
||||
pub fn with_level(mut self, level: Level) -> Self {
|
||||
self.max_level = Some(level);
|
||||
self
|
||||
}
|
||||
|
||||
/// **🧉 » `with_time_fn`**
|
||||
///
|
||||
/// Sets the time function that will be used to get the current time
|
||||
pub fn with_time_fn(mut self, time_fn: fn() -> String) -> Self {
|
||||
self.time_fn = Some(time_fn);
|
||||
self
|
||||
}
|
||||
|
||||
/// **🧉 » `ignore_all`**
|
||||
///
|
||||
/// Sets the ignored contexts from a list of strings
|
||||
pub fn ignore_all(mut self, ignored_ctx: Vec<String>) -> Self {
|
||||
self.ignored_ctx = Some(ignored_ctx);
|
||||
self
|
||||
}
|
||||
|
||||
/// **🧉 » `ignore`**
|
||||
///
|
||||
/// Adds a context to the ignored list.
|
||||
///
|
||||
/// Unlike `ignore_all`, this method allows for adding a single ignored context at a time.
|
||||
pub fn ignore<Str: AsRef<str>>(mut self, ctx: Str) -> Self {
|
||||
let mut ignored_ctx = self.ignored_ctx.unwrap();
|
||||
ignored_ctx.push(ctx.as_ref().to_string());
|
||||
self.ignored_ctx = Some(ignored_ctx);
|
||||
self
|
||||
}
|
||||
|
||||
/// **🧉 » `install`**
|
||||
///
|
||||
/// Builds and installs the logger.
|
||||
pub fn install(self) -> Result<()> {
|
||||
let logger = Box::new(ConsoleLogger {
|
||||
name: self.name.unwrap_or("".to_string()),
|
||||
time_fn: self.time_fn.unwrap_or(datetime::utc_current_time),
|
||||
ignored_ctx: self.ignored_ctx.unwrap_or(vec![]),
|
||||
});
|
||||
log::set_logger(Box::leak(logger) as &'static dyn Log)
|
||||
.map(|()| log::set_max_level(self.max_level.unwrap().to_level_filter()))
|
||||
.map_err(|err| eyre!("failed to set logger: {}", err))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user