diff --git a/examples/fn_layout_filter.rs b/examples/fn_layout_filter.rs index 06c266c..f8414d6 100644 --- a/examples/fn_layout_filter.rs +++ b/examples/fn_layout_filter.rs @@ -16,6 +16,7 @@ use log::LevelFilter; use logforth::append; use logforth::filter; use logforth::filter::FilterResult; +use logforth::layout; use logforth::logger::Dispatch; use logforth::logger::Logger; @@ -24,18 +25,15 @@ fn main() { .dispatch( Dispatch::new() .filter(filter::BoxDyn::new(|metadata: &log::Metadata| { - if metadata.level() <= LevelFilter::Info { + if metadata.level() > LevelFilter::Info { FilterResult::Accept } else { FilterResult::Reject } })) - // .layout(layout::BoxDyn::new(|record: &log::Record| { - // let args = format_args!("[box dyn] {}", record.args()); - // Ok(record.to_builder().args(args).build()) - // // ...or - // // anyhow::bail!("boom: {}", message) - // })) + .layout(layout::CustomLayout::new(|record, f| { + f(format_args!("[system alert] {}", record.args())) + })) .append(append::Stdout), ) .apply() diff --git a/src/append/boxdyn.rs b/src/append/boxdyn.rs index 9c990db..eef0eea 100644 --- a/src/append/boxdyn.rs +++ b/src/append/boxdyn.rs @@ -19,7 +19,7 @@ use log::Record; use crate::append::Append; use crate::append::AppendImpl; use crate::filter::FilterImpl; -use crate::layout::LayoutImpl; +use crate::layout::Layout; pub struct BoxDyn(Box); @@ -44,7 +44,7 @@ impl Append for BoxDyn { (*self.0).flush() } - fn default_layout(&self) -> LayoutImpl { + fn default_layout(&self) -> Layout { (*self.0).default_layout() } diff --git a/src/append/mod.rs b/src/append/mod.rs index 5985a4a..1713e78 100644 --- a/src/append/mod.rs +++ b/src/append/mod.rs @@ -15,7 +15,7 @@ use crate::dynlog::DynLog; use crate::filter::FilterImpl; use crate::layout; -use crate::layout::LayoutImpl; +use crate::layout::Layout; pub use boxdyn::*; #[cfg(feature = "fastrace")] pub use fastrace::*; @@ -39,8 +39,8 @@ pub trait Append { /// Default layout to use when [Dispatch][crate::logger::Dispatch] does not configure a /// preferred layout. - fn default_layout(&self) -> LayoutImpl { - LayoutImpl::Identical(layout::Identical) + fn default_layout(&self) -> Layout { + Layout::Identical(layout::Identical) } /// Default filters associated to this append. [log::Log] is mixed with @@ -89,7 +89,7 @@ impl Append for AppendImpl { } } - fn default_layout(&self) -> LayoutImpl { + fn default_layout(&self) -> Layout { match self { AppendImpl::BoxDyn(append) => append.default_layout(), AppendImpl::DynLog(append) => append.default_layout(), diff --git a/src/layout/boxdyn.rs b/src/layout/boxdyn.rs deleted file mode 100644 index d09353c..0000000 --- a/src/layout/boxdyn.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 tison -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fmt::Debug; - -use log::Record; - -use crate::layout::Layout; -use crate::layout::LayoutImpl; - -pub struct BoxDyn(Box); - -impl Debug for BoxDyn { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "BoxDynLayout {{ ... }}") - } -} - -impl BoxDyn { - pub fn new(layout: impl Layout + Send + Sync + 'static) -> Self { - Self(Box::new(layout)) - } -} - -impl Layout for BoxDyn { - fn format_record<'a>(&'_ self, record: &'a Record<'a>) -> anyhow::Result> { - (*self.0).format_record(record) - } -} - -impl From for LayoutImpl { - fn from(layout: BoxDyn) -> Self { - LayoutImpl::BoxDyn(layout) - } -} - -impl Fn(&Record<'a>) -> anyhow::Result>> Layout for T { - fn format_record<'a>(&'_ self, record: &'a Record<'a>) -> anyhow::Result> { - self(record) - } -} diff --git a/src/layout/custom.rs b/src/layout/custom.rs new file mode 100644 index 0000000..36eb57f --- /dev/null +++ b/src/layout/custom.rs @@ -0,0 +1,59 @@ +// Copyright 2024 tison +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::{Arguments, Debug, Formatter}; + +use crate::layout::Layout; + +pub struct CustomLayout { + #[allow(clippy::type_complexity)] + f: Box< + dyn Fn(&log::Record, &dyn Fn(Arguments) -> anyhow::Result<()>) -> anyhow::Result<()> + + Send + + Sync + + 'static, + >, +} + +impl Debug for CustomLayout { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "CustomLayout {{ ... }}") + } +} + +impl CustomLayout { + pub fn new( + layout: impl Fn(&log::Record, &dyn Fn(Arguments) -> anyhow::Result<()>) -> anyhow::Result<()> + + Send + + Sync + + 'static, + ) -> Self { + CustomLayout { + f: Box::new(layout), + } + } + + pub fn format(&self, record: &log::Record, f: &F) -> anyhow::Result<()> + where + F: Fn(Arguments) -> anyhow::Result<()>, + { + (self.f)(record, f) + } +} + +impl From for Layout { + fn from(layout: CustomLayout) -> Self { + Layout::Custom(layout) + } +} diff --git a/src/layout/identical.rs b/src/layout/identical.rs index f66c213..742b51a 100644 --- a/src/layout/identical.rs +++ b/src/layout/identical.rs @@ -1,20 +1,20 @@ use crate::layout::Layout; -use crate::layout::LayoutImpl; +use std::fmt::Arguments; #[derive(Debug, Default, Clone, Copy)] pub struct Identical; -impl Layout for Identical { - fn format(&self, record: &log::Record, f: F) -> anyhow::Result<()> +impl Identical { + pub fn format(&self, record: &log::Record, f: &F) -> anyhow::Result<()> where - F: Fn(&log::Record) -> anyhow::Result<()>, + F: Fn(Arguments) -> anyhow::Result<()>, { - f(record) + f(*record.args()) } } -impl From for LayoutImpl { +impl From for Layout { fn from(layout: Identical) -> Self { - LayoutImpl::Identical(layout) + Layout::Identical(layout) } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index bc3e4db..f40c2ef 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -12,46 +12,48 @@ // See the License for the specific language governing permissions and // limitations under the License. -// pub use boxdyn::BoxDyn; +pub use custom::CustomLayout; pub use identical::Identical; pub use kv_display::KvDisplay; #[cfg(feature = "json")] pub use simple_json::SimpleJson; pub use simple_text::SimpleText; -// mod boxdyn; +mod custom; mod identical; mod kv_display; #[cfg(feature = "json")] mod simple_json; mod simple_text; -pub trait Layout { - fn format(&self, record: &log::Record, f: F) -> anyhow::Result<()> - where - F: Fn(&log::Record) -> anyhow::Result<()>; -} - #[derive(Debug)] -pub enum LayoutImpl { - // BoxDyn(BoxDyn), +pub enum Layout { Identical(Identical), SimpleText(SimpleText), #[cfg(feature = "json")] SimpleJson(SimpleJson), + Custom(CustomLayout), } -impl Layout for LayoutImpl { - fn format(&self, record: &log::Record, f: F) -> anyhow::Result<()> +impl Layout { + pub fn format(&self, record: &log::Record, f: &F) -> anyhow::Result<()> where F: Fn(&log::Record) -> anyhow::Result<()>, { match self { - // LayoutImpl::BoxDyn(layout) => layout.format_record(record), - LayoutImpl::Identical(layout) => layout.format(record, f), - LayoutImpl::SimpleText(layout) => layout.format(record, f), + Layout::Identical(layout) => { + layout.format(record, &|args| f(&record.to_builder().args(args).build())) + } + Layout::SimpleText(layout) => { + layout.format(record, &|args| f(&record.to_builder().args(args).build())) + } #[cfg(feature = "json")] - LayoutImpl::SimpleJson(layout) => layout.format(record, f), + Layout::SimpleJson(layout) => { + layout.format(record, &|args| f(&record.to_builder().args(args).build())) + } + Layout::Custom(layout) => { + layout.format(record, &|args| f(&record.to_builder().args(args).build())) + } } } } diff --git a/src/layout/simple_json.rs b/src/layout/simple_json.rs index 069267a..5ee502c 100644 --- a/src/layout/simple_json.rs +++ b/src/layout/simple_json.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::fmt::Arguments; use std::path::Path; use std::time::SystemTime; @@ -22,7 +23,6 @@ use serde_json::Map; use serde_json::Value; use crate::layout::Layout; -use crate::layout::LayoutImpl; #[derive(Default, Debug, Clone)] pub struct SimpleJson; @@ -55,10 +55,10 @@ struct RecordLine<'a> { kvs: Map, } -impl Layout for SimpleJson { - fn format(&self, record: &Record, f: F) -> anyhow::Result<()> +impl SimpleJson { + pub fn format(&self, record: &Record, f: &F) -> anyhow::Result<()> where - F: Fn(&Record) -> anyhow::Result<()>, + F: Fn(Arguments) -> anyhow::Result<()>, { let mut kvs = Map::new(); let mut visitor = KvCollector { kvs: &mut kvs }; @@ -80,12 +80,12 @@ impl Layout for SimpleJson { }; let text = serde_json::to_string(&record_line)?; - f(&record.to_builder().args(format_args!("{text}",)).build()) + f(format_args!("{text}")) } } -impl From for LayoutImpl { +impl From for Layout { fn from(layout: SimpleJson) -> Self { - LayoutImpl::SimpleJson(layout) + Layout::SimpleJson(layout) } } diff --git a/src/layout/simple_text.rs b/src/layout/simple_text.rs index 3cdfa9c..5aa42d7 100644 --- a/src/layout/simple_text.rs +++ b/src/layout/simple_text.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::fmt::Arguments; use std::path::Path; use std::time::SystemTime; @@ -22,7 +23,6 @@ use log::Level; use crate::layout::kv_display::KvDisplay; use crate::layout::Layout; -use crate::layout::LayoutImpl; #[derive(Default, Debug, Clone)] pub struct SimpleText { @@ -50,10 +50,10 @@ impl Default for ColoredLevel { } } -impl Layout for SimpleText { - fn format(&self, record: &log::Record, f: F) -> anyhow::Result<()> +impl SimpleText { + pub fn format(&self, record: &log::Record, f: &F) -> anyhow::Result<()> where - F: Fn(&log::Record) -> anyhow::Result<()>, + F: Fn(Arguments) -> anyhow::Result<()>, { let color = match record.level() { Level::Error => self.colors.error, @@ -75,17 +75,14 @@ impl Layout for SimpleText { let message = record.args(); let kvs = KvDisplay::new(record.key_values()); - f(&record - .to_builder() - .args(format_args!( - "{time} {level:>5} {module}: {file}:{line} {message}{kvs}" - )) - .build()) + f(format_args!( + "{time} {level:>5} {module}: {file}:{line} {message}{kvs}" + )) } } -impl From for LayoutImpl { +impl From for Layout { fn from(layout: SimpleText) -> Self { - LayoutImpl::SimpleText(layout) + Layout::SimpleText(layout) } } diff --git a/src/logger.rs b/src/logger.rs index be17fee..44fa739 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -24,13 +24,12 @@ use crate::filter::Filter; use crate::filter::FilterImpl; use crate::filter::FilterResult; use crate::layout::Layout; -use crate::layout::LayoutImpl; #[derive(Debug)] pub struct Dispatch { filters: Vec, appends: Vec, - preferred_layout: Option, + preferred_layout: Option, } impl Default for Dispatch { @@ -58,7 +57,7 @@ impl Dispatch { self } - pub fn layout(mut self, layout: impl Into) -> Self { + pub fn layout(mut self, layout: impl Into) -> Self { self.preferred_layout = Some(layout.into()); self } @@ -87,7 +86,7 @@ impl Dispatch { fn do_append( record: &Record, append: &AppendImpl, - preferred_layout: Option<&LayoutImpl>, + preferred_layout: Option<&Layout>, ) -> anyhow::Result<()> { if let Some(filters) = append.default_filters() { for filter in filters { @@ -100,10 +99,10 @@ impl Dispatch { } match preferred_layout { - Some(layout) => layout.format(record, |record| append.try_append(record)), + Some(layout) => layout.format(record, &|record| append.try_append(record)), None => append .default_layout() - .format(record, |record| append.try_append(record)), + .format(record, &|record| append.try_append(record)), } }