From 1e955bc005dd6130d288f37ea6278e40da33e88a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 12:25:59 -0500 Subject: [PATCH 1/8] refactor(fmt): Open space for DefaultFormat name --- src/fmt/mod.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index e608a5c..6ac3a3f 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -238,7 +238,7 @@ impl Builder { fmt } else { Box::new(move |buf, record| { - let fmt = DefaultFormat { + let fmt = DefaultFormatWriter { timestamp: built.format_timestamp, module_path: built.format_module_path, target: built.format_target, @@ -310,7 +310,7 @@ type StyledValue = T; /// The default format. /// /// This format needs to work with any combination of crate features. -struct DefaultFormat<'a> { +struct DefaultFormatWriter<'a> { timestamp: Option, module_path: bool, target: bool, @@ -325,7 +325,7 @@ struct DefaultFormat<'a> { kv_format: &'a KvFormatFn, } -impl DefaultFormat<'_> { +impl DefaultFormatWriter<'_> { fn write(mut self, record: &Record<'_>) -> io::Result<()> { self.write_timestamp()?; self.write_level(record)?; @@ -475,7 +475,7 @@ impl DefaultFormat<'_> { // Create a wrapper around the buffer only if we have to actually indent the message struct IndentWrapper<'a, 'b> { - fmt: &'a mut DefaultFormat<'b>, + fmt: &'a mut DefaultFormatWriter<'b>, indent_count: usize, } @@ -531,7 +531,7 @@ mod tests { use log::{Level, Record}; - fn write_record(record: Record<'_>, fmt: DefaultFormat<'_>) -> String { + fn write_record(record: Record<'_>, fmt: DefaultFormatWriter<'_>) -> String { let buf = fmt.buf.buf.clone(); fmt.write(&record).expect("failed to write record"); @@ -540,7 +540,7 @@ mod tests { String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record") } - fn write_target(target: &str, fmt: DefaultFormat<'_>) -> String { + fn write_target(target: &str, fmt: DefaultFormatWriter<'_>) -> String { write_record( Record::builder() .args(format_args!("log\nmessage")) @@ -554,7 +554,7 @@ mod tests { ) } - fn write(fmt: DefaultFormat<'_>) -> String { + fn write(fmt: DefaultFormatWriter<'_>) -> String { write_target("", fmt) } @@ -570,7 +570,7 @@ mod tests { fn format_with_header() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: true, target: false, @@ -592,7 +592,7 @@ mod tests { fn format_no_header() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: false, target: false, @@ -614,7 +614,7 @@ mod tests { fn format_indent_spaces() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: true, target: false, @@ -636,7 +636,7 @@ mod tests { fn format_indent_zero_spaces() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: true, target: false, @@ -658,7 +658,7 @@ mod tests { fn format_indent_spaces_no_header() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: false, target: false, @@ -680,7 +680,7 @@ mod tests { fn format_suffix() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: false, target: false, @@ -702,7 +702,7 @@ mod tests { fn format_suffix_with_indent() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: false, target: false, @@ -726,7 +726,7 @@ mod tests { let written = write_target( "target", - DefaultFormat { + DefaultFormatWriter { timestamp: None, module_path: true, target: true, @@ -749,7 +749,7 @@ mod tests { fn format_empty_target() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: true, target: true, @@ -773,7 +773,7 @@ mod tests { let written = write_target( "target", - DefaultFormat { + DefaultFormatWriter { timestamp: None, module_path: true, target: false, @@ -796,7 +796,7 @@ mod tests { fn format_with_source_file_and_line_number() { let mut f = formatter(); - let written = write(DefaultFormat { + let written = write(DefaultFormatWriter { timestamp: None, module_path: false, target: false, @@ -828,7 +828,7 @@ mod tests { let written = write_record( record, - DefaultFormat { + DefaultFormatWriter { timestamp: None, module_path: false, target: false, @@ -863,7 +863,7 @@ mod tests { let written = write_record( record, - DefaultFormat { + DefaultFormatWriter { timestamp: None, module_path: true, target: true, From 6169a5a4628257d75c79931273bb46a2fc39e409 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 12:31:40 -0500 Subject: [PATCH 2/8] refactor(fmt): Pull out DefaultFormat --- src/fmt/mod.rs | 86 ++++++++++++++++++++++++++++---------------------- src/logger.rs | 18 +++++------ 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 6ac3a3f..cc23da6 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -202,18 +202,10 @@ impl fmt::Debug for Formatter { pub(crate) type FormatFn = Box) -> io::Result<()> + Sync + Send>; +#[derive(Default)] pub(crate) struct Builder { - pub(crate) format_timestamp: Option, - pub(crate) format_module_path: bool, - pub(crate) format_target: bool, - pub(crate) format_level: bool, - pub(crate) format_indent: Option, + pub(crate) default_format: DefaultFormat, pub(crate) custom_format: Option, - pub(crate) format_suffix: &'static str, - pub(crate) format_file: bool, - pub(crate) format_line_number: bool, - #[cfg(feature = "kv")] - pub(crate) kv_format: Option>, built: bool, } @@ -239,17 +231,21 @@ impl Builder { } else { Box::new(move |buf, record| { let fmt = DefaultFormatWriter { - timestamp: built.format_timestamp, - module_path: built.format_module_path, - target: built.format_target, - level: built.format_level, + timestamp: built.default_format.timestamp, + module_path: built.default_format.module_path, + target: built.default_format.target, + level: built.default_format.level, written_header_value: false, - indent: built.format_indent, - suffix: built.format_suffix, - source_file: built.format_file, - source_line_number: built.format_line_number, + indent: built.default_format.indent, + suffix: built.default_format.suffix, + source_file: built.default_format.source_file, + source_line_number: built.default_format.source_line_number, #[cfg(feature = "kv")] - kv_format: built.kv_format.as_deref().unwrap_or(&default_kv_format), + kv_format: built + .default_format + .kv_format + .as_deref() + .unwrap_or(&default_kv_format), buf, }; @@ -259,25 +255,6 @@ impl Builder { } } -impl Default for Builder { - fn default() -> Self { - Builder { - format_timestamp: Some(Default::default()), - format_module_path: false, - format_target: true, - format_level: true, - format_file: false, - format_line_number: false, - format_indent: Some(4), - custom_format: None, - format_suffix: "\n", - #[cfg(feature = "kv")] - kv_format: None, - built: false, - } - } -} - #[cfg(feature = "color")] type SubtleStyle = StyledValue<&'static str>; #[cfg(not(feature = "color"))] @@ -307,6 +284,39 @@ impl Display for StyledValue { #[cfg(not(feature = "color"))] type StyledValue = T; +/// The default format. +/// +/// This format needs to work with any combination of crate features. +pub(crate) struct DefaultFormat { + pub(crate) timestamp: Option, + pub(crate) module_path: bool, + pub(crate) target: bool, + pub(crate) level: bool, + pub(crate) source_file: bool, + pub(crate) source_line_number: bool, + pub(crate) indent: Option, + pub(crate) suffix: &'static str, + #[cfg(feature = "kv")] + pub(crate) kv_format: Option>, +} + +impl Default for DefaultFormat { + fn default() -> Self { + Self { + timestamp: Some(Default::default()), + module_path: false, + target: true, + level: true, + source_file: false, + source_line_number: false, + indent: Some(4), + suffix: "\n", + #[cfg(feature = "kv")] + kv_format: None, + } + } +} + /// The default format. /// /// This format needs to work with any combination of crate features. diff --git a/src/logger.rs b/src/logger.rs index ed8100d..479bace 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -258,13 +258,13 @@ impl Builder { /// Whether or not to write the level in the default format. pub fn format_level(&mut self, write: bool) -> &mut Self { - self.format.format_level = write; + self.format.default_format.level = write; self } /// Whether or not to write the source file path in the default format. pub fn format_file(&mut self, write: bool) -> &mut Self { - self.format.format_file = write; + self.format.default_format.source_file = write; self } @@ -272,7 +272,7 @@ impl Builder { /// /// Only has effect if `format_file` is also enabled pub fn format_line_number(&mut self, write: bool) -> &mut Self { - self.format.format_line_number = write; + self.format.default_format.source_line_number = write; self } @@ -287,26 +287,26 @@ impl Builder { /// Whether or not to write the module path in the default format. pub fn format_module_path(&mut self, write: bool) -> &mut Self { - self.format.format_module_path = write; + self.format.default_format.module_path = write; self } /// Whether or not to write the target in the default format. pub fn format_target(&mut self, write: bool) -> &mut Self { - self.format.format_target = write; + self.format.default_format.target = write; self } /// Configures the amount of spaces to use to indent multiline log records. /// A value of `None` disables any kind of indentation. pub fn format_indent(&mut self, indent: Option) -> &mut Self { - self.format.format_indent = indent; + self.format.default_format.indent = indent; self } /// Configures if timestamp should be included and in what precision. pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { - self.format.format_timestamp = timestamp; + self.format.default_format.timestamp = timestamp; self } @@ -332,7 +332,7 @@ impl Builder { /// Configures the end of line suffix. pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self { - self.format.format_suffix = suffix; + self.format.default_format.suffix = suffix; self } @@ -351,7 +351,7 @@ impl Builder { where F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static, { - self.format.kv_format = Some(Box::new(format)); + self.format.default_format.kv_format = Some(Box::new(format)); self } From e351bcb92d99d7835c987838f09ba7d3949df055 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 12:37:33 -0500 Subject: [PATCH 3/8] refactor(fmt): Reduce duplication in DefaultFormatWriter --- src/fmt/mod.rs | 342 +++++++++++++++++++++++++------------------------ 1 file changed, 177 insertions(+), 165 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index cc23da6..c1e17f6 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -231,22 +231,9 @@ impl Builder { } else { Box::new(move |buf, record| { let fmt = DefaultFormatWriter { - timestamp: built.default_format.timestamp, - module_path: built.default_format.module_path, - target: built.default_format.target, - level: built.default_format.level, - written_header_value: false, - indent: built.default_format.indent, - suffix: built.default_format.suffix, - source_file: built.default_format.source_file, - source_line_number: built.default_format.source_line_number, - #[cfg(feature = "kv")] - kv_format: built - .default_format - .kv_format - .as_deref() - .unwrap_or(&default_kv_format), + format: &built.default_format, buf, + written_header_value: false, }; fmt.write(record) @@ -321,18 +308,9 @@ impl Default for DefaultFormat { /// /// This format needs to work with any combination of crate features. struct DefaultFormatWriter<'a> { - timestamp: Option, - module_path: bool, - target: bool, - level: bool, - source_file: bool, - source_line_number: bool, - written_header_value: bool, - indent: Option, + format: &'a DefaultFormat, buf: &'a mut Formatter, - suffix: &'a str, - #[cfg(feature = "kv")] - kv_format: &'a KvFormatFn, + written_header_value: bool, } impl DefaultFormatWriter<'_> { @@ -347,7 +325,7 @@ impl DefaultFormatWriter<'_> { self.write_args(record)?; #[cfg(feature = "kv")] self.write_kv(record)?; - write!(self.buf, "{}", self.suffix) + write!(self.buf, "{}", self.format.suffix) } fn subtle_style(&self, text: &'static str) -> SubtleStyle { @@ -383,7 +361,7 @@ impl DefaultFormatWriter<'_> { } fn write_level(&mut self, record: &Record<'_>) -> io::Result<()> { - if !self.level { + if !self.format.level { return Ok(()); } @@ -409,7 +387,7 @@ impl DefaultFormatWriter<'_> { #[cfg(feature = "humantime")] { use self::TimestampPrecision::{Micros, Millis, Nanos, Seconds}; - let ts = match self.timestamp { + let ts = match self.format.timestamp { None => return Ok(()), Some(Seconds) => self.buf.timestamp_seconds(), Some(Millis) => self.buf.timestamp_millis(), @@ -423,13 +401,13 @@ impl DefaultFormatWriter<'_> { { // Trick the compiler to think we have used self.timestamp // Workaround for "field is never used: `timestamp`" compiler nag. - let _ = self.timestamp; + let _ = self.format.timestamp; Ok(()) } } fn write_module_path(&mut self, record: &Record<'_>) -> io::Result<()> { - if !self.module_path { + if !self.format.module_path { return Ok(()); } @@ -441,12 +419,16 @@ impl DefaultFormatWriter<'_> { } fn write_source_location(&mut self, record: &Record<'_>) -> io::Result<()> { - if !self.source_file { + if !self.format.source_file { return Ok(()); } if let Some(file_path) = record.file() { - let line = self.source_line_number.then(|| record.line()).flatten(); + let line = self + .format + .source_line_number + .then(|| record.line()) + .flatten(); match line { Some(line) => self.write_header_value(format_args!("{file_path}:{line}")), None => self.write_header_value(file_path), @@ -457,7 +439,7 @@ impl DefaultFormatWriter<'_> { } fn write_target(&mut self, record: &Record<'_>) -> io::Result<()> { - if !self.target { + if !self.format.target { return Ok(()); } @@ -477,7 +459,7 @@ impl DefaultFormatWriter<'_> { } fn write_args(&mut self, record: &Record<'_>) -> io::Result<()> { - match self.indent { + match self.format.indent { // Fast path for no indentation None => write!(self.buf, "{}", record.args()), @@ -497,7 +479,7 @@ impl DefaultFormatWriter<'_> { write!( self.fmt.buf, "{}{:width$}", - self.fmt.suffix, + self.fmt.format.suffix, "", width = self.indent_count )?; @@ -530,7 +512,11 @@ impl DefaultFormatWriter<'_> { #[cfg(feature = "kv")] fn write_kv(&mut self, record: &Record<'_>) -> io::Result<()> { - let format = self.kv_format; + let format = self + .format + .kv_format + .as_deref() + .unwrap_or(&default_kv_format); format(self.buf, record.key_values()) } } @@ -581,17 +567,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: true, - target: false, - level: true, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: false, + level: true, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }); @@ -603,17 +591,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: false, - target: false, - level: false, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: false, + target: false, + level: false, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }); @@ -625,17 +615,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: true, - target: false, - level: true, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: false, + level: true, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: Some(4), + suffix: "\n", + }, written_header_value: false, - indent: Some(4), - suffix: "\n", buf: &mut f, }); @@ -647,17 +639,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: true, - target: false, - level: true, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: false, + level: true, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: Some(0), + suffix: "\n", + }, written_header_value: false, - indent: Some(0), - suffix: "\n", buf: &mut f, }); @@ -669,17 +663,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: false, - target: false, - level: false, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: false, + target: false, + level: false, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: Some(4), + suffix: "\n", + }, written_header_value: false, - indent: Some(4), - suffix: "\n", buf: &mut f, }); @@ -691,17 +687,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: false, - target: false, - level: false, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: false, + target: false, + level: false, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n\n", + }, written_header_value: false, - indent: None, - suffix: "\n\n", buf: &mut f, }); @@ -713,17 +711,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: false, - target: false, - level: false, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: false, + target: false, + level: false, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: Some(4), + suffix: "\n\n", + }, written_header_value: false, - indent: Some(4), - suffix: "\n\n", buf: &mut f, }); @@ -737,17 +737,19 @@ mod tests { let written = write_target( "target", DefaultFormatWriter { - timestamp: None, - module_path: true, - target: true, - level: true, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: true, + level: true, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }, ); @@ -760,17 +762,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: true, - target: true, - level: true, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: true, + level: true, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }); @@ -784,17 +788,19 @@ mod tests { let written = write_target( "target", DefaultFormatWriter { - timestamp: None, - module_path: true, - target: false, - level: true, - source_file: false, - source_line_number: false, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: false, + level: true, + source_file: false, + source_line_number: false, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }, ); @@ -807,17 +813,19 @@ mod tests { let mut f = formatter(); let written = write(DefaultFormatWriter { - timestamp: None, - module_path: false, - target: false, - level: true, - source_file: true, - source_line_number: true, - #[cfg(feature = "kv")] - kv_format: &hidden_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: false, + target: false, + level: true, + source_file: true, + source_line_number: true, + #[cfg(feature = "kv")] + kv_format: Some(Box::new(hidden_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }); @@ -839,16 +847,18 @@ mod tests { let written = write_record( record, DefaultFormatWriter { - timestamp: None, - module_path: false, - target: false, - level: true, - source_file: false, - source_line_number: false, - kv_format: &default_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: false, + target: false, + level: true, + source_file: false, + source_line_number: false, + kv_format: Some(Box::new(default_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }, ); @@ -874,16 +884,18 @@ mod tests { let written = write_record( record, DefaultFormatWriter { - timestamp: None, - module_path: true, - target: true, - level: true, - source_file: true, - source_line_number: true, - kv_format: &default_kv_format, + format: &DefaultFormat { + timestamp: None, + module_path: true, + target: true, + level: true, + source_file: true, + source_line_number: true, + kv_format: Some(Box::new(default_kv_format)), + indent: None, + suffix: "\n", + }, written_header_value: false, - indent: None, - suffix: "\n", buf: &mut f, }, ); From 3acb571daa9e9c63ac4a491df55ec69000380630 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 12:45:10 -0500 Subject: [PATCH 4/8] refactor(fmt): Delegate formatting to DefaultFormat --- src/fmt/mod.rs | 37 +++++++++++++++++++++++++++---------- src/logger.rs | 6 ++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index c1e17f6..9e7ace0 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -200,7 +200,20 @@ impl fmt::Debug for Formatter { } } -pub(crate) type FormatFn = Box) -> io::Result<()> + Sync + Send>; +pub(crate) trait RecordFormat { + fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()>; +} + +impl RecordFormat for F +where + F: Fn(&mut Formatter, &Record<'_>) -> io::Result<()>, +{ + fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { + (self)(formatter, record) + } +} + +pub(crate) type FormatFn = Box; #[derive(Default)] pub(crate) struct Builder { @@ -229,15 +242,7 @@ impl Builder { if let Some(fmt) = built.custom_format { fmt } else { - Box::new(move |buf, record| { - let fmt = DefaultFormatWriter { - format: &built.default_format, - buf, - written_header_value: false, - }; - - fmt.write(record) - }) + Box::new(built.default_format) } } } @@ -304,6 +309,18 @@ impl Default for DefaultFormat { } } +impl RecordFormat for DefaultFormat { + fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { + let fmt = DefaultFormatWriter { + format: self, + buf: formatter, + written_header_value: false, + }; + + fmt.write(record) + } +} + /// The default format. /// /// This format needs to work with any combination of crate features. diff --git a/src/logger.rs b/src/logger.rs index 479bace..0c20a89 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -664,8 +664,10 @@ impl Log for Logger { } let print = |formatter: &mut Formatter, record: &Record<'_>| { - let _ = - (self.format)(formatter, record).and_then(|_| formatter.print(&self.writer)); + let _ = self + .format + .format(formatter, record) + .and_then(|_| formatter.print(&self.writer)); // Always clear the buffer afterwards formatter.clear(); From 739ebb1d37dcfc466d0533aa2c974449610e7910 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 12:56:12 -0500 Subject: [PATCH 5/8] refactor(fmt): Pull out logger's builder methods --- src/fmt/mod.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/logger.rs | 18 ++++++------- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 9e7ace0..7e5d287 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -292,6 +292,78 @@ pub(crate) struct DefaultFormat { pub(crate) kv_format: Option>, } +impl DefaultFormat { + /// Whether or not to write the level in the default format. + pub(crate) fn level(&mut self, write: bool) -> &mut Self { + self.level = write; + self + } + + /// Whether or not to write the source file path in the default format. + pub(crate) fn file(&mut self, write: bool) -> &mut Self { + self.source_file = write; + self + } + + /// Whether or not to write the source line number path in the default format. + /// + /// Only has effect if `format_file` is also enabled + pub(crate) fn line_number(&mut self, write: bool) -> &mut Self { + self.source_line_number = write; + self + } + + /// Whether or not to write the module path in the default format. + pub(crate) fn module_path(&mut self, write: bool) -> &mut Self { + self.module_path = write; + self + } + + /// Whether or not to write the target in the default format. + pub(crate) fn target(&mut self, write: bool) -> &mut Self { + self.target = write; + self + } + + /// Configures the amount of spaces to use to indent multiline log records. + /// A value of `None` disables any kind of indentation. + pub(crate) fn indent(&mut self, indent: Option) -> &mut Self { + self.indent = indent; + self + } + + /// Configures if timestamp should be included and in what precision. + pub(crate) fn timestamp(&mut self, timestamp: Option) -> &mut Self { + self.timestamp = timestamp; + self + } + + /// Configures the end of line suffix. + pub(crate) fn suffix(&mut self, suffix: &'static str) -> &mut Self { + self.suffix = suffix; + self + } + + /// Set the format for structured key/value pairs in the log record + /// + /// With the default format, this function is called for each record and should format + /// the structured key-value pairs as returned by [`log::Record::key_values`]. + /// + /// The format function is expected to output the string directly to the `Formatter` so that + /// implementations can use the [`std::fmt`] macros, similar to the main format function. + /// + /// The default format uses a space to separate each key-value pair, with an "=" between + /// the key and value. + #[cfg(feature = "kv")] + pub(crate) fn key_values(&mut self, format: F) -> &mut Self + where + F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static, + { + self.kv_format = Some(Box::new(format)); + self + } +} + impl Default for DefaultFormat { fn default() -> Self { Self { diff --git a/src/logger.rs b/src/logger.rs index 0c20a89..bd4d13b 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -258,13 +258,13 @@ impl Builder { /// Whether or not to write the level in the default format. pub fn format_level(&mut self, write: bool) -> &mut Self { - self.format.default_format.level = write; + self.format.default_format.level(write); self } /// Whether or not to write the source file path in the default format. pub fn format_file(&mut self, write: bool) -> &mut Self { - self.format.default_format.source_file = write; + self.format.default_format.file(write); self } @@ -272,7 +272,7 @@ impl Builder { /// /// Only has effect if `format_file` is also enabled pub fn format_line_number(&mut self, write: bool) -> &mut Self { - self.format.default_format.source_line_number = write; + self.format.default_format.line_number(write); self } @@ -287,26 +287,26 @@ impl Builder { /// Whether or not to write the module path in the default format. pub fn format_module_path(&mut self, write: bool) -> &mut Self { - self.format.default_format.module_path = write; + self.format.default_format.module_path(write); self } /// Whether or not to write the target in the default format. pub fn format_target(&mut self, write: bool) -> &mut Self { - self.format.default_format.target = write; + self.format.default_format.target(write); self } /// Configures the amount of spaces to use to indent multiline log records. /// A value of `None` disables any kind of indentation. pub fn format_indent(&mut self, indent: Option) -> &mut Self { - self.format.default_format.indent = indent; + self.format.default_format.indent(indent); self } /// Configures if timestamp should be included and in what precision. pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { - self.format.default_format.timestamp = timestamp; + self.format.default_format.timestamp(timestamp); self } @@ -332,7 +332,7 @@ impl Builder { /// Configures the end of line suffix. pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self { - self.format.default_format.suffix = suffix; + self.format.default_format.suffix(suffix); self } @@ -351,7 +351,7 @@ impl Builder { where F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static, { - self.format.default_format.kv_format = Some(Box::new(format)); + self.format.default_format.key_values(format); self } From ce25c7396116665cd3dc2f4622d25fce5a818975 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 12:59:01 -0500 Subject: [PATCH 6/8] refactor(fmt): Make DefaultFormats name more specific --- src/fmt/mod.rs | 78 +++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 7e5d287..ad216a6 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -217,7 +217,7 @@ pub(crate) type FormatFn = Box; #[derive(Default)] pub(crate) struct Builder { - pub(crate) default_format: DefaultFormat, + pub(crate) default_format: ConfigurableFormat, pub(crate) custom_format: Option, built: bool, } @@ -279,7 +279,7 @@ type StyledValue = T; /// The default format. /// /// This format needs to work with any combination of crate features. -pub(crate) struct DefaultFormat { +pub(crate) struct ConfigurableFormat { pub(crate) timestamp: Option, pub(crate) module_path: bool, pub(crate) target: bool, @@ -292,7 +292,7 @@ pub(crate) struct DefaultFormat { pub(crate) kv_format: Option>, } -impl DefaultFormat { +impl ConfigurableFormat { /// Whether or not to write the level in the default format. pub(crate) fn level(&mut self, write: bool) -> &mut Self { self.level = write; @@ -364,7 +364,7 @@ impl DefaultFormat { } } -impl Default for DefaultFormat { +impl Default for ConfigurableFormat { fn default() -> Self { Self { timestamp: Some(Default::default()), @@ -381,9 +381,9 @@ impl Default for DefaultFormat { } } -impl RecordFormat for DefaultFormat { +impl RecordFormat for ConfigurableFormat { fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { - let fmt = DefaultFormatWriter { + let fmt = ConfigurableFormatWriter { format: self, buf: formatter, written_header_value: false, @@ -396,13 +396,13 @@ impl RecordFormat for DefaultFormat { /// The default format. /// /// This format needs to work with any combination of crate features. -struct DefaultFormatWriter<'a> { - format: &'a DefaultFormat, +struct ConfigurableFormatWriter<'a> { + format: &'a ConfigurableFormat, buf: &'a mut Formatter, written_header_value: bool, } -impl DefaultFormatWriter<'_> { +impl ConfigurableFormatWriter<'_> { fn write(mut self, record: &Record<'_>) -> io::Result<()> { self.write_timestamp()?; self.write_level(record)?; @@ -556,7 +556,7 @@ impl DefaultFormatWriter<'_> { // Create a wrapper around the buffer only if we have to actually indent the message struct IndentWrapper<'a, 'b> { - fmt: &'a mut DefaultFormatWriter<'b>, + fmt: &'a mut ConfigurableFormatWriter<'b>, indent_count: usize, } @@ -616,7 +616,7 @@ mod tests { use log::{Level, Record}; - fn write_record(record: Record<'_>, fmt: DefaultFormatWriter<'_>) -> String { + fn write_record(record: Record<'_>, fmt: ConfigurableFormatWriter<'_>) -> String { let buf = fmt.buf.buf.clone(); fmt.write(&record).expect("failed to write record"); @@ -625,7 +625,7 @@ mod tests { String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record") } - fn write_target(target: &str, fmt: DefaultFormatWriter<'_>) -> String { + fn write_target(target: &str, fmt: ConfigurableFormatWriter<'_>) -> String { write_record( Record::builder() .args(format_args!("log\nmessage")) @@ -639,7 +639,7 @@ mod tests { ) } - fn write(fmt: DefaultFormatWriter<'_>) -> String { + fn write(fmt: ConfigurableFormatWriter<'_>) -> String { write_target("", fmt) } @@ -655,8 +655,8 @@ mod tests { fn format_with_header() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: false, @@ -679,8 +679,8 @@ mod tests { fn format_no_header() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: false, target: false, @@ -703,8 +703,8 @@ mod tests { fn format_indent_spaces() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: false, @@ -727,8 +727,8 @@ mod tests { fn format_indent_zero_spaces() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: false, @@ -751,8 +751,8 @@ mod tests { fn format_indent_spaces_no_header() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: false, target: false, @@ -775,8 +775,8 @@ mod tests { fn format_suffix() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: false, target: false, @@ -799,8 +799,8 @@ mod tests { fn format_suffix_with_indent() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: false, target: false, @@ -825,8 +825,8 @@ mod tests { let written = write_target( "target", - DefaultFormatWriter { - format: &DefaultFormat { + ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: true, @@ -850,8 +850,8 @@ mod tests { fn format_empty_target() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: true, @@ -876,8 +876,8 @@ mod tests { let written = write_target( "target", - DefaultFormatWriter { - format: &DefaultFormat { + ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: false, @@ -901,8 +901,8 @@ mod tests { fn format_with_source_file_and_line_number() { let mut f = formatter(); - let written = write(DefaultFormatWriter { - format: &DefaultFormat { + let written = write(ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: false, target: false, @@ -935,8 +935,8 @@ mod tests { let written = write_record( record, - DefaultFormatWriter { - format: &DefaultFormat { + ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: false, target: false, @@ -972,8 +972,8 @@ mod tests { let written = write_record( record, - DefaultFormatWriter { - format: &DefaultFormat { + ConfigurableFormatWriter { + format: &ConfigurableFormat { timestamp: None, module_path: true, target: true, From c567fdee731d76f19da887ba9f4ebdf435335d3c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 14:19:16 -0500 Subject: [PATCH 7/8] refactor(fmt): Pull out format logic --- src/fmt/mod.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index ad216a6..fedc08f 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -292,6 +292,19 @@ pub(crate) struct ConfigurableFormat { pub(crate) kv_format: Option>, } +impl ConfigurableFormat { + /// Format the [`Record`] as configured for outputting + pub(crate) fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { + let fmt = ConfigurableFormatWriter { + format: self, + buf: formatter, + written_header_value: false, + }; + + fmt.write(record) + } +} + impl ConfigurableFormat { /// Whether or not to write the level in the default format. pub(crate) fn level(&mut self, write: bool) -> &mut Self { @@ -383,13 +396,7 @@ impl Default for ConfigurableFormat { impl RecordFormat for ConfigurableFormat { fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { - let fmt = ConfigurableFormatWriter { - format: self, - buf: formatter, - written_header_value: false, - }; - - fmt.write(record) + self.format(formatter, record) } } From bc02d61e0a60210f846896c0cc80d52a55901460 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 1 Apr 2025 13:00:27 -0500 Subject: [PATCH 8/8] feat(fmt): Expose ConfigurableFormat --- src/fmt/mod.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index fedc08f..bd2a2b8 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -276,10 +276,9 @@ impl Display for StyledValue { #[cfg(not(feature = "color"))] type StyledValue = T; -/// The default format. -/// -/// This format needs to work with any combination of crate features. -pub(crate) struct ConfigurableFormat { +/// A [custom format][crate::Builder::format] with settings for which fields to show +pub struct ConfigurableFormat { + // This format needs to work with any combination of crate features. pub(crate) timestamp: Option, pub(crate) module_path: bool, pub(crate) target: bool, @@ -294,7 +293,7 @@ pub(crate) struct ConfigurableFormat { impl ConfigurableFormat { /// Format the [`Record`] as configured for outputting - pub(crate) fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { + pub fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> { let fmt = ConfigurableFormatWriter { format: self, buf: formatter, @@ -307,13 +306,13 @@ impl ConfigurableFormat { impl ConfigurableFormat { /// Whether or not to write the level in the default format. - pub(crate) fn level(&mut self, write: bool) -> &mut Self { + pub fn level(&mut self, write: bool) -> &mut Self { self.level = write; self } /// Whether or not to write the source file path in the default format. - pub(crate) fn file(&mut self, write: bool) -> &mut Self { + pub fn file(&mut self, write: bool) -> &mut Self { self.source_file = write; self } @@ -321,38 +320,38 @@ impl ConfigurableFormat { /// Whether or not to write the source line number path in the default format. /// /// Only has effect if `format_file` is also enabled - pub(crate) fn line_number(&mut self, write: bool) -> &mut Self { + pub fn line_number(&mut self, write: bool) -> &mut Self { self.source_line_number = write; self } /// Whether or not to write the module path in the default format. - pub(crate) fn module_path(&mut self, write: bool) -> &mut Self { + pub fn module_path(&mut self, write: bool) -> &mut Self { self.module_path = write; self } /// Whether or not to write the target in the default format. - pub(crate) fn target(&mut self, write: bool) -> &mut Self { + pub fn target(&mut self, write: bool) -> &mut Self { self.target = write; self } /// Configures the amount of spaces to use to indent multiline log records. /// A value of `None` disables any kind of indentation. - pub(crate) fn indent(&mut self, indent: Option) -> &mut Self { + pub fn indent(&mut self, indent: Option) -> &mut Self { self.indent = indent; self } /// Configures if timestamp should be included and in what precision. - pub(crate) fn timestamp(&mut self, timestamp: Option) -> &mut Self { + pub fn timestamp(&mut self, timestamp: Option) -> &mut Self { self.timestamp = timestamp; self } /// Configures the end of line suffix. - pub(crate) fn suffix(&mut self, suffix: &'static str) -> &mut Self { + pub fn suffix(&mut self, suffix: &'static str) -> &mut Self { self.suffix = suffix; self } @@ -368,7 +367,7 @@ impl ConfigurableFormat { /// The default format uses a space to separate each key-value pair, with an "=" between /// the key and value. #[cfg(feature = "kv")] - pub(crate) fn key_values(&mut self, format: F) -> &mut Self + pub fn key_values(&mut self, format: F) -> &mut Self where F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static, {