diff --git a/Cargo.toml b/Cargo.toml index a8435975..18d46f04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,9 @@ yaml_format = ["serde_yaml"] json_format = ["serde_json"] toml_format = ["toml"] -console_appender = ["console_writer", "simple_writer", "pattern_encoder"] -file_appender = ["parking_lot", "simple_writer", "pattern_encoder"] -rolling_file_appender = ["parking_lot", "simple_writer", "pattern_encoder"] +console_appender = ["console_writer", "simple_writer"] +file_appender = ["parking_lot", "simple_writer"] +rolling_file_appender = ["parking_lot", "simple_writer"] compound_policy = [] delete_roller = [] fixed_window_roller = [] diff --git a/benches/rotation.rs b/benches/rotation.rs index de108dab..71724149 100644 --- a/benches/rotation.rs +++ b/benches/rotation.rs @@ -1,5 +1,7 @@ -use std::thread; -use std::time::{Duration, Instant}; +use std::{ + thread, + time::{Duration, Instant}, +}; use lazy_static::lazy_static; use tempfile::{tempdir, TempDir}; @@ -95,13 +97,14 @@ fn mk_config(file_size: u64, file_count: u32) -> log4rs::config::Config { #[derive(Debug)] struct Stats { - min: Duration, - max: Duration, - median: Duration, + _min: Duration, + _max: Duration, + _median: Duration, mean_nanos: u128, - variance_nanos: f64, + _variance_nanos: f64, stddev_nanos: f64, } + impl Stats { fn new(measurements: &mut [Duration]) -> Self { measurements.sort(); @@ -121,11 +124,11 @@ impl Stats { }); Self { - min: measurements.first().unwrap().to_owned(), - max: measurements.last().unwrap().to_owned(), - median: measurements[measurements.len() / 2], + _min: measurements.first().unwrap().to_owned(), + _max: measurements.last().unwrap().to_owned(), + _median: measurements[measurements.len() / 2], mean_nanos, - variance_nanos, + _variance_nanos: variance_nanos, stddev_nanos: variance_nanos.sqrt(), } } diff --git a/src/append/console.rs b/src/append/console.rs index 4eabc6d1..33497e6e 100644 --- a/src/append/console.rs +++ b/src/append/console.rs @@ -11,13 +11,14 @@ use std::{ #[cfg(feature = "config_parsing")] use crate::config::{Deserialize, Deserializers}; +#[cfg(feature = "pattern_encoder")] +use crate::encode::pattern::PatternEncoder; #[cfg(feature = "config_parsing")] use crate::encode::EncoderConfig; use crate::{ append::Append, encode::{ self, - pattern::PatternEncoder, writer::{ console::{ConsoleWriter, ConsoleWriterLock}, simple::SimpleWriter, @@ -143,6 +144,7 @@ impl ConsoleAppender { /// Creates a new `ConsoleAppender` builder. pub fn builder() -> ConsoleAppenderBuilder { ConsoleAppenderBuilder { + #[cfg(feature = "pattern_encoder")] encoder: None, target: Target::Stdout, tty_only: false, @@ -152,6 +154,7 @@ impl ConsoleAppender { /// A builder for `ConsoleAppender`s. pub struct ConsoleAppenderBuilder { + #[cfg(feature = "pattern_encoder")] encoder: Option>, target: Target, tty_only: bool, @@ -159,6 +162,7 @@ pub struct ConsoleAppenderBuilder { impl ConsoleAppenderBuilder { /// Sets the output encoder for the `ConsoleAppender`. + #[cfg(feature = "pattern_encoder")] pub fn encoder(mut self, encoder: Box) -> ConsoleAppenderBuilder { self.encoder = Some(encoder); self @@ -181,8 +185,30 @@ impl ConsoleAppenderBuilder { } /// Consumes the `ConsoleAppenderBuilder`, producing a `ConsoleAppender`. + #[cfg(feature = "pattern_encoder")] pub fn build(self) -> ConsoleAppender { - let writer = match self.target { + self.build_internal(|this| { + let encoder = this + .encoder + .unwrap_or_else(|| Box::new(PatternEncoder::default())); + + (encoder, this.target, this.tty_only) + }) + } + + /// Consumes the `ConsoleAppenderBuilder`, producing a `ConsoleAppender`. + #[cfg(not(feature = "pattern_encoder"))] + pub fn build(self, encoder: Box) -> ConsoleAppender { + self.build_internal(|this| (encoder, this.target, this.tty_only)) + } + + fn build_internal(self, destructure: F) -> ConsoleAppender + where + F: FnOnce(Self) -> (Box, Target, bool), + { + let (encoder, target, tty_only) = destructure(self); + + let writer = match target { Target::Stderr => match ConsoleWriter::stderr() { Some(writer) => Writer::Tty(writer), None => Writer::Raw(StdWriter::stderr()), @@ -193,13 +219,11 @@ impl ConsoleAppenderBuilder { }, }; - let do_write = writer.is_tty() || !self.tty_only; + let do_write = writer.is_tty() || !tty_only; ConsoleAppender { writer, - encoder: self - .encoder - .unwrap_or_else(|| Box::new(PatternEncoder::default())), + encoder, do_write, } } diff --git a/src/append/file.rs b/src/append/file.rs index cf12943d..a6a98326 100644 --- a/src/append/file.rs +++ b/src/append/file.rs @@ -13,12 +13,14 @@ use std::{ #[cfg(feature = "config_parsing")] use crate::config::{Deserialize, Deserializers}; +#[cfg(feature = "pattern_encoder")] +use crate::encode::pattern::PatternEncoder; #[cfg(feature = "config_parsing")] use crate::encode::EncoderConfig; use crate::{ append::Append, - encode::{pattern::PatternEncoder, writer::simple::SimpleWriter, Encode}, + encode::{writer::simple::SimpleWriter, Encode}, }; /// The file appender's configuration. @@ -31,13 +33,15 @@ pub struct FileAppenderConfig { append: Option, } +type SharedFileWriter = Mutex>>; + /// An appender which logs to a file. #[derive(Derivative)] #[derivative(Debug)] pub struct FileAppender { path: PathBuf, #[derivative(Debug = "ignore")] - file: Mutex>>, + file: SharedFileWriter, encoder: Box, } @@ -88,24 +92,53 @@ impl FileAppenderBuilder { /// where 'name_here' will be the name of the environment variable that /// will be resolved. Note that if the variable fails to resolve, /// $ENV{name_here} will NOT be replaced in the path. + #[cfg(feature = "pattern_encoder")] pub fn build>(self, path: P) -> io::Result { - let path = super::env_util::expand_env_vars(path.as_ref().to_path_buf()); + self.build_internal(path.as_ref(), |this| { + let encoder = this + .encoder + .unwrap_or_else(|| Box::new(PatternEncoder::default())); + + (encoder, this.append) + }) + } + + /// Consumes the `FileAppenderBuilder`, producing a `FileAppender`. + /// The path argument can contain environment variables of the form $ENV{name_here}, + /// where 'name_here' will be the name of the environment variable that + /// will be resolved. Note that if the variable fails to resolve, + /// $ENV{name_here} will NOT be replaced in the path. + #[cfg(not(feature = "pattern_encoder"))] + pub fn build>( + self, + path: P, + encoder: Box, + ) -> io::Result { + self.build_internal(path.as_ref(), |this| (encoder, this.append)) + } + + fn build_internal(self, path: &Path, destructure: F) -> io::Result + where + F: FnOnce(Self) -> (Box, bool), + { + let (encoder, append) = destructure(self); + + let path = super::env_util::expand_env_vars(path.to_path_buf()); if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } + let file = OpenOptions::new() .write(true) - .append(self.append) - .truncate(!self.append) + .append(append) + .truncate(!append) .create(true) .open(&path)?; Ok(FileAppender { path, file: Mutex::new(SimpleWriter(BufWriter::with_capacity(1024, file))), - encoder: self - .encoder - .unwrap_or_else(|| Box::new(PatternEncoder::default())), + encoder, }) } } @@ -166,17 +199,33 @@ mod test { fn create_directories() { let tempdir = tempfile::tempdir().unwrap(); - FileAppender::builder() - .build(tempdir.path().join("foo").join("bar").join("baz.log")) - .unwrap(); + build( + FileAppender::builder(), + tempdir.path().join("foo").join("bar").join("baz.log"), + ) + .unwrap(); } #[test] fn append_false() { let tempdir = tempfile::tempdir().unwrap(); - FileAppender::builder() - .append(false) - .build(tempdir.path().join("foo.log")) - .unwrap(); + + build( + FileAppender::builder().append(false), + tempdir.path().join("foo.log"), + ) + .unwrap(); + } + + #[cfg(feature = "pattern_encoder")] + fn build>(builder: FileAppenderBuilder, path: P) -> io::Result { + builder.build(path) + } + + #[cfg(not(feature = "pattern_encoder"))] + fn build>(builder: FileAppenderBuilder, path: P) -> io::Result { + use crate::encode::tests::DummyEncoder; + + builder.build(path, Box::new(DummyEncoder)) } } diff --git a/src/append/rolling_file/mod.rs b/src/append/rolling_file/mod.rs index d391abb8..39fabaac 100644 --- a/src/append/rolling_file/mod.rs +++ b/src/append/rolling_file/mod.rs @@ -25,6 +25,8 @@ use std::{ path::{Path, PathBuf}, }; +#[cfg(feature = "pattern_encoder")] +use crate::encode::pattern::PatternEncoder; #[cfg(feature = "config_parsing")] use serde_value::Value; #[cfg(feature = "config_parsing")] @@ -32,7 +34,7 @@ use std::collections::BTreeMap; use crate::{ append::Append, - encode::{self, pattern::PatternEncoder, Encode}, + encode::{self, Encode}, }; #[cfg(feature = "config_parsing")] @@ -193,6 +195,7 @@ impl RollingFileAppender { pub fn builder() -> RollingFileAppenderBuilder { RollingFileAppenderBuilder { append: true, + #[cfg(feature = "pattern_encoder")] encoder: None, } } @@ -224,6 +227,7 @@ impl RollingFileAppender { /// A builder for the `RollingFileAppender`. pub struct RollingFileAppenderBuilder { append: bool, + #[cfg(feature = "pattern_encoder")] encoder: Option>, } @@ -239,6 +243,7 @@ impl RollingFileAppenderBuilder { /// Sets the encoder used by the appender. /// /// Defaults to a `PatternEncoder` with the default pattern. + #[cfg(feature = "pattern_encoder")] pub fn encoder(mut self, encoder: Box) -> RollingFileAppenderBuilder { self.encoder = Some(encoder); self @@ -249,6 +254,7 @@ impl RollingFileAppenderBuilder { /// where 'name_here' will be the name of the environment variable that /// will be resolved. Note that if the variable fails to resolve, /// $ENV{name_here} will NOT be replaced in the path. + #[cfg(feature = "pattern_encoder")] pub fn build

( self, path: P, @@ -257,14 +263,50 @@ impl RollingFileAppenderBuilder { where P: AsRef, { - let path = super::env_util::expand_env_vars(path.as_ref().to_path_buf()); + self.build_internal(path.as_ref(), policy, |this| { + let encoder = this + .encoder + .unwrap_or_else(|| Box::new(PatternEncoder::default())); + + (encoder, this.append) + }) + } + + /// Constructs a `RollingFileAppender`. + /// The path argument can contain environment variables of the form $ENV{name_here}, + /// where 'name_here' will be the name of the environment variable that + /// will be resolved. Note that if the variable fails to resolve, + /// $ENV{name_here} will NOT be replaced in the path. + #[cfg(not(feature = "pattern_encoder"))] + pub fn build

( + self, + path: P, + encoder: Box, + policy: Box, + ) -> io::Result + where + P: AsRef, + { + self.build_internal(path.as_ref(), policy, |this| (encoder, this.append)) + } + + fn build_internal( + self, + path: &Path, + policy: Box, + destructure: F, + ) -> io::Result + where + F: FnOnce(Self) -> (Box, bool), + { + let (encoder, append) = destructure(self); + + let path = super::env_util::expand_env_vars(path.to_path_buf()); let appender = RollingFileAppender { writer: Mutex::new(None), path, - append: self.append, - encoder: self - .encoder - .unwrap_or_else(|| Box::new(PatternEncoder::default())), + append, + encoder, policy, }; @@ -411,17 +453,11 @@ appenders: fn append() { let dir = tempfile::tempdir().unwrap(); let path = dir.path().join("append.log"); - RollingFileAppender::builder() - .append(true) - .build(&path, Box::new(NopPolicy)) - .unwrap(); + build(RollingFileAppender::builder().append(true), &path).unwrap(); assert!(path.exists()); File::create(&path).unwrap().write_all(b"hello").unwrap(); - RollingFileAppender::builder() - .append(true) - .build(&path, Box::new(NopPolicy)) - .unwrap(); + build(RollingFileAppender::builder().append(true), &path).unwrap(); let mut contents = vec![]; File::open(&path) .unwrap() @@ -434,17 +470,11 @@ appenders: fn truncate() { let dir = tempfile::tempdir().unwrap(); let path = dir.path().join("truncate.log"); - RollingFileAppender::builder() - .append(false) - .build(&path, Box::new(NopPolicy)) - .unwrap(); + build(RollingFileAppender::builder().append(false), &path).unwrap(); assert!(path.exists()); File::create(&path).unwrap().write_all(b"hello").unwrap(); - RollingFileAppender::builder() - .append(false) - .build(&path, Box::new(NopPolicy)) - .unwrap(); + build(RollingFileAppender::builder().append(false), &path).unwrap(); let mut contents = vec![]; File::open(&path) .unwrap() @@ -452,4 +482,16 @@ appenders: .unwrap(); assert_eq!(contents, b""); } + + #[cfg(feature = "pattern_encoder")] + fn build(builder: RollingFileAppenderBuilder, path: &Path) -> io::Result { + builder.build(path, Box::new(NopPolicy)) + } + + #[cfg(not(feature = "pattern_encoder"))] + fn build(builder: RollingFileAppenderBuilder, path: &Path) -> io::Result { + use crate::encode::tests::DummyEncoder; + + builder.build(path, Box::new(DummyEncoder), Box::new(NopPolicy)) + } } diff --git a/src/append/rolling_file/policy/compound/trigger/size.rs b/src/append/rolling_file/policy/compound/trigger/size.rs index 346bf495..511ea981 100644 --- a/src/append/rolling_file/policy/compound/trigger/size.rs +++ b/src/append/rolling_file/policy/compound/trigger/size.rs @@ -60,7 +60,7 @@ where where E: de::Error, { - let (number, unit) = match v.find(|c: char| !c.is_digit(10)) { + let (number, unit) = match v.find(|c: char| !c.is_ascii_digit()) { Some(n) => (v[..n].trim(), Some(v[n..].trim())), None => (v.trim(), None), }; diff --git a/src/encode/mod.rs b/src/encode/mod.rs index aa290b3c..c8c757da 100644 --- a/src/encode/mod.rs +++ b/src/encode/mod.rs @@ -154,3 +154,16 @@ impl<'a, W: Write + ?Sized> Write for &'a mut W { ::set_style(*self, style) } } + +#[cfg(test)] +#[allow(dead_code)] +pub(crate) mod tests { + #[derive(Debug)] + pub struct DummyEncoder; + + impl super::Encode for DummyEncoder { + fn encode(&self, _w: &mut dyn super::Write, _record: &log::Record) -> anyhow::Result<()> { + Ok(()) + } + } +}