diff --git a/src/builders.rs b/src/builders.rs index fdb6400..5e42a52 100644 --- a/src/builders.rs +++ b/src/builders.rs @@ -97,7 +97,7 @@ use crate::{Syslog4Rfc3164Logger, Syslog4Rfc5424Logger}; #[must_use = "this is only a logger configuration and must be consumed with into_log() or apply()"] pub struct Dispatch { format: Option>, - children: Vec, + children: Vec>, default_level: log::LevelFilter, levels: Vec<(Cow<'static, str>, log::LevelFilter)>, filters: Vec>, @@ -115,7 +115,19 @@ pub struct Dispatch { #[derive(Clone)] pub struct SharedDispatch { inner: Arc, - min_level: log::LevelFilter, +} + +impl Into> for SharedDispatch { + fn into(self) -> Box { + if self.inner.max_level() == log::LevelFilter::Off { + Box::new(log_impl::Null) + } else { + Box::new(log_impl::LogWrapper::<_, log_impl::Dispatch>( + self.inner, + Default::default(), + )) + } + } } impl Dispatch { @@ -389,11 +401,8 @@ impl Dispatch { /// /// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html pub fn into_shared(self) -> SharedDispatch { - let (min_level, dispatch) = self.into_dispatch(); - SharedDispatch { - inner: Arc::new(dispatch), - min_level, + inner: Arc::new(self.into_dispatch()), } } @@ -401,7 +410,7 @@ impl Dispatch { /// /// This could probably be refactored, but having everything in one place /// is also nice. - fn into_dispatch(self) -> (log::LevelFilter, log_impl::Dispatch) { + fn into_dispatch(self) -> log_impl::Dispatch { let Dispatch { format, children, @@ -414,90 +423,10 @@ impl Dispatch { let output = children .into_iter() - .filter_map(|child| match child { - OutputInner::Stdout(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child) as Box) - } - OutputInner::Stderr(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - OutputInner::File(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - OutputInner::Writer(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - #[cfg(all(not(windows), feature = "reopen-03"))] - OutputInner::Reopen(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - OutputInner::Sender(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - #[cfg(all(not(windows), feature = "syslog-3"))] - OutputInner::Syslog3(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - #[cfg(all(not(windows), feature = "syslog-4"))] - OutputInner::Syslog4Rfc3164(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - #[cfg(all(not(windows), feature = "syslog-4"))] - OutputInner::Syslog4Rfc5424(child) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child)) - } - OutputInner::Panic => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(log_impl::Panic)) - } - OutputInner::Dispatch(child_dispatch) => { - let (child_level, child) = child_dispatch.into_dispatch(); - if child_level > log::LevelFilter::Off { - max_child_level = cmp::max(max_child_level, child_level); - Some(Box::new(child)) - } else { - None - } - } - OutputInner::SharedDispatch(child_dispatch) => { - let SharedDispatch { - inner: child, - min_level: child_level, - } = child_dispatch; - - if child_level > log::LevelFilter::Off { - max_child_level = cmp::max(max_child_level, child_level); - Some(Box::new(log_impl::LogWrapper::<_, log_impl::Dispatch>( - child, - Default::default(), - ))) - } else { - None - } - } - OutputInner::OtherBoxed(child_log) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(child_log)) - } - OutputInner::OtherStatic(child_log) => { - max_child_level = log::LevelFilter::Trace; - Some(Box::new(log_impl::LogRef(child_log))) - } - #[cfg(feature = "date-based")] - OutputInner::DateBased(child) => { - max_child_level = log::LevelFilter::Trace; - - Some(Box::new(child)) - } + .filter_map(|child| { + let child_level = child.max_level(); + max_child_level = max_child_level.max(child_level); + (child_level > log::LevelFilter::Off).then(|| child) }) .collect(); @@ -510,15 +439,14 @@ impl Dispatch { filters.shrink_to_fit(); - let dispatch = log_impl::Dispatch { - output: output, - default_level: default_level, + log_impl::Dispatch { + output, + default_level, + max_level: real_min, levels: levels.into(), - format: format, - filters: filters, - }; - - (real_min, dispatch) + format, + filters, + } } /// Builds this logger into a `Box` and calculates the minimum @@ -546,7 +474,8 @@ impl Dispatch { /// # } /// ``` pub fn into_log(self) -> (log::LevelFilter, Box) { - let (level, logger) = self.into_dispatch(); + let logger = self.into_dispatch(); + let level = logger.max_level(); if level == log::LevelFilter::Off { (level, Box::new(log_impl::Null)) } else { @@ -554,6 +483,11 @@ impl Dispatch { } } + /// TODO + pub fn into_log_ext(self) -> Box { + self.into() + } + /// Builds this logger and instantiates it as the global [`log`] logger. /// /// # Errors: @@ -572,46 +506,36 @@ impl Dispatch { } } -/// This enum contains various outputs that you can send messages to. -enum OutputInner { - /// Prints all messages to stdout with `line_sep` separator. - Stdout(log_impl::Stdout), - /// Prints all messages to stderr with `line_sep` separator. - Stderr(log_impl::Stderr), - /// Writes all messages to file with `line_sep` separator. - File(log_impl::File), - /// Writes all messages to the writer with `line_sep` separator. - Writer(log_impl::Writer), - /// Writes all messages to the reopen::Reopen file with `line_sep` - /// separator. - #[cfg(all(not(windows), feature = "reopen-03"))] - Reopen(log_impl::Reopen), - /// Writes all messages to mpst::Sender with `line_sep` separator. - Sender(log_impl::Sender), - /// Passes all messages to other dispatch. - Dispatch(Dispatch), - /// Passes all messages to other dispatch that's shared. - SharedDispatch(SharedDispatch), - /// Passes all messages to other logger. - OtherBoxed(Box), - /// Passes all messages to other logger. - OtherStatic(&'static dyn Log), - /// Passes all messages to the syslog. - #[cfg(all(not(windows), feature = "syslog-3"))] - Syslog3(log_impl::Syslog3), - /// Passes all messages to the syslog. - #[cfg(all(not(windows), feature = "syslog-4"))] - Syslog4Rfc3164(log_impl::Syslog4Rfc3164), - /// Sends all messages through the transform then passes to the syslog. - #[cfg(all(not(windows), feature = "syslog-4"))] - Syslog4Rfc5424(log_impl::Syslog4Rfc5424), - /// Panics with messages text for all messages. - Panic, - /// File logger with custom date and timestamp suffix in file name. - #[cfg(feature = "date-based")] - DateBased(log_impl::DateBased), +impl Into> for Dispatch { + fn into(self) -> Box { + let logger = self.into_dispatch(); + if logger.max_level() == log::LevelFilter::Off { + Box::new(log_impl::Null) + } else { + Box::new(logger) + } + } } +// ########### Orphaned documentation ########### +// TODO give it a new home +/// Prints all messages to stdout with `line_sep` separator. +/// Prints all messages to stderr with `line_sep` separator. +/// Writes all messages to file with `line_sep` separator. +/// Writes all messages to the writer with `line_sep` separator. +/// Writes all messages to the reopen::Reopen file with `line_sep` +/// separator. +/// Writes all messages to mpst::Sender with `line_sep` separator. +/// Passes all messages to other dispatch. +/// Passes all messages to other dispatch that's shared. +/// Passes all messages to other logger. +/// Passes all messages to other logger. +/// Passes all messages to the syslog. +/// Passes all messages to the syslog. +/// Sends all messages through the transform then passes to the syslog. +/// Panics with messages text for all messages. +/// File logger with custom date and timestamp suffix in file name. + /// Logger which will panic whenever anything is logged. The panic /// will be exactly the message of the log. /// @@ -673,36 +597,65 @@ pub trait LogExt: Log { } } -impl LogExt for T {} +// This ought to be a blanket trait implementation, but it won't work until we get impl specialization +pub struct LogExtWrapper(T); + +impl Log for LogExtWrapper { + fn enabled(&self, metadata: &log::Metadata) -> bool { + self.0.enabled(metadata) + } + fn log(&self, record: &log::Record) { + self.0.log(record) + } + fn flush(&self) { + self.0.flush() + } +} + +impl LogExt for LogExtWrapper {} /// Configuration for a logger output. -pub struct Output(OutputInner); +pub struct Output(Box); impl From for Output { /// Creates an output logger forwarding all messages to the dispatch. fn from(log: Dispatch) -> Self { - Output(OutputInner::Dispatch(log)) + Output(log.into()) } } impl From for Output { /// Creates an output logger forwarding all messages to the dispatch. fn from(log: SharedDispatch) -> Self { - Output(OutputInner::SharedDispatch(log)) + Output(log.into()) } } impl From> for Output { /// Creates an output logger forwarding all messages to the custom logger. fn from(log: Box) -> Self { - Output(OutputInner::OtherBoxed(log)) + Output(Box::new(LogExtWrapper(log))) // TODO we wouldn't need an additional wrapper if we could cast trait objects + } +} + +impl From> for Output { + /// Creates an output logger forwarding all messages to the custom logger. + fn from(log: Box) -> Self { + Output(log) } } impl From<&'static dyn Log> for Output { /// Creates an output logger forwarding all messages to the custom logger. fn from(log: &'static dyn Log) -> Self { - Output(OutputInner::OtherStatic(log)) + Output(Box::new(LogExtWrapper(log_impl::LogRef(log)))) + } +} + +impl From<&'static dyn LogExt> for Output { + /// Creates an output logger forwarding all messages to the custom logger. + fn from(log: &'static dyn LogExt) -> Self { + Output(Box::new(log_impl::LogExtRef(log))) } } @@ -712,7 +665,7 @@ impl From for Output { /// /// File writes are buffered and flushed once per log record. fn from(file: fs::File) -> Self { - Output(OutputInner::File(log_impl::File { + Output(Box::new(log_impl::File { stream: Mutex::new(io::BufWriter::new(file)), line_sep: "\n".into(), })) @@ -727,7 +680,7 @@ impl From> for Output { /// needed (eg. wrap it in `BufWriter`). However, flush is called after /// each log record. fn from(writer: Box) -> Self { - Output(OutputInner::Writer(log_impl::Writer { + Output(Box::new(log_impl::Writer { stream: Mutex::new(writer), line_sep: "\n".into(), })) @@ -739,7 +692,7 @@ impl From> for Output { /// Creates an output logger which writes all messages to the file contained /// in the Reopen struct, using `\n` as the separator. fn from(reopen: reopen::Reopen) -> Self { - Output(OutputInner::Reopen(log_impl::Reopen { + Output(Box::new(log_impl::Reopen { stream: Mutex::new(reopen), line_sep: "\n".into(), })) @@ -750,7 +703,7 @@ impl From for Output { /// Creates an output logger which writes all messages to stdout with the /// given handle and `\n` as the separator. fn from(stream: io::Stdout) -> Self { - Output(OutputInner::Stdout(log_impl::Stdout { + Output(Box::new(log_impl::Stdout { stream, line_sep: "\n".into(), })) @@ -761,7 +714,7 @@ impl From for Output { /// Creates an output logger which writes all messages to stderr with the /// given handle and `\n` as the separator. fn from(stream: io::Stderr) -> Self { - Output(OutputInner::Stderr(log_impl::Stderr { + Output(Box::new(log_impl::Stderr { stream, line_sep: "\n".into(), })) @@ -774,7 +727,7 @@ impl From> for Output { /// /// All messages sent to the mpsc channel are suffixed with '\n'. fn from(stream: Sender) -> Self { - Output(OutputInner::Sender(log_impl::Sender { + Output(Box::new(log_impl::Sender { stream: Mutex::new(stream), line_sep: "\n".into(), })) @@ -791,7 +744,7 @@ impl From for Output { /// /// This requires the `"syslog-3"` feature. fn from(log: syslog3::Logger) -> Self { - Output(OutputInner::Syslog3(log_impl::Syslog3 { inner: log })) + Output(Box::new(log_impl::Syslog3 { inner: log })) } } @@ -810,7 +763,7 @@ impl From> for Output { /// /// This requires the `"syslog-3"` feature. fn from(log: Box) -> Self { - Output(OutputInner::Syslog3(log_impl::Syslog3 { inner: *log })) + Output(Box::new(log_impl::Syslog3 { inner: *log })) } } @@ -829,7 +782,7 @@ impl From for Output { /// /// This requires the `"syslog-4"` feature. fn from(log: Syslog4Rfc3164Logger) -> Self { - Output(OutputInner::Syslog4Rfc3164(log_impl::Syslog4Rfc3164 { + Output(Box::new(log_impl::Syslog4Rfc3164 { inner: Mutex::new(log), })) } @@ -839,7 +792,7 @@ impl From for Output { /// Creates an output logger which will panic with message text for all /// messages. fn from(_: Panic) -> Self { - Output(OutputInner::Panic) + Output(Box::new(log_impl::Panic)) } } @@ -885,7 +838,7 @@ impl Output { /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain /// [`fern::log_file`]: fn.log_file.html pub fn file>>(file: fs::File, line_sep: T) -> Self { - Output(OutputInner::File(log_impl::File { + Output(Box::new(log_impl::File { stream: Mutex::new(io::BufWriter::new(file)), line_sep: line_sep.into(), })) @@ -927,7 +880,7 @@ impl Output { /// /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain pub fn writer>>(writer: Box, line_sep: T) -> Self { - Output(OutputInner::Writer(log_impl::Writer { + Output(Box::new(log_impl::Writer { stream: Mutex::new(writer), line_sep: line_sep.into(), })) @@ -966,7 +919,7 @@ impl Output { reopen: reopen::Reopen, line_sep: T, ) -> Self { - Output(OutputInner::Reopen(log_impl::Reopen { + Output(Box::new(log_impl::Reopen { stream: Mutex::new(reopen), line_sep: line_sep.into(), })) @@ -992,7 +945,7 @@ impl Output { /// # .into_log(); /// ``` pub fn stdout>>(line_sep: T) -> Self { - Output(OutputInner::Stdout(log_impl::Stdout { + Output(Box::new(log_impl::Stdout { stream: io::stdout(), line_sep: line_sep.into(), })) @@ -1015,7 +968,7 @@ impl Output { /// # .into_log(); /// ``` pub fn stderr>>(line_sep: T) -> Self { - Output(OutputInner::Stderr(log_impl::Stderr { + Output(Box::new(log_impl::Stderr { stream: io::stderr(), line_sep: line_sep.into(), })) @@ -1038,7 +991,7 @@ impl Output { /// # .into_log(); /// ``` pub fn sender>>(sender: Sender, line_sep: T) -> Self { - Output(OutputInner::Sender(log_impl::Sender { + Output(Box::new(log_impl::Sender { stream: Mutex::new(sender), line_sep: line_sep.into(), })) @@ -1066,7 +1019,7 @@ impl Output { + Send + 'static, { - Output(OutputInner::Syslog4Rfc5424(log_impl::Syslog4Rfc5424 { + Output(Box::new(log_impl::Syslog4Rfc5424 { inner: Mutex::new(logger), transform: Box::new(transform), })) @@ -1142,7 +1095,7 @@ impl fmt::Debug for Dispatch { "format", &self.format.as_ref().map(|_| ""), ) - .field("children", &self.children) + .field("children_count", &self.children.len()) .field("default_level", &self.default_level) .field("levels", &LevelsDebug(&self.levels)) .field("filters", &FiltersDebug(&self.filters)) @@ -1150,95 +1103,9 @@ impl fmt::Debug for Dispatch { } } -impl fmt::Debug for OutputInner { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - OutputInner::Stdout(log_impl::Stdout { - ref stream, - ref line_sep, - }) => f - .debug_struct("Output::Stdout") - .field("stream", stream) - .field("line_sep", line_sep) - .finish(), - OutputInner::Stderr(log_impl::Stderr { - ref stream, - ref line_sep, - }) => f - .debug_struct("Output::Stderr") - .field("stream", stream) - .field("line_sep", line_sep) - .finish(), - OutputInner::File(log_impl::File { - ref stream, - ref line_sep, - }) => f - .debug_struct("Output::File") - .field("stream", stream) - .field("line_sep", line_sep) - .finish(), - OutputInner::Writer(log_impl::Writer { ref line_sep, .. }) => f - .debug_struct("Output::Writer") - .field("stream", &"") - .field("line_sep", line_sep) - .finish(), - #[cfg(all(not(windows), feature = "reopen-03"))] - OutputInner::Reopen(log_impl::Reopen { ref line_sep, .. }) => f - .debug_struct("Output::Reopen") - .field("stream", &"") - .field("line_sep", line_sep) - .finish(), - OutputInner::Sender(log_impl::Sender { - ref stream, - ref line_sep, - }) => f - .debug_struct("Output::Sender") - .field("stream", stream) - .field("line_sep", line_sep) - .finish(), - #[cfg(all(not(windows), feature = "syslog-3"))] - OutputInner::Syslog3(_) => f - .debug_tuple("Output::Syslog3") - .field(&"") - .finish(), - #[cfg(all(not(windows), feature = "syslog-4"))] - OutputInner::Syslog4Rfc3164 { .. } => f - .debug_tuple("Output::Syslog4Rfc3164") - .field(&"") - .finish(), - #[cfg(all(not(windows), feature = "syslog-4"))] - OutputInner::Syslog4Rfc5424 { .. } => f - .debug_tuple("Output::Syslog4Rfc5424") - .field(&"") - .finish(), - OutputInner::Dispatch(ref dispatch) => { - f.debug_tuple("Output::Dispatch").field(dispatch).finish() - } - OutputInner::SharedDispatch(_) => f - .debug_tuple("Output::SharedDispatch") - .field(&"") - .finish(), - OutputInner::OtherBoxed { .. } => f - .debug_tuple("Output::OtherBoxed") - .field(&"") - .finish(), - OutputInner::OtherStatic { .. } => f - .debug_tuple("Output::OtherStatic") - .field(&"") - .finish(), - OutputInner::Panic => f.debug_tuple("Output::Panic").finish(), - #[cfg(feature = "date-based")] - OutputInner::DateBased(log_impl::DateBased { ref config, .. }) => f - .debug_struct("Output::DateBased") - .field("config", config) - .finish(), - } - } -} - impl fmt::Debug for Output { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) + f.write_str("Output") } } @@ -1423,7 +1290,7 @@ impl From for Output { // ignore errors - we'll just retry later. let initial_file = config.open_current_log_file(&computed_suffix).ok(); - Output(OutputInner::DateBased(log_impl::DateBased { + Output(Box::new(log_impl::DateBased { config, state: Mutex::new(DateBasedState::new(computed_suffix, initial_file)), })) diff --git a/src/log_impl.rs b/src/log_impl.rs index 8c7118f..636d438 100644 --- a/src/log_impl.rs +++ b/src/log_impl.rs @@ -1,3 +1,4 @@ +use crate::builders::LogExt; use std::borrow::Borrow; use std::{ borrow::Cow, @@ -30,8 +31,10 @@ pub enum LevelConfiguration { } pub struct Dispatch { - pub output: Vec>, + pub output: Vec>, pub default_level: log::LevelFilter, + /// The actual maximum level computed based on the bounds of all children. See [`LogExt::max_leevel`] + pub max_level: log::LevelFilter, pub levels: LevelConfiguration, pub format: Option>, pub filters: Vec>, @@ -125,9 +128,29 @@ impl Log for LogRef { } } +pub struct LogExtRef(pub &'static dyn LogExt); + +impl Log for LogExtRef { + fn enabled(&self, meta: &log::Metadata<'_>) -> bool { + self.0.enabled(meta) + } + fn log(&self, record: &log::Record<'_>) { + self.0.log(record) + } + fn flush(&self) { + self.0.flush() + } +} + +impl LogExt for LogExtRef { + fn max_level(&self) -> log::LevelFilter { + self.0.max_level() + } +} + // TODO this causes an unnecessary double indirect call. // See https://github.com/rust-lang/log/issues/458 -pub struct LogWrapper, R: Log>(pub T, pub std::marker::PhantomData); +pub struct LogWrapper, R: Log + ?Sized>(pub T, pub std::marker::PhantomData); impl Log for LogWrapper where @@ -145,6 +168,16 @@ where } } +impl LogExt for LogWrapper +where + T: Borrow + Send + Sync, + R: LogExt, +{ + fn max_level(&self) -> log::LevelFilter { + self.0.borrow().max_level() + } +} + pub struct Panic; pub struct Null; @@ -317,6 +350,12 @@ impl Log for Null { fn flush(&self) {} } +impl LogExt for Null { + fn max_level(&self) -> log::LevelFilter { + log::LevelFilter::Off + } +} + impl Log for Dispatch { fn enabled(&self, metadata: &log::Metadata) -> bool { self.deep_enabled(metadata) @@ -358,6 +397,12 @@ impl Log for Dispatch { } } +impl LogExt for Dispatch { + fn max_level(&self) -> log::LevelFilter { + self.max_level + } +} + impl Dispatch { fn finish_logging(&self, record: &log::Record) { for log in &self.output { @@ -455,6 +500,8 @@ macro_rules! std_log_impl { let _ = self.stream.lock().flush(); } } + + impl LogExt for $ident {} }; } @@ -501,6 +548,8 @@ macro_rules! writer_log_impl { .flush(); } } + + impl LogExt for $ident {} }; } @@ -529,6 +578,8 @@ impl Log for Sender { fn flush(&self) {} } +impl LogExt for Sender {} + #[cfg(any(feature = "syslog-3", feature = "syslog-4"))] macro_rules! send_syslog { ($logger:expr, $level:expr, $message:expr) => { @@ -559,6 +610,9 @@ impl Log for Syslog3 { fn flush(&self) {} } +#[cfg(all(not(windows), feature = "syslog-3"))] +impl LogExt for Syslog3 {} + #[cfg(all(not(windows), feature = "syslog-4"))] impl Log for Syslog4Rfc3164 { fn enabled(&self, _: &log::Metadata) -> bool { @@ -577,6 +631,9 @@ impl Log for Syslog4Rfc3164 { fn flush(&self) {} } +#[cfg(all(not(windows), feature = "syslog-4"))] +impl LogExt for Syslog4Rfc3164 {} + #[cfg(all(not(windows), feature = "syslog-4"))] impl Log for Syslog4Rfc5424 { fn enabled(&self, _: &log::Metadata) -> bool { @@ -595,6 +652,9 @@ impl Log for Syslog4Rfc5424 { fn flush(&self) {} } +#[cfg(all(not(windows), feature = "syslog-4"))] +impl LogExt for Syslog4Rfc5424 {} + impl Log for Panic { fn enabled(&self, _: &log::Metadata) -> bool { true @@ -607,6 +667,8 @@ impl Log for Panic { fn flush(&self) {} } +impl LogExt for Panic {} + #[cfg(feature = "date-based")] impl Log for DateBased { fn enabled(&self, _: &log::Metadata) -> bool { @@ -662,6 +724,9 @@ impl Log for DateBased { } } +#[cfg(feature = "date-based")] +impl LogExt for DateBased {} + #[inline(always)] fn fallback_on_error(record: &log::Record, log_func: F) where