From aee4d1066183341e165253e0913ec985ff3b38b3 Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 30 Oct 2024 17:39:24 +0800 Subject: [PATCH] refactor: use more comprehensive api (#69) Signed-off-by: tison --- .github/workflows/ci.yml | 2 +- Cargo.toml | 4 +- README.md | 15 +- ...ilter.rs => default_filter_respect_env.rs} | 8 +- examples/fn_layout_filter.rs | 7 +- examples/json_stdio.rs | 5 +- examples/rolling_file.rs | 13 +- examples/simple_stdio.rs | 11 +- src/lib.rs | 15 +- src/logger/builder.rs | 204 +++++++++--------- tests/recursive_logging.rs | 11 +- 11 files changed, 144 insertions(+), 151 deletions(-) rename examples/{env_filter.rs => default_filter_respect_env.rs} (80%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0670696..fed56d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: run: | cargo run --example simple_stdio cargo run --example fn_layout_filter - cargo run --example env_filter + cargo run --example default_filter_respect_env cargo run --features="no-color" --example simple_stdio cargo run --features="json" --example json_stdio cargo run --features="json,rolling_file" --example rolling_file diff --git a/Cargo.toml b/Cargo.toml index b7576a8..d49c2d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -114,5 +114,5 @@ name = "fn_layout_filter" path = "examples/fn_layout_filter.rs" [[example]] -name = "env_filter" -path = "examples/env_filter.rs" +name = "default_filter_respect_env" +path = "examples/default_filter_respect_env.rs" diff --git a/README.md b/README.md index afb3dc9..4e37a81 100644 --- a/README.md +++ b/README.md @@ -35,21 +35,20 @@ Then, you can use the logger with the simplest default setup: ```rust fn main() { - logforth::stderr().finish(); + logforth::stderr().apply(); } ``` Or configure the logger in a more fine-grained way: ```rust +use log::LevelFilter; +use logforth::append; + fn main() { - logforth::builder() - .filter(log::LevelFilter::Debug) - .append(logforth::append::Stderr::default()) - .dispatch() - .filter(log::LevelFilter::Info) - .append(logforth::append::Stdout::default()) - .finish(); + logforth::dispatch(|b| b.filter(LevelFilter::Debug).append(append::Stderr::default())) + .and_dispatch(|b| b.filter(LevelFilter::Info).append(append::Stdout::default())) + .apply(); } ``` diff --git a/examples/env_filter.rs b/examples/default_filter_respect_env.rs similarity index 80% rename from examples/env_filter.rs rename to examples/default_filter_respect_env.rs index e423481..d850e28 100644 --- a/examples/env_filter.rs +++ b/examples/default_filter_respect_env.rs @@ -12,14 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use logforth::append; -use logforth::filter::EnvFilter; - fn main() { - logforth::builder() - .filter(EnvFilter::from_default_env()) - .append(append::Stdout::default()) - .finish(); + logforth::stdout().apply(); log::error!("Hello error!"); log::warn!("Hello warn!"); diff --git a/examples/fn_layout_filter.rs b/examples/fn_layout_filter.rs index eae0c34..fe822f7 100644 --- a/examples/fn_layout_filter.rs +++ b/examples/fn_layout_filter.rs @@ -19,8 +19,8 @@ use logforth::filter::FilterResult; use logforth::layout::CustomLayout; fn main() { - logforth::builder() - .filter(CustomFilter::new(|metadata: &log::Metadata| { + logforth::dispatch(|b| { + b.filter(CustomFilter::new(|metadata: &log::Metadata| { if metadata.level() > LevelFilter::Info { FilterResult::Accept } else { @@ -32,7 +32,8 @@ fn main() { Ok(format!("[system alert] {}", record.args()).into_bytes()) })), ) - .finish(); + }) + .apply(); log::error!("Hello error!"); log::warn!("Hello warn!"); diff --git a/examples/json_stdio.rs b/examples/json_stdio.rs index c6e0760..b495ab5 100644 --- a/examples/json_stdio.rs +++ b/examples/json_stdio.rs @@ -16,9 +16,8 @@ use logforth::append; use logforth::layout::JsonLayout; fn main() { - logforth::builder() - .append(append::Stdout::default().with_layout(JsonLayout::default())) - .finish(); + logforth::dispatch(|b| b.append(append::Stdout::default().with_layout(JsonLayout::default()))) + .apply(); log::error!("Hello error!"); log::warn!("Hello warn!"); diff --git a/examples/rolling_file.rs b/examples/rolling_file.rs index 1b60424..8a116f0 100644 --- a/examples/rolling_file.rs +++ b/examples/rolling_file.rs @@ -30,13 +30,12 @@ fn main() { .unwrap(); let (writer, _guard) = NonBlockingBuilder::default().finish(rolling); - logforth::builder() - .filter("trace") - .append(RollingFile::new(writer).with_layout(JsonLayout::default())) - .dispatch() - .filter("info") - .append(Stdout::default()) - .finish(); + logforth::dispatch(|b| { + b.filter("trace") + .append(RollingFile::new(writer).with_layout(JsonLayout::default())) + }) + .and_dispatch(|b| b.filter("info").append(Stdout::default())) + .apply(); let repeat = 1; diff --git a/examples/simple_stdio.rs b/examples/simple_stdio.rs index 992fa9e..24129d1 100644 --- a/examples/simple_stdio.rs +++ b/examples/simple_stdio.rs @@ -16,11 +16,12 @@ use log::LevelFilter; use logforth::append; fn main() { - logforth::builder() - .filter(LevelFilter::Trace) - .append(append::Stdout::default()) - .append(append::Stderr::default()) - .finish(); + logforth::dispatch(|b| { + b.filter(LevelFilter::Trace) + .append(append::Stdout::default()) + .append(append::Stderr::default()) + }) + .apply(); log::error!("Hello error!"); log::warn!("Hello warn!"); diff --git a/src/lib.rs b/src/lib.rs index 8c2e1fc..f9fc384 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,19 +29,18 @@ cargo add logforth Then, you can use the logger with the simplest default setup: ```rust -logforth::stderr().finish(); +logforth::stderr().apply(); ``` Or configure the logger in a more fine-grained way: ```rust -logforth::builder() - .filter(log::LevelFilter::Debug) - .append(logforth::append::Stderr::default()) - .dispatch() - .filter(log::LevelFilter::Info) - .append(logforth::append::Stdout::default()) - .finish(); +use log::LevelFilter; +use logforth::append; + +logforth::dispatch(|b| b.filter(LevelFilter::Debug).append(append::Stderr::default())) + .and_dispatch(|b| b.filter(LevelFilter::Info).append(append::Stdout::default())) + .apply(); ``` Read more demos under the [examples](https://github.com/fast/logforth/tree/main/examples) directory. diff --git a/src/logger/builder.rs b/src/logger/builder.rs index 0711651..b69e450 100644 --- a/src/logger/builder.rs +++ b/src/logger/builder.rs @@ -17,23 +17,23 @@ use log::LevelFilter; use super::log_impl::Dispatch; use super::log_impl::Logger; use crate::append; +use crate::filter::EnvFilter; use crate::Append; use crate::Filter; /// Create a new empty [builder][Builder]. /// -/// The builder must be configured before initializing the global logger. At least one dispatch -/// should be added: +/// At least one dispatch would be added: /// /// ```rust /// use log::LevelFilter; /// use logforth::append; /// -/// logforth::builder() -/// // .finish() CANNOT COMPILE: a staging dispatch without Append -/// .filter(LevelFilter::Info) -/// .append(append::Stdout::default()) -/// .finish(); +/// logforth::dispatch(|b| { +/// b.filter(LevelFilter::Info) +/// .append(append::Stdout::default()) +/// }) +/// .apply(); /// ``` /// /// Multiple dispatches can be added: @@ -42,47 +42,54 @@ use crate::Filter; /// use log::LevelFilter; /// use logforth::append; /// -/// logforth::builder() -/// .filter(LevelFilter::Info) -/// .append(append::Stdout::default()) -/// .dispatch() // finish the current dispatch and start a new staging dispatch with no Append and Filter configured -/// .filter(LevelFilter::Debug) -/// .append(append::Stderr::default()) -/// .finish(); +/// logforth::dispatch(|b| { +/// b.filter(LevelFilter::Info) +/// .append(append::Stdout::default()) +/// }) +/// .and_dispatch(|b| { +/// b.filter(LevelFilter::Debug) +/// .append(append::Stderr::default()) +/// }) +/// .apply(); /// ``` -pub fn builder() -> Builder { - Builder::default() +pub fn dispatch(f: F) -> Builder +where + F: FnOnce(DispatchBuilder) -> DispatchBuilder, +{ + Builder::dispatch(f) } -/// Create a new [`Builder`] with a default `Stdout` append configured. +/// Create a new [`Builder`] with a default [`Stdout`][append::Stdout] append configured, and +/// respect the `RUST_LOG` environment variable for filtering logs. /// /// This is a convenient API that you can use as: /// /// ```rust -/// logforth::stdout().finish(); +/// logforth::stdout().apply(); /// ``` -pub fn stdout() -> Builder { - builder().append(append::Stdout::default()) +pub fn stdout() -> Builder { + dispatch(|b| { + b.filter(EnvFilter::from_default_env()) + .append(append::Stdout::default()) + }) } -/// Create a new [`Builder`] with a default `Stderr` append configured. +/// Create a new [`Builder`] with a default [`Stderr`][append::Stderr] append configured, and +/// respect the `RUST_LOG` environment variable for filtering logs. /// /// This is a convenient API that you can use as: /// /// ```rust -/// logforth::stderr().finish(); +/// logforth::stderr().apply(); /// ``` -pub fn stderr() -> Builder { - builder().append(append::Stdout::default()) +pub fn stderr() -> Builder { + dispatch(|b| { + b.filter(EnvFilter::from_default_env()) + .append(append::Stderr::default()) + }) } -/// A builder for configuring the logger. See also [`builder`] for a fluent API. -/// -/// * `READY=false`: The initialized state. You can configure [`Filter`]s and [`Append`]s for the -/// current staging dispatch. Once at least one append is configured, the builder transit to -/// `READY=true`. -/// * `READY=true`: The builder can be [finished][Builder::finish] to set up the global logger. Or, -/// you can start a new staging dispatch by calling [dispatch][Builder::dispatch]. +/// A builder for configuring the logger. Always constructed via [`dispatch`] for a fluent API. /// /// ## Examples /// @@ -92,20 +99,15 @@ pub fn stderr() -> Builder { /// use log::LevelFilter; /// use logforth::append; /// -/// logforth::Builder::new() -/// .filter(LevelFilter::Info) -/// .append(append::Stdout::default()) -/// .finish(); +/// logforth::dispatch(|b| { +/// b.filter(LevelFilter::Info) +/// .append(append::Stdout::default()) +/// }) +/// .apply(); /// ``` -// TODO(tisonkun): consider use an enum as const generic param once `adt_const_params` stabilized. -// @see https://doc.rust-lang.org/beta/unstable-book/language-features/adt-const-params.html -#[must_use = "call `dispatch` to add a dispatch to the logger and `finish` to set the global logger"] +#[must_use = "call `apply` to set the global logger"] #[derive(Debug)] -pub struct Builder { - // for current dispatch - filters: Vec, - appends: Vec>, - +pub struct Builder { // stashed dispatches dispatches: Vec, @@ -113,23 +115,25 @@ pub struct Builder { max_level: LevelFilter, } -impl Default for Builder { - fn default() -> Self { - Self::new() +impl Builder { + /// Create a new logger builder with the first dispatch configured by `f`. + fn dispatch(f: F) -> Self + where + F: FnOnce(DispatchBuilder) -> DispatchBuilder, + { + Self { + dispatches: vec![f(DispatchBuilder::new()).build()], + max_level: LevelFilter::Trace, + } } -} -impl Builder { - /// Add an [`Append`] to the under constructing `Dispatch`. - pub fn append(mut self, append: impl Append) -> Builder { - self.appends.push(Box::new(append)); - - Builder { - filters: self.filters, - appends: self.appends, - dispatches: self.dispatches, - max_level: self.max_level, - } + /// Stage a new dispatch. + pub fn and_dispatch(mut self, f: F) -> Self + where + F: FnOnce(DispatchBuilder) -> DispatchBuilder, + { + self.dispatches.push(f(DispatchBuilder::new()).build()); + self } /// Set the global maximum log level. @@ -139,39 +143,6 @@ impl Builder { self.max_level = max_level; self } -} - -impl Builder { - /// Create a new empty [`Builder`]. - pub fn new() -> Self { - Self { - filters: vec![], - appends: vec![], - dispatches: vec![], - max_level: LevelFilter::Trace, - } - } - - /// Add a [`Filter`] to the under constructing `Dispatch`. - pub fn filter(mut self, filter: impl Into) -> Builder { - self.filters.push(filter.into()); - self - } -} - -impl Builder { - /// Construct a new `Dispatch` with the configured [`Filter`]s and [`Append`]s. - pub fn dispatch(mut self) -> Builder { - let dispatch = Dispatch::new(self.filters, self.appends); - self.dispatches.push(dispatch); - - Builder { - filters: vec![], - appends: vec![], - dispatches: self.dispatches, - max_level: self.max_level, - } - } /// Set up the global logger with all the dispatches configured. /// @@ -182,12 +153,7 @@ impl Builder { /// /// This function will fail if it is called more than once, or if another library has already /// initialized a global logger. - pub fn try_finish(mut self) -> Result<(), log::SetLoggerError> { - // finish the current staging dispatch - let dispatch = Dispatch::new(self.filters, self.appends); - self.dispatches.push(dispatch); - - // set up the global logger + pub fn try_apply(self) -> Result<(), log::SetLoggerError> { let logger = Logger::new(self.dispatches); log::set_boxed_logger(Box::new(logger))?; log::set_max_level(self.max_level); @@ -203,8 +169,46 @@ impl Builder { /// /// This function will panic if it is called more than once, or if another library has already /// initialized a global logger. - pub fn finish(self) { - self.try_finish() - .expect("Builder::finish should not be called after the global logger initialized"); + pub fn apply(self) { + self.try_apply() + .expect("Builder::apply should not be called after the global logger initialized"); + } +} + +#[derive(Debug)] +pub struct DispatchBuilder { + filters: Vec, + appends: Vec>, +} + +impl DispatchBuilder { + fn new() -> Self { + DispatchBuilder { + filters: vec![], + appends: vec![], + } + } +} + +impl DispatchBuilder { + fn build(self) -> Dispatch { + Dispatch::new(self.filters, self.appends) + } +} + +impl DispatchBuilder { + /// Add a [`Filter`] to the under constructing `Dispatch`. + pub fn filter(mut self, filter: impl Into) -> Self { + self.filters.push(filter.into()); + self + } + + /// Add an [`Append`] to the under constructing `Dispatch`. + pub fn append(mut self, append: impl Append) -> DispatchBuilder { + self.appends.push(Box::new(append)); + DispatchBuilder { + filters: self.filters, + appends: self.appends, + } } } diff --git a/tests/recursive_logging.rs b/tests/recursive_logging.rs index f9bc2a3..3ea5632 100644 --- a/tests/recursive_logging.rs +++ b/tests/recursive_logging.rs @@ -37,13 +37,10 @@ fn test_meta_logging_in_format_works() { }) }; - logforth::builder() - .append(append::Stdout::default().with_layout(layout("out"))) - .dispatch() - .append(append::Stderr::default().with_layout(layout("err"))) - .dispatch() - .append(append::RollingFile::new(writer).with_layout(layout("file"))) - .finish(); + logforth::dispatch(|b| b.append(append::Stdout::default().with_layout(layout("out")))) + .and_dispatch(|b| b.append(append::Stderr::default().with_layout(layout("err")))) + .and_dispatch(|b| b.append(append::RollingFile::new(writer).with_layout(layout("file")))) + .apply(); struct Thing<'a>(&'a str);