From 28b8fcae2433530796c010d903e65112f9aaecd7 Mon Sep 17 00:00:00 2001 From: TilCreator Date: Thu, 25 Feb 2021 16:15:48 +0100 Subject: [PATCH 01/11] Add Target::Pipe Now logs can be written to custom pipes and not just to stdout or stderr --- examples/custom_target.rs | 81 +++++++++++++++++++++++++ src/fmt/writer/mod.rs | 31 +++++++++- src/fmt/writer/termcolor/extern_impl.rs | 21 ++++++- src/lib.rs | 30 +++++++++ 4 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 examples/custom_target.rs diff --git a/examples/custom_target.rs b/examples/custom_target.rs new file mode 100644 index 00000000..9d82154d --- /dev/null +++ b/examples/custom_target.rs @@ -0,0 +1,81 @@ +/*! +Using `env_logger`. + +Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`: + +```no_run,shell +$ export MY_LOG_LEVEL='info' +``` + +Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors +or `auto` to enable them: + +```no_run,shell +$ export MY_LOG_STYLE=never +``` +*/ + +#[macro_use] +extern crate log; + +use env_logger::{Builder, Env}; +use std::{ + io, + sync::mpsc::{channel, Sender}, +}; + +// This struct is used as an adaptor, it implements io::Write and forwards the buffer to a mpsc::Sender +struct WriteAdapter { + sender: Sender, +} + +impl io::Write for WriteAdapter { + // On write we forward each u8 of the buffer to the sender and return the length of the buffer + fn write(&mut self, buf: &[u8]) -> io::Result { + for chr in buf { + self.sender.send(*chr).unwrap(); + } + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +fn main() { + // The `Env` lets us tweak what the environment + // variables to read are and what the default + // value is if they're missing + let env = Env::default() + .filter_or("MY_LOG_LEVEL", "trace") + // Normaly using a pipe as target would asume this as false, but this forces it to true + .write_style_or("MY_LOG_STYLE", "always"); + + // Create the channel for the log messages + let (rx, tx) = channel(); + + Builder::from_env(env) + // The Sender of the channel is given to the logger + // The wrapper is used, because Sender itself doesn't implement io::Write + .target_pipe(WriteAdapter { sender: rx }) + .init(); + + trace!("some trace log"); + debug!("some debug log"); + info!("some information log"); + warn!("some warning log"); + error!("some error log"); + + // Collect all messages send to the channel and parse the result as a string + String::from_utf8(tx.try_iter().collect::>()) + .unwrap() + // Split the result into lines so a prefix can be added to each line + .split("\n") + .for_each(|msg| { + // Print the message with a prefix if it has any content + if msg.len() > 0 { + println!("from pipe: {}", msg) + } + }); +} diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 6ee63a39..5526fa5c 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -3,7 +3,10 @@ mod termcolor; use self::atty::{is_stderr, is_stdout}; use self::termcolor::BufferWriter; -use std::{fmt, io}; +use std::{ + fmt, io, + sync::{Arc, Mutex}, +}; pub(in crate::fmt) mod glob { pub use super::termcolor::glob::*; @@ -14,11 +17,14 @@ pub(in crate::fmt) use self::termcolor::Buffer; /// Log target, either `stdout` or `stderr`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] pub enum Target { /// Logs will be sent to standard output. Stdout, /// Logs will be sent to standard error. Stderr, + /// Logs will be send to a custom pipe. + Pipe, } impl Default for Target { @@ -69,6 +75,7 @@ impl Writer { /// The target and style choice can be configured before building. pub(crate) struct Builder { target: Target, + target_pipe: Option>>, write_style: WriteStyle, is_test: bool, built: bool, @@ -79,6 +86,7 @@ impl Builder { pub(crate) fn new() -> Self { Builder { target: Default::default(), + target_pipe: None, write_style: Default::default(), is_test: false, built: false, @@ -87,7 +95,24 @@ impl Builder { /// Set the target to write to. pub(crate) fn target(&mut self, target: Target) -> &mut Self { + if let Target::Pipe = target { + panic!("Can not set target to Target::Pipe, use the target_pipe method for that"); + } self.target = target; + self.target_pipe = None; + self + } + + /// Set the target to write to a custom pipe. + /// + /// This can be used to send the log to a file directly or something more complex. + /// It is advertised to use a handle for log files, so that rollover and slow FSs are handled well. + pub(crate) fn target_pipe( + &mut self, + target_pipe: W, + ) -> &mut Self { + self.target_pipe = Some(Arc::new(Mutex::new(target_pipe))); + self.target = Target::Pipe; self } @@ -122,6 +147,7 @@ impl Builder { if match self.target { Target::Stderr => is_stderr(), Target::Stdout => is_stdout(), + Target::Pipe => false, } { WriteStyle::Auto } else { @@ -134,6 +160,9 @@ impl Builder { let writer = match self.target { Target::Stderr => BufferWriter::stderr(self.is_test, color_choice), Target::Stdout => BufferWriter::stdout(self.is_test, color_choice), + Target::Pipe => { + BufferWriter::pipe(color_choice, self.target_pipe.as_ref().unwrap().clone()) + } }; Writer { diff --git a/src/fmt/writer/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs index a6eaf425..6fd4fcf3 100644 --- a/src/fmt/writer/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; use std::fmt; use std::io::{self, Write}; use std::rc::Rc; +use std::sync::{Arc, Mutex}; use log::Level; use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; @@ -71,6 +72,7 @@ impl Formatter { pub(in crate::fmt::writer) struct BufferWriter { inner: termcolor::BufferWriter, test_target: Option, + target_pipe: Option>>, } pub(in crate::fmt) struct Buffer { @@ -83,6 +85,7 @@ impl BufferWriter { BufferWriter { inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), test_target: if is_test { Some(Target::Stderr) } else { None }, + target_pipe: None, } } @@ -90,6 +93,19 @@ impl BufferWriter { BufferWriter { inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()), test_target: if is_test { Some(Target::Stdout) } else { None }, + target_pipe: None, + } + } + + pub(in crate::fmt::writer) fn pipe( + write_style: WriteStyle, + target_pipe: Arc>, + ) -> Self { + BufferWriter { + // The inner Buffer is never printed from, but it is still needed to handle coloring and other formating + inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), + test_target: None, + target_pipe: Some(target_pipe), } } @@ -101,7 +117,9 @@ impl BufferWriter { } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { - if let Some(target) = self.test_target { + if let Some(pipe) = &self.target_pipe { + pipe.lock().unwrap().write_all(&buf.bytes()) + } else if let Some(target) = self.test_target { // This impl uses the `eprint` and `print` macros // instead of `termcolor`'s buffer. // This is so their output can be captured by `cargo test` @@ -110,6 +128,7 @@ impl BufferWriter { match target { Target::Stderr => eprint!("{}", log), Target::Stdout => print!("{}", log), + Target::Pipe => unreachable!(), } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 31ea7c3d..f478fdec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -709,6 +709,7 @@ impl Builder { /// Sets the target for the log output. /// /// Env logger can log to either stdout or stderr. The default is stderr. + /// If you want to set the target to a custom pipe, use [`target_pipe`], this will panic if given [`Target::Pipe`]. /// /// # Examples /// @@ -721,11 +722,40 @@ impl Builder { /// /// builder.target(Target::Stdout); /// ``` + /// + /// [`target_pipe`]: #method.target_pipe + /// [`Target::Pipe`]: enum.Target.html#variant.Pipe pub fn target(&mut self, target: fmt::Target) -> &mut Self { self.writer.target(target); self } + /// Sets the target for the log output to a custom pipe. + /// + /// Replaces the target set by [`target`] if executed and the other way around. + /// + /// This can be used to send the log to a file directly or something more complex. + /// It is advertised to use a wrapper for log files, so that rollover and slow FSs are handled well. + /// + /// # Examples + /// + /// Write log message to file `example.log`: + /// + /// ``` + /// use env_logger::{Builder}; + /// use std::fs::File; + /// + /// let mut builder = Builder::new(); + /// + /// builder.target_pipe(File::open("example.log").unwrap()); + /// ``` + /// + /// [`target`]: #method.target + pub fn target_pipe(&mut self, target_pipe: W) -> &mut Self { + self.writer.target_pipe(target_pipe); + self + } + /// Sets whether or not styles will be written. /// /// This can be useful in environments that don't support control characters From 31033f46fdf86a7819a15096b49766224878a0a1 Mon Sep 17 00:00:00 2001 From: TilCreator Date: Thu, 25 Feb 2021 20:41:58 +0100 Subject: [PATCH 02/11] Fix example of target_pipe method --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f478fdec..f95b3077 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -747,7 +747,9 @@ impl Builder { /// /// let mut builder = Builder::new(); /// - /// builder.target_pipe(File::open("example.log").unwrap()); + /// builder.target_pipe( + /// File::open("example.log").unwrap_or_else(|_| File::create("example.log").unwrap()), + /// ); /// ``` /// /// [`target`]: #method.target From fc0b51719b1a62ca955599a2aa2186a68484f45e Mon Sep 17 00:00:00 2001 From: TilCreator Date: Thu, 25 Feb 2021 20:53:33 +0100 Subject: [PATCH 03/11] Add Target::Pipe to the non termcolor BufferWrite --- src/fmt/writer/termcolor/shim_impl.rs | 48 +++++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index 563f8ad4..ebabba57 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -1,4 +1,7 @@ -use std::io; +use std::{ + io, + sync::{Arc, Mutex}, +}; use crate::fmt::{Target, WriteStyle}; @@ -6,6 +9,7 @@ pub(in crate::fmt::writer) mod glob {} pub(in crate::fmt::writer) struct BufferWriter { target: Target, + target_pipe: Option>>, } pub(in crate::fmt) struct Buffer(Vec); @@ -14,12 +18,24 @@ impl BufferWriter { pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { target: Target::Stderr, + target_pipe: None, } } pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { target: Target::Stdout, + target_pipe: None, + } + } + + pub(in crate::fmt::writer) fn pipe( + _write_style: WriteStyle, + target_pipe: Arc>, + ) -> Self { + BufferWriter { + target: Target::Pipe, + target_pipe: Some(target_pipe), } } @@ -28,17 +44,27 @@ impl BufferWriter { } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { - // This impl uses the `eprint` and `print` macros - // instead of using the streams directly. - // This is so their output can be captured by `cargo test` - let log = String::from_utf8_lossy(&buf.0); - - match self.target { - Target::Stderr => eprint!("{}", log), - Target::Stdout => print!("{}", log), - } + if let Target::Pipe = self.target { + self.target_pipe + .as_ref() + .unwrap() + .lock() + .unwrap() + .write_all(&buf.0) + } else { + // This impl uses the `eprint` and `print` macros + // instead of using the streams directly. + // This is so their output can be captured by `cargo test` + let log = String::from_utf8_lossy(&buf.0); - Ok(()) + match self.target { + Target::Stderr => eprint!("{}", log), + Target::Stdout => print!("{}", log), + Target::Pipe => unreachable!(), + } + + Ok(()) + } } } From aebf1c206a4ac519f0eb6fc31b2d3d1f54323226 Mon Sep 17 00:00:00 2001 From: TilCreator J Date: Thu, 25 Feb 2021 21:57:30 +0100 Subject: [PATCH 04/11] Fix typo Co-authored-by: Jonas Platte --- src/fmt/writer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 5526fa5c..325af024 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -23,7 +23,7 @@ pub enum Target { Stdout, /// Logs will be sent to standard error. Stderr, - /// Logs will be send to a custom pipe. + /// Logs will be sent to a custom pipe. Pipe, } From fe00534587eb64c070e1224b4bc76d972460947a Mon Sep 17 00:00:00 2001 From: TilCreator Date: Tue, 2 Mar 2021 21:59:36 +0100 Subject: [PATCH 05/11] remove target_pipe methode --- examples/custom_target.rs | 4 +- src/fmt/writer/mod.rs | 92 ++++++++++++++++--------- src/fmt/writer/termcolor/extern_impl.rs | 34 +++++---- src/lib.rs | 37 ++-------- 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/examples/custom_target.rs b/examples/custom_target.rs index 9d82154d..2f49bb3b 100644 --- a/examples/custom_target.rs +++ b/examples/custom_target.rs @@ -18,7 +18,7 @@ $ export MY_LOG_STYLE=never #[macro_use] extern crate log; -use env_logger::{Builder, Env}; +use env_logger::{Builder, Env, Target}; use std::{ io, sync::mpsc::{channel, Sender}, @@ -58,7 +58,7 @@ fn main() { Builder::from_env(env) // The Sender of the channel is given to the logger // The wrapper is used, because Sender itself doesn't implement io::Write - .target_pipe(WriteAdapter { sender: rx }) + .target(Target::Pipe(Box::new(WriteAdapter { sender: rx }))) .init(); trace!("some trace log"); diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 325af024..9f715e9c 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -15,8 +15,7 @@ pub(in crate::fmt) mod glob { pub(in crate::fmt) use self::termcolor::Buffer; -/// Log target, either `stdout` or `stderr`. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +/// Log target, either `stdout`, `stderr` or a custom pipe. #[non_exhaustive] pub enum Target { /// Logs will be sent to standard output. @@ -24,7 +23,7 @@ pub enum Target { /// Logs will be sent to standard error. Stderr, /// Logs will be sent to a custom pipe. - Pipe, + Pipe(Box), } impl Default for Target { @@ -33,6 +32,48 @@ impl Default for Target { } } +impl fmt::Debug for Target { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + write!( + f, + "{}", + match self { + Self::Stdout => "stdout", + Self::Stderr => "stderr", + Self::Pipe(_) => "pipe", + } + ) + } +} + +/// Log target, either `stdout`, `stderr` or a custom pipe. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] +pub(in crate::fmt) enum TargetType { + /// Logs will be sent to standard output. + Stdout, + /// Logs will be sent to standard error. + Stderr, + /// Logs will be sent to a custom pipe. + Pipe, +} + +impl From<&Target> for TargetType { + fn from(target: &Target) -> Self { + match target { + Target::Stdout => Self::Stdout, + Target::Stderr => Self::Stderr, + Target::Pipe(_) => Self::Pipe, + } + } +} + +impl Default for TargetType { + fn default() -> Self { + Self::from(&Target::default()) + } +} + /// Whether or not to print styles to the target. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum WriteStyle { @@ -74,7 +115,7 @@ impl Writer { /// /// The target and style choice can be configured before building. pub(crate) struct Builder { - target: Target, + target_type: TargetType, target_pipe: Option>>, write_style: WriteStyle, is_test: bool, @@ -85,7 +126,7 @@ impl Builder { /// Initialize the writer builder with defaults. pub(crate) fn new() -> Self { Builder { - target: Default::default(), + target_type: Default::default(), target_pipe: None, write_style: Default::default(), is_test: false, @@ -95,24 +136,11 @@ impl Builder { /// Set the target to write to. pub(crate) fn target(&mut self, target: Target) -> &mut Self { - if let Target::Pipe = target { - panic!("Can not set target to Target::Pipe, use the target_pipe method for that"); - } - self.target = target; - self.target_pipe = None; - self - } - - /// Set the target to write to a custom pipe. - /// - /// This can be used to send the log to a file directly or something more complex. - /// It is advertised to use a handle for log files, so that rollover and slow FSs are handled well. - pub(crate) fn target_pipe( - &mut self, - target_pipe: W, - ) -> &mut Self { - self.target_pipe = Some(Arc::new(Mutex::new(target_pipe))); - self.target = Target::Pipe; + self.target_type = TargetType::from(&target); + self.target_pipe = match target { + Target::Stdout | Target::Stderr => None, + Target::Pipe(pipe) => Some(Arc::new(Mutex::new(pipe))), + }; self } @@ -144,10 +172,10 @@ impl Builder { let color_choice = match self.write_style { WriteStyle::Auto => { - if match self.target { - Target::Stderr => is_stderr(), - Target::Stdout => is_stdout(), - Target::Pipe => false, + if match self.target_type { + TargetType::Stderr => is_stderr(), + TargetType::Stdout => is_stdout(), + TargetType::Pipe => false, } { WriteStyle::Auto } else { @@ -157,10 +185,10 @@ impl Builder { color_choice => color_choice, }; - let writer = match self.target { - Target::Stderr => BufferWriter::stderr(self.is_test, color_choice), - Target::Stdout => BufferWriter::stdout(self.is_test, color_choice), - Target::Pipe => { + let writer = match self.target_type { + TargetType::Stderr => BufferWriter::stderr(self.is_test, color_choice), + TargetType::Stdout => BufferWriter::stdout(self.is_test, color_choice), + TargetType::Pipe => { BufferWriter::pipe(color_choice, self.target_pipe.as_ref().unwrap().clone()) } }; @@ -181,7 +209,7 @@ impl Default for Builder { impl fmt::Debug for Builder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Logger") - .field("target", &self.target) + .field("target", &self.target_type) .field("write_style", &self.write_style) .finish() } diff --git a/src/fmt/writer/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs index 6fd4fcf3..f36237f5 100644 --- a/src/fmt/writer/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex}; use log::Level; use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; -use crate::fmt::{Formatter, Target, WriteStyle}; +use crate::fmt::{Formatter, TargetType, WriteStyle}; pub(in crate::fmt::writer) mod glob { pub use super::*; @@ -71,20 +71,24 @@ impl Formatter { pub(in crate::fmt::writer) struct BufferWriter { inner: termcolor::BufferWriter, - test_target: Option, + test_target_type: Option, target_pipe: Option>>, } pub(in crate::fmt) struct Buffer { inner: termcolor::Buffer, - test_target: Option, + test_target_type: Option, } impl BufferWriter { pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self { BufferWriter { inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), - test_target: if is_test { Some(Target::Stderr) } else { None }, + test_target_type: if is_test { + Some(TargetType::Stderr) + } else { + None + }, target_pipe: None, } } @@ -92,7 +96,11 @@ impl BufferWriter { pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self { BufferWriter { inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()), - test_target: if is_test { Some(Target::Stdout) } else { None }, + test_target_type: if is_test { + Some(TargetType::Stdout) + } else { + None + }, target_pipe: None, } } @@ -104,7 +112,7 @@ impl BufferWriter { BufferWriter { // The inner Buffer is never printed from, but it is still needed to handle coloring and other formating inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), - test_target: None, + test_target_type: None, target_pipe: Some(target_pipe), } } @@ -112,23 +120,23 @@ impl BufferWriter { pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { Buffer { inner: self.inner.buffer(), - test_target: self.test_target, + test_target_type: self.test_target_type, } } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { if let Some(pipe) = &self.target_pipe { pipe.lock().unwrap().write_all(&buf.bytes()) - } else if let Some(target) = self.test_target { + } else if let Some(target) = self.test_target_type { // This impl uses the `eprint` and `print` macros // instead of `termcolor`'s buffer. // This is so their output can be captured by `cargo test` let log = String::from_utf8_lossy(buf.bytes()); match target { - Target::Stderr => eprint!("{}", log), - Target::Stdout => print!("{}", log), - Target::Pipe => unreachable!(), + TargetType::Stderr => eprint!("{}", log), + TargetType::Stdout => print!("{}", log), + TargetType::Pipe => unreachable!(), } Ok(()) @@ -157,7 +165,7 @@ impl Buffer { fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { // Ignore styles for test captured logs because they can't be printed - if self.test_target.is_none() { + if self.test_target_type.is_none() { self.inner.set_color(spec) } else { Ok(()) @@ -166,7 +174,7 @@ impl Buffer { fn reset(&mut self) -> io::Result<()> { // Ignore styles for test captured logs because they can't be printed - if self.test_target.is_none() { + if self.test_target_type.is_none() { self.inner.reset() } else { Ok(()) diff --git a/src/lib.rs b/src/lib.rs index f95b3077..cfb14791 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -708,8 +708,10 @@ impl Builder { /// Sets the target for the log output. /// - /// Env logger can log to either stdout or stderr. The default is stderr. - /// If you want to set the target to a custom pipe, use [`target_pipe`], this will panic if given [`Target::Pipe`]. + /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr. + /// + /// The custom pipe can be used to send the log to a file directly or something more complex. + /// It is advertised to use a wrapper for log files, so that rollover and slow FSs are handled well. /// /// # Examples /// @@ -722,42 +724,11 @@ impl Builder { /// /// builder.target(Target::Stdout); /// ``` - /// - /// [`target_pipe`]: #method.target_pipe - /// [`Target::Pipe`]: enum.Target.html#variant.Pipe pub fn target(&mut self, target: fmt::Target) -> &mut Self { self.writer.target(target); self } - /// Sets the target for the log output to a custom pipe. - /// - /// Replaces the target set by [`target`] if executed and the other way around. - /// - /// This can be used to send the log to a file directly or something more complex. - /// It is advertised to use a wrapper for log files, so that rollover and slow FSs are handled well. - /// - /// # Examples - /// - /// Write log message to file `example.log`: - /// - /// ``` - /// use env_logger::{Builder}; - /// use std::fs::File; - /// - /// let mut builder = Builder::new(); - /// - /// builder.target_pipe( - /// File::open("example.log").unwrap_or_else(|_| File::create("example.log").unwrap()), - /// ); - /// ``` - /// - /// [`target`]: #method.target - pub fn target_pipe(&mut self, target_pipe: W) -> &mut Self { - self.writer.target_pipe(target_pipe); - self - } - /// Sets whether or not styles will be written. /// /// This can be useful in environments that don't support control characters From 6cfa0d9b20532228a56fdabfd1048c3bdce75648 Mon Sep 17 00:00:00 2001 From: TilCreator Date: Tue, 2 Mar 2021 22:13:34 +0100 Subject: [PATCH 06/11] Fix the non termcolor BufferWrite Forgot about it... again --- src/fmt/writer/termcolor/shim_impl.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index ebabba57..3f48d2cb 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -3,12 +3,12 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::fmt::{Target, WriteStyle}; +use crate::fmt::{TargetType, WriteStyle}; pub(in crate::fmt::writer) mod glob {} pub(in crate::fmt::writer) struct BufferWriter { - target: Target, + target: TargetType, target_pipe: Option>>, } @@ -17,14 +17,14 @@ pub(in crate::fmt) struct Buffer(Vec); impl BufferWriter { pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { - target: Target::Stderr, + target: TargetType::Stderr, target_pipe: None, } } pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { - target: Target::Stdout, + target: TargetType::Stdout, target_pipe: None, } } @@ -34,7 +34,7 @@ impl BufferWriter { target_pipe: Arc>, ) -> Self { BufferWriter { - target: Target::Pipe, + target: TargetType::Pipe, target_pipe: Some(target_pipe), } } @@ -44,7 +44,7 @@ impl BufferWriter { } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { - if let Target::Pipe = self.target { + if let TargetType::Pipe = self.target { self.target_pipe .as_ref() .unwrap() @@ -58,9 +58,9 @@ impl BufferWriter { let log = String::from_utf8_lossy(&buf.0); match self.target { - Target::Stderr => eprint!("{}", log), - Target::Stdout => print!("{}", log), - Target::Pipe => unreachable!(), + TargetType::Stderr => eprint!("{}", log), + TargetType::Stdout => print!("{}", log), + TargetType::Pipe => unreachable!(), } Ok(()) From 92fd1566265f4a0cee67e9ba0c687c95339cb3ea Mon Sep 17 00:00:00 2001 From: TilCreator J Date: Sat, 13 Mar 2021 23:05:25 +0100 Subject: [PATCH 07/11] Improve docs Co-authored-by: SirWindfield --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cfb14791..be9cf58f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -710,8 +710,8 @@ impl Builder { /// /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr. /// - /// The custom pipe can be used to send the log to a file directly or something more complex. - /// It is advertised to use a wrapper for log files, so that rollover and slow FSs are handled well. + /// The custom pipe can be used to send the log messages to a custom sink (for example a file). + /// Do note that direct writes to a file can become a bottleneck due to IO operation times. /// /// # Examples /// From b6673aea4906bda7940e2c394f89e977265bcab3 Mon Sep 17 00:00:00 2001 From: TilCreator J Date: Sat, 13 Mar 2021 23:05:42 +0100 Subject: [PATCH 08/11] Fix typo Co-authored-by: SirWindfield --- examples/custom_target.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom_target.rs b/examples/custom_target.rs index 2f49bb3b..805a3026 100644 --- a/examples/custom_target.rs +++ b/examples/custom_target.rs @@ -57,7 +57,7 @@ fn main() { Builder::from_env(env) // The Sender of the channel is given to the logger - // The wrapper is used, because Sender itself doesn't implement io::Write + // A wrapper is needed, because the `Sender` itself doesn't implement `std::io::Write`. .target(Target::Pipe(Box::new(WriteAdapter { sender: rx }))) .init(); From bda88f09807b99799e8ed215c04def75df695ffc Mon Sep 17 00:00:00 2001 From: TilCreator J Date: Sat, 13 Mar 2021 23:05:53 +0100 Subject: [PATCH 09/11] Fix typo Co-authored-by: SirWindfield --- examples/custom_target.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom_target.rs b/examples/custom_target.rs index 805a3026..766df464 100644 --- a/examples/custom_target.rs +++ b/examples/custom_target.rs @@ -49,7 +49,7 @@ fn main() { // value is if they're missing let env = Env::default() .filter_or("MY_LOG_LEVEL", "trace") - // Normaly using a pipe as target would asume this as false, but this forces it to true + // Normally using a pipe as a target would mean a value of false, but this forces it to be true. .write_style_or("MY_LOG_STYLE", "always"); // Create the channel for the log messages From 90f8a552f87a12037544f7dd8e2065ef7bda3486 Mon Sep 17 00:00:00 2001 From: TilCreator Date: Sat, 13 Mar 2021 23:24:44 +0100 Subject: [PATCH 10/11] Simplify function thx https://github.com/SirWindfield --- src/fmt/writer/termcolor/shim_impl.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index 3f48d2cb..89fb7802 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -44,27 +44,22 @@ impl BufferWriter { } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { - if let TargetType::Pipe = self.target { - self.target_pipe + // This impl uses the `eprint` and `print` macros + // instead of using the streams directly. + // This is so their output can be captured by `cargo test`. + match self.target { + // Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty. + TargetType::Pipe => self + .target_pipe .as_ref() .unwrap() .lock() .unwrap() - .write_all(&buf.0) - } else { - // This impl uses the `eprint` and `print` macros - // instead of using the streams directly. - // This is so their output can be captured by `cargo test` - let log = String::from_utf8_lossy(&buf.0); - - match self.target { - TargetType::Stderr => eprint!("{}", log), - TargetType::Stdout => print!("{}", log), - TargetType::Pipe => unreachable!(), - } - - Ok(()) + .write_all(&buf.0)?, + TargetType::Stdout => print!("{}", String::from_utf8_lossy(&buf.0)), + TargetType::Stderr => eprint!("{}", String::from_utf8_lossy(&buf.0)), } + Ok(()) } } From bac26d1c57c039d1e031dc44dc293273b0764449 Mon Sep 17 00:00:00 2001 From: TilCreator Date: Tue, 6 Apr 2021 20:23:52 +0200 Subject: [PATCH 11/11] Add nice newline --- src/fmt/writer/termcolor/shim_impl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index 89fb7802..a74e810c 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -59,6 +59,7 @@ impl BufferWriter { TargetType::Stdout => print!("{}", String::from_utf8_lossy(&buf.0)), TargetType::Stderr => eprint!("{}", String::from_utf8_lossy(&buf.0)), } + Ok(()) } }