From ff856469db251cec5c95f9c9ec6acbd588c8be22 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 9 Mar 2018 13:31:15 +1000 Subject: [PATCH 01/17] adjust the default format --- src/lib.rs | 71 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 87459c3a..17f4d3b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,40 +270,65 @@ impl Format { } else { Box::new(move |buf, record| { - let write_level = if self.default_format_level { + let mut brace_style = buf.style(); + brace_style.set_color(Color::Black).set_intense(true); + + // Write the start of a header value + let mut written_header_value = false; + let write_header_pre = |buf: &mut Formatter, sentinal: &mut bool| { + if !*sentinal { + *sentinal = true; + write!(buf, "{}", brace_style.value("[")) + } + else { + write!(buf, " ") + } + }; + + let mut write = Ok(()); + + // Write the record level + if self.default_format_level { + write = write.and(write_header_pre(buf, &mut written_header_value)); + let level = record.level(); let mut level_style = buf.style(); - match level { - Level::Trace => level_style.set_color(Color::White), - Level::Debug => level_style.set_color(Color::Blue), - Level::Info => level_style.set_color(Color::Green), - Level::Warn => level_style.set_color(Color::Yellow), - Level::Error => level_style.set_color(Color::Red).set_bold(true), + let level = match level { + Level::Trace => level_style.set_color(Color::White).value("TRC"), + Level::Debug => level_style.set_color(Color::Blue).value("DBG"), + Level::Info => level_style.set_color(Color::Green).value("INF"), + Level::Warn => level_style.set_color(Color::Yellow).value("WRN"), + Level::Error => level_style.set_color(Color::Red).set_bold(true).value("ERR"), }; - write!(buf, "{:>5} ", level_style.value(level)) - } else { - Ok(()) - }; + write = write.and(write!(buf, "{}", level)); + } + + // Write the timestamp + if self.default_format_timestamp { + write = write.and(write_header_pre(buf, &mut written_header_value)); - let write_ts = if self.default_format_timestamp { let ts = buf.timestamp(); - write!(buf, "{}: ", ts) - } else { - Ok(()) - }; + write = write.and(write!(buf, "{}", ts)); + } + // Write the module path let default_format_module_path = (self.default_format_module_path, record.module_path()); - let write_module_path = if let (true, Some(module_path)) = default_format_module_path { - write!(buf, "{}: ", module_path) - } else { - Ok(()) - }; + if let (true, Some(module_path)) = default_format_module_path { + write = write.and(write_header_pre(buf, &mut written_header_value)); + + write = write.and(write!(buf, "{}", module_path)); + } + + if written_header_value { + write = write.and(write!(buf, "{} ", brace_style.value("]"))); + } - let write_args = writeln!(buf, "{}", record.args()); + // Write the record arguments + write = write.and(writeln!(buf, "{}", record.args())); - write_level.and(write_ts).and(write_module_path).and(write_args) + write }) } } From dce40eac13f26d2e9ae6ddf0024a306daf98b012 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 2 Apr 2018 14:39:06 +1000 Subject: [PATCH 02/17] tweak default fmt --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 17f4d3b7..00a51a49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,6 +287,14 @@ impl Format { let mut write = Ok(()); + // Write the timestamp + if self.default_format_timestamp { + write = write.and(write_header_pre(buf, &mut written_header_value)); + + let ts = buf.timestamp(); + write = write.and(write!(buf, "{}", ts)); + } + // Write the record level if self.default_format_level { write = write.and(write_header_pre(buf, &mut written_header_value)); @@ -305,14 +313,6 @@ impl Format { write = write.and(write!(buf, "{}", level)); } - // Write the timestamp - if self.default_format_timestamp { - write = write.and(write_header_pre(buf, &mut written_header_value)); - - let ts = buf.timestamp(); - write = write.and(write!(buf, "{}", ts)); - } - // Write the module path let default_format_module_path = (self.default_format_module_path, record.module_path()); if let (true, Some(module_path)) = default_format_module_path { From 6353f16039c868cb96ac7a031dc2a61d5eb4bf68 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 16 Apr 2018 09:55:43 +1000 Subject: [PATCH 03/17] add helper method for styling levels --- src/fmt.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 13 ++----------- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 383f0fca..1a36a5d9 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -37,9 +37,11 @@ use std::io::prelude::*; use std::{io, fmt}; use std::rc::Rc; +use std::borrow::Cow; use std::cell::RefCell; use std::time::SystemTime; +use log::Level; use termcolor::{ColorSpec, ColorChoice, Buffer, BufferWriter, WriteColor}; use atty; use humantime::format_rfc3339_seconds; @@ -135,7 +137,7 @@ pub struct Style { /// /// [`Style::value`]: struct.Style.html#method.value pub struct StyledValue<'a, T> { - style: &'a Style, + style: Cow<'a, Style>, value: T, } @@ -396,7 +398,14 @@ impl Style { /// ``` pub fn value(&self, value: T) -> StyledValue { StyledValue { - style: &self, + style: Cow::Borrowed(self), + value + } + } + + fn into_value(self, value: T) -> StyledValue<'static, T> { + StyledValue { + style: Cow::Owned(self), value } } @@ -468,6 +477,50 @@ impl Formatter { Timestamp(SystemTime::now()) } + /// Get a styled log level. + /// + /// # Examples + /// + /// Include a styled level with the log record: + /// + /// ``` + /// use std::io::Write; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let level = buf.level_style(record.level()); + /// + /// writeln!(buf, "{}: {}", level, record.args()) + /// }); + /// ``` + pub fn level_style(&self, level: Level) -> StyledValue<'static, &'static str> { + let mut level_style = self.style(); + + match level { + Level::Trace => { + level_style.set_color(Color::White); + level_style.into_value("TRC") + }, + Level::Debug => { + level_style.set_color(Color::Blue); + level_style.into_value("DBG") + }, + Level::Info => { + level_style.set_color(Color::Green); + level_style.into_value("INF") + }, + Level::Warn => { + level_style.set_color(Color::Yellow); + level_style.into_value("WRN") + }, + Level::Error => { + level_style.set_color(Color::Red).set_bold(true); + level_style.into_value("ERR") + }, + } + } + pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { writer.inner.print(&self.buf.borrow()) } diff --git a/src/lib.rs b/src/lib.rs index 00a51a49..dc7ac9be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,7 +191,7 @@ use std::io; use std::mem; use std::cell::RefCell; -use log::{Log, LevelFilter, Level, Record, SetLoggerError, Metadata}; +use log::{Log, LevelFilter, Record, SetLoggerError, Metadata}; pub mod filter; pub mod fmt; @@ -299,16 +299,7 @@ impl Format { if self.default_format_level { write = write.and(write_header_pre(buf, &mut written_header_value)); - let level = record.level(); - let mut level_style = buf.style(); - - let level = match level { - Level::Trace => level_style.set_color(Color::White).value("TRC"), - Level::Debug => level_style.set_color(Color::Blue).value("DBG"), - Level::Info => level_style.set_color(Color::Green).value("INF"), - Level::Warn => level_style.set_color(Color::Yellow).value("WRN"), - Level::Error => level_style.set_color(Color::Red).set_bold(true).value("ERR"), - }; + let level = buf.level_style(record.level()); write = write.and(write!(buf, "{}", level)); } From 715c8b2444914406153e8545c2d0eaa1e1b15c03 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 16 Apr 2018 10:04:12 +1000 Subject: [PATCH 04/17] shif the format into the fmt module --- src/fmt.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 88 +----------------------------------------------------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 1ba3d924..0de7b8e8 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -43,11 +43,97 @@ use std::error::Error; use std::cell::RefCell; use std::time::SystemTime; -use log::Level; +use log::{Level, Record}; use termcolor::{self, ColorSpec, ColorChoice, Buffer, BufferWriter, WriteColor}; use atty; use humantime::format_rfc3339_seconds; +/// A format to write log records with a configurable default. +pub(crate) struct Format { + pub(crate) default_format_timestamp: bool, + pub(crate) default_format_module_path: bool, + pub(crate) default_format_level: bool, + pub(crate) custom_format: Option io::Result<()> + Sync + Send>>, +} + +impl Default for Format { + fn default() -> Self { + Format { + default_format_timestamp: true, + default_format_module_path: true, + default_format_level: true, + custom_format: None, + } + } +} + +impl Format { + /// Convert the format into a callable function. + /// + /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. + /// If the `custom_format` is `None`, then a default format is returned. + /// Any `default_format` switches set to `false` won't be written by the format. + pub(crate) fn into_boxed_fn(self) -> Box io::Result<()> + Sync + Send> { + if let Some(fmt) = self.custom_format { + fmt + } + else { + Box::new(move |buf, record| { + let mut brace_style = buf.style(); + brace_style.set_color(Color::Black).set_intense(true); + + // Write the start of a header value + let mut written_header_value = false; + let write_header_pre = |buf: &mut Formatter, sentinal: &mut bool| { + if !*sentinal { + *sentinal = true; + write!(buf, "{}", brace_style.value("[")) + } + else { + write!(buf, " ") + } + }; + + let mut write = Ok(()); + + // Write the timestamp + if self.default_format_timestamp { + write = write.and(write_header_pre(buf, &mut written_header_value)); + + let ts = buf.timestamp(); + write = write.and(write!(buf, "{}", ts)); + } + + // Write the record level + if self.default_format_level { + write = write.and(write_header_pre(buf, &mut written_header_value)); + + let level = buf.level_style(record.level()); + + write = write.and(write!(buf, "{}", level)); + } + + // Write the module path + let default_format_module_path = (self.default_format_module_path, record.module_path()); + if let (true, Some(module_path)) = default_format_module_path { + write = write.and(write_header_pre(buf, &mut written_header_value)); + + write = write.and(write!(buf, "{}", module_path)); + } + + if written_header_value { + write = write.and(write!(buf, "{} ", brace_style.value("]"))); + } + + // Write the record arguments + write = write.and(writeln!(buf, "{}", record.args())); + + write + }) + } + } +} + /// A formatter to write logs into. /// /// `Formatter` implements the standard [`Write`] trait for writing log records. diff --git a/src/lib.rs b/src/lib.rs index 2111452d..f47a3270 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,7 +199,6 @@ extern crate atty; use std::env; use std::borrow::Cow; -use std::io::prelude::*; use std::io; use std::mem; use std::cell::RefCell; @@ -255,91 +254,6 @@ pub struct Logger { format: Box io::Result<()> + Sync + Send>, } -struct Format { - default_format_timestamp: bool, - default_format_module_path: bool, - default_format_level: bool, - custom_format: Option io::Result<()> + Sync + Send>>, -} - -impl Default for Format { - fn default() -> Self { - Format { - default_format_timestamp: true, - default_format_module_path: true, - default_format_level: true, - custom_format: None, - } - } -} - -impl Format { - /// Convert the format into a callable function. - /// - /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. - /// If the `custom_format` is `None`, then a default format is returned. - /// Any `default_format` switches set to `false` won't be written by the format. - fn into_boxed_fn(self) -> Box io::Result<()> + Sync + Send> { - if let Some(fmt) = self.custom_format { - fmt - } - else { - Box::new(move |buf, record| { - let mut brace_style = buf.style(); - brace_style.set_color(Color::Black).set_intense(true); - - // Write the start of a header value - let mut written_header_value = false; - let write_header_pre = |buf: &mut Formatter, sentinal: &mut bool| { - if !*sentinal { - *sentinal = true; - write!(buf, "{}", brace_style.value("[")) - } - else { - write!(buf, " ") - } - }; - - let mut write = Ok(()); - - // Write the timestamp - if self.default_format_timestamp { - write = write.and(write_header_pre(buf, &mut written_header_value)); - - let ts = buf.timestamp(); - write = write.and(write!(buf, "{}", ts)); - } - - // Write the record level - if self.default_format_level { - write = write.and(write_header_pre(buf, &mut written_header_value)); - - let level = buf.level_style(record.level()); - - write = write.and(write!(buf, "{}", level)); - } - - // Write the module path - let default_format_module_path = (self.default_format_module_path, record.module_path()); - if let (true, Some(module_path)) = default_format_module_path { - write = write.and(write_header_pre(buf, &mut written_header_value)); - - write = write.and(write!(buf, "{}", module_path)); - } - - if written_header_value { - write = write.and(write!(buf, "{} ", brace_style.value("]"))); - } - - // Write the record arguments - write = write.and(writeln!(buf, "{}", record.args())); - - write - }) - } - } -} - /// `Builder` acts as builder for initializing a `Logger`. /// /// It can be used to customize the log format, change the environment variable used @@ -372,7 +286,7 @@ impl Format { pub struct Builder { filter: filter::Builder, writer: fmt::Builder, - format: Format, + format: fmt::Format, } impl Builder { From 084ea30b77d24c57c3e1a2c27c3ec3f18f042ba3 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 16 Apr 2018 12:13:54 +1000 Subject: [PATCH 05/17] add note about catch blocks for future --- src/fmt.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fmt.rs b/src/fmt.rs index 0de7b8e8..4f915edb 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -78,6 +78,7 @@ impl Format { fmt } else { + // NOTE: We could simplify this with a `catch` block. Box::new(move |buf, record| { let mut brace_style = buf.style(); brace_style.set_color(Color::Black).set_intense(true); From 79656287c026082c04b54ade575c6de44fc352ef Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 16 Apr 2018 12:41:08 +1000 Subject: [PATCH 06/17] use full level name --- src/fmt.rs | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 4f915edb..8043fb61 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -111,7 +111,7 @@ impl Format { let level = buf.level_style(record.level()); - write = write.and(write!(buf, "{}", level)); + write = write.and(write!(buf, "{:<5}", level)); } // Write the module path @@ -581,31 +581,18 @@ impl Formatter { /// writeln!(buf, "{}: {}", level, record.args()) /// }); /// ``` - pub fn level_style(&self, level: Level) -> StyledValue<'static, &'static str> { + pub fn level_style(&self, level: Level) -> StyledValue<'static, Level> { let mut level_style = self.style(); match level { - Level::Trace => { - level_style.set_color(Color::White); - level_style.into_value("TRC") - }, - Level::Debug => { - level_style.set_color(Color::Blue); - level_style.into_value("DBG") - }, - Level::Info => { - level_style.set_color(Color::Green); - level_style.into_value("INF") - }, - Level::Warn => { - level_style.set_color(Color::Yellow); - level_style.into_value("WRN") - }, - Level::Error => { - level_style.set_color(Color::Red).set_bold(true); - level_style.into_value("ERR") - }, - } + Level::Trace => level_style.set_color(Color::White), + Level::Debug => level_style.set_color(Color::Blue), + Level::Info => level_style.set_color(Color::Green), + Level::Warn => level_style.set_color(Color::Yellow), + Level::Error => level_style.set_color(Color::Red).set_bold(true), + }; + + level_style.into_value(level) } pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { From 8da7d09becf326bf37edacda20892de9f15978d3 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 16 Apr 2018 12:50:15 +1000 Subject: [PATCH 07/17] fix formatting --- src/fmt.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/fmt.rs b/src/fmt.rs index 8043fb61..e2e152b2 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -89,10 +89,9 @@ impl Format { if !*sentinal { *sentinal = true; write!(buf, "{}", brace_style.value("[")) + } else { + write!(buf, " ") } - else { - write!(buf, " ") - } }; let mut write = Ok(()); From 7bc9ff9e68e9a56bbaf86048d8c605462893d300 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 3 Nov 2018 19:59:02 +1000 Subject: [PATCH 08/17] refactor the default format --- src/fmt/mod.rs | 184 +++++++++++++++++++++---------- src/fmt/termcolor/extern_impl.rs | 7 +- 2 files changed, 129 insertions(+), 62 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 3b454bc0..b55681c3 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -33,6 +33,7 @@ use std::io::prelude::*; use std::{io, fmt}; use std::rc::Rc; use std::cell::RefCell; +use std::fmt::Display; use log::Record; @@ -85,73 +86,138 @@ impl Format { } else { Box::new(move |buf, record| { - let mut brace_style = buf.style(); - brace_style.set_color(Color::Black).set_intense(true); - - // Write the start of a header value - let mut written_header_value = false; - let write_header_pre = |buf: &mut Formatter, sentinal: &mut bool| { - if !*sentinal { - *sentinal = true; - write!(buf, "{}", brace_style.value("[")) - } else { - write!(buf, " ") - } + let fmt = DefaultFormat { + timestamp: self.default_format_level, + module_path: self.default_format_module_path, + level: self.default_format_level, + timestamp_nanos: self.default_format_timestamp_nanos, + written_header_value: false, + buf, }; - let mut write = Ok(()); - - // Write the record level - if self.default_format_level { - let level = { - #[cfg(feature = "termcolor")] - { - buf.default_styled_level(record.level()) - } - #[cfg(not(feature = "termcolor"))] - { - record.level() - } - }; - - write = write.and(write_header_pre(buf, &mut written_header_value)); - write = write.and(write!(buf, "{:<5}", level)); - } + fmt.write(record) + }) + } + } +} - // Write the timestamp - #[cfg(feature = "humantime")] - { - if self.default_format_timestamp { - write = write.and(write_header_pre(buf, &mut written_header_value)); - - if self.default_format_timestamp_nanos { - let ts_nanos = buf.precise_timestamp(); - write = write.and(write!(buf, "{}", ts_nanos)); - } else { - let ts = buf.timestamp(); - write = write.and(write!(buf, "{}", ts)); - } - } - } +#[cfg(feature = "termcolor")] +type SubtleStyle = StyledValue<'static, &'static str>; +#[cfg(not(feature = "termcolor"))] +type SubtleStyle = &'static str; + +struct DefaultFormat<'a> { + timestamp: bool, + module_path: bool, + level: bool, + timestamp_nanos: bool, + written_header_value: bool, + buf: &'a mut Formatter, +} - // Write the module path - let default_format_module_path = (self.default_format_module_path, record.module_path()); - if let (true, Some(module_path)) = default_format_module_path { - write = write.and(write_header_pre(buf, &mut written_header_value)); - write = write.and(write!(buf, "{}", module_path)); - } +impl<'a> DefaultFormat<'a> { + fn write(mut self, record: &Record) -> io::Result<()> { + self.write_level(record)?; + self.write_timestamp()?; + self.write_module_path(record)?; + self.finish_header()?; - // Write the end of the header - if written_header_value { - write = write.and(write!(buf, "{} ", brace_style.value("]"))); - } + self.write_args(record) + } + + fn subtle_style(&self, text: &'static str) -> SubtleStyle { + #[cfg(feature = "termcolor")] + { + self.buf.style() + .set_color(Color::Black) + .set_intense(true) + .into_value(text) + } + #[cfg(not(feature = "termcolor"))] + { + text + } + } - // Write the record args - write = write.and(writeln!(buf, "{}", record.args())); + fn write_header_value(&mut self, value: T) -> io::Result<()> + where + T: Display, + { + if !self.written_header_value { + self.written_header_value = true; + + let open_brace = self.subtle_style("["); + write!(self.buf, "{}{}", open_brace, value) + } else { + write!(self.buf, " {}", value) + } + } - write - }) + fn write_level(&mut self, record: &Record) -> io::Result<()> { + if !self.level { + return Ok(()) } + + let level = { + #[cfg(feature = "termcolor")] + { + self.buf.default_styled_level(record.level()) + } + #[cfg(not(feature = "termcolor"))] + { + record.level() + } + }; + + self.write_header_value(format_args!("{:<5}", level)) + } + + fn write_timestamp(&mut self) -> io::Result<()> { + #[cfg(feature = "humantime")] + { + if !self.timestamp { + return Ok(()) + } + + if self.timestamp_nanos { + let ts_nanos = self.buf.precise_timestamp(); + self.write_header_value(ts_nanos) + } else { + let ts = self.buf.timestamp(); + self.write_header_value(ts) + } + } + #[cfg(not(feature = "humantime"))] + { + let _ = self.timestamp; + let _ = self.timestamp_nanos; + Ok(()) + } + } + + fn write_module_path(&mut self, record: &Record) -> io::Result<()> { + if !self.module_path { + return Ok(()) + } + + if let Some(module_path) = record.module_path() { + self.write_header_value(module_path) + } else { + Ok(()) + } + } + + fn finish_header(&mut self) -> io::Result<()> { + if self.written_header_value { + let close_brace = self.subtle_style("]"); + write!(self.buf, "{}", close_brace) + } else { + Ok(()) + } + } + + fn write_args(&mut self, record: &Record) -> io::Result<()> { + writeln!(self.buf, "{}", record.args()) } } diff --git a/src/fmt/termcolor/extern_impl.rs b/src/fmt/termcolor/extern_impl.rs index 8e3b56b2..60508fd9 100644 --- a/src/fmt/termcolor/extern_impl.rs +++ b/src/fmt/termcolor/extern_impl.rs @@ -55,7 +55,7 @@ impl Formatter { let mut level_style = self.style(); match level { Level::Trace => level_style.set_color(Color::White), - Level::Debug => level_style.set_color(Color::Blue), + Level::Debug => level_style.set_color(Color::Black).set_intense(true), Level::Info => level_style.set_color(Color::Green), Level::Warn => level_style.set_color(Color::Yellow), Level::Error => level_style.set_color(Color::Red).set_bold(true), @@ -328,9 +328,10 @@ impl Style { } } - fn into_value(self, value: T) -> StyledValue<'static, T> { + /// Wrap a value in the style by taking ownership of it. + pub fn into_value(&mut self, value: T) -> StyledValue<'static, T> { StyledValue { - style: Cow::Owned(self), + style: Cow::Owned(self.clone()), value } } From e6648396551e25dde39f3a17f83b8790c16c8ff9 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 3 Nov 2018 20:24:16 +1000 Subject: [PATCH 09/17] write timestamp first --- src/fmt/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index b55681c3..bd607f1d 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -117,8 +117,8 @@ struct DefaultFormat<'a> { impl<'a> DefaultFormat<'a> { fn write(mut self, record: &Record) -> io::Result<()> { - self.write_level(record)?; self.write_timestamp()?; + self.write_level(record)?; self.write_module_path(record)?; self.finish_header()?; From c48bf756ea4f1a5aa454fec4450e29cdce7c8a80 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 3 Nov 2018 20:27:38 +1000 Subject: [PATCH 10/17] remove FromStr impl for Color --- src/fmt/termcolor/extern_impl.rs | 147 ------------------------------- 1 file changed, 147 deletions(-) diff --git a/src/fmt/termcolor/extern_impl.rs b/src/fmt/termcolor/extern_impl.rs index 60508fd9..b51286ee 100644 --- a/src/fmt/termcolor/extern_impl.rs +++ b/src/fmt/termcolor/extern_impl.rs @@ -1,8 +1,6 @@ -use std::error::Error; use std::borrow::Cow; use std::fmt; use std::io::{self, Write}; -use std::str::FromStr; use std::cell::RefCell; use std::rc::Rc; @@ -421,61 +419,6 @@ pub enum Color { __Nonexhaustive, } -/// An error from parsing an invalid color specification. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ParseColorError(ParseColorErrorKind); - -#[derive(Clone, Debug, Eq, PartialEq)] -enum ParseColorErrorKind { - /// An error originating from `termcolor`. - TermColor(termcolor::ParseColorError), - /// An error converting the `termcolor` color to a `env_logger::Color`. - /// - /// This variant should only get reached if a user uses a new spec that's - /// valid for `termcolor`, but not recognised in `env_logger` yet. - Unrecognized { - given: String, - } -} - -impl ParseColorError { - fn termcolor(err: termcolor::ParseColorError) -> Self { - ParseColorError(ParseColorErrorKind::TermColor(err)) - } - - fn unrecognized(given: String) -> Self { - ParseColorError(ParseColorErrorKind::Unrecognized { given }) - } - - /// Return the string that couldn't be parsed as a valid color. - pub fn invalid(&self) -> &str { - match self.0 { - ParseColorErrorKind::TermColor(ref err) => err.invalid(), - ParseColorErrorKind::Unrecognized { ref given, .. } => given, - } - } -} - -impl Error for ParseColorError { - fn description(&self) -> &str { - match self.0 { - ParseColorErrorKind::TermColor(ref err) => err.description(), - ParseColorErrorKind::Unrecognized { .. } => "unrecognized color value", - } - } -} - -impl fmt::Display for ParseColorError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - ParseColorErrorKind::TermColor(ref err) => fmt::Display::fmt(err, f), - ParseColorErrorKind::Unrecognized { ref given, .. } => { - write!(f, "unrecognized color value '{}'", given) - } - } - } -} - impl Color { fn into_termcolor(self) -> Option { match self { @@ -492,94 +435,4 @@ impl Color { _ => None, } } - - fn from_termcolor(color: termcolor::Color) -> Option { - match color { - termcolor::Color::Black => Some(Color::Black), - termcolor::Color::Blue => Some(Color::Blue), - termcolor::Color::Green => Some(Color::Green), - termcolor::Color::Red => Some(Color::Red), - termcolor::Color::Cyan => Some(Color::Cyan), - termcolor::Color::Magenta => Some(Color::Magenta), - termcolor::Color::Yellow => Some(Color::Yellow), - termcolor::Color::White => Some(Color::White), - termcolor::Color::Ansi256(value) => Some(Color::Ansi256(value)), - termcolor::Color::Rgb(r, g, b) => Some(Color::Rgb(r, g, b)), - _ => None, - } - } } - -impl FromStr for Color { - type Err = ParseColorError; - - fn from_str(s: &str) -> Result { - let tc = termcolor::Color::from_str(s).map_err(ParseColorError::termcolor)?; - Color::from_termcolor(tc).ok_or_else(|| ParseColorError::unrecognized(s.into())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_color_name_valid() { - let inputs = vec![ - "black", - "blue", - "green", - "red", - "cyan", - "magenta", - "yellow", - "white", - ]; - - for input in inputs { - assert!(Color::from_str(input).is_ok()); - } - } - - #[test] - fn parse_color_ansi_valid() { - let inputs = vec![ - "7", - "32", - "0xFF", - ]; - - for input in inputs { - assert!(Color::from_str(input).is_ok()); - } - } - - #[test] - fn parse_color_rgb_valid() { - let inputs = vec![ - "0,0,0", - "0,128,255", - "0x0,0x0,0x0", - "0x33,0x66,0xFF", - ]; - - for input in inputs { - assert!(Color::from_str(input).is_ok()); - } - } - - #[test] - fn parse_color_invalid() { - let inputs = vec![ - "not_a_color", - "256", - "0,0", - "0,0,256", - ]; - - for input in inputs { - let err = Color::from_str(input).unwrap_err(); - assert_eq!(input, err.invalid()); - } - } -} \ No newline at end of file From a2910d53fbc56c3834bd9460b21c43d93e82ef01 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 09:58:38 +1000 Subject: [PATCH 11/17] note the stability of default format --- README.md | 16 +++++++++++----- src/lib.rs | 37 +++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f73264af..b0bcaa80 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ environment variable that corresponds with the log messages you want to show. ```bash $ RUST_LOG=info ./main -[INFO 2018-11-03T06:09:06Z default] starting up +[2018-11-03T06:09:06Z INFO default] starting up ``` ### In tests @@ -93,11 +93,11 @@ $ RUST_LOG=my_lib=info cargo test Running target/debug/my_lib-... running 2 tests -[INFO 2017-11-09T02:12:24Z my_lib::tests] logging from another test -[INFO 2017-11-09T02:12:24Z my_lib] add_one called with -8 +[2017-11-09T02:12:24Z INFO my_lib::tests] logging from another test +[2017-11-09T02:12:24Z INFO my_lib] add_one called with -8 test tests::it_handles_negative_numbers ... ok -[INFO 2017-11-09T02:12:24Z my_lib::tests] can log from the test too -[INFO 2017-11-09T02:12:24Z my_lib] add_one called with 2 +[2017-11-09T02:12:24Z INFO my_lib::tests] can log from the test too +[2017-11-09T02:12:24Z INFO my_lib] add_one called with 2 test tests::it_adds_one ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured @@ -138,3 +138,9 @@ if env::var("RUST_LOG").is_ok() { } builder.init(); ``` + +## Stability of the default format + +The default format won't optimise for long-term stability, and explicitly makes no guarantees about the stability of its output across major, minor or patch version bumps during `0.x`. + +If you want to capture or innterpret the output of `env_logger` programmatically then you should use a custom format. \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1e3691b3..47ba36d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,37 +37,37 @@ //! //! ```{.bash} //! $ RUST_LOG=error ./main -//! [ERROR 2017-11-09T02:12:24Z main] this is printed by default +//! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! ``` //! //! ```{.bash} //! $ RUST_LOG=info ./main -//! [ERROR 2017-11-09T02:12:24Z main] this is printed by default -//! [INFO 2017-11-09T02:12:24Z main] the answer was: 12 +//! [2017-11-09T02:12:24Z ERROR main] this is printed by default +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! ```{.bash} //! $ RUST_LOG=debug ./main -//! [DEBUG 2017-11-09T02:12:24Z main] this is a debug message -//! [ERROR 2017-11-09T02:12:24Z main] this is printed by default -//! [INFO 2017-11-09T02:12:24Z main] the answer was: 12 +//! [2017-11-09T02:12:24Z DEBUG main] this is a debug message +//! [2017-11-09T02:12:24Z ERROR main] this is printed by default +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! You can also set the log level on a per module basis: //! //! ```{.bash} //! $ RUST_LOG=main=info ./main -//! [ERROR 2017-11-09T02:12:24Z main] this is printed by default -//! [INFO 2017-11-09T02:12:24Z main] the answer was: 12 +//! [2017-11-09T02:12:24Z ERROR main] this is printed by default +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! And enable all logging: //! //! ```{.bash} //! $ RUST_LOG=main ./main -//! [DEBUG 2017-11-09T02:12:24Z main] this is a debug message -//! [ERROR 2017-11-09T02:12:24Z main] this is printed by default -//! [INFO 2017-11-09T02:12:24Z main] the answer was: 12 +//! [2017-11-09T02:12:24Z DEBUG main] this is a debug message +//! [2017-11-09T02:12:24Z ERROR main] this is printed by default +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! If the binary name contains hyphens, you will need to replace @@ -75,9 +75,9 @@ //! //! ```{.bash} //! $ RUST_LOG=my_app ./my-app -//! [DEBUG 2017-11-09T02:12:24Z my_app] this is a debug message -//! [ERROR 2017-11-09T02:12:24Z my_app] this is printed by default -//! [INFO 2017-11-09T02:12:24Z my_app] the answer was: 12 +//! [2017-11-09T02:12:24Z DEBUG my_app] this is a debug message +//! [2017-11-09T02:12:24Z ERROR my_app] this is printed by default +//! [2017-11-09T02:12:24Z INFO my_app] the answer was: 12 //! ``` //! //! This is because Rust modules and crates cannot contain hyphens @@ -177,6 +177,15 @@ //! } //! ``` //! +//! ### Stability of the default format +//! +//! The default format won't optimise for long-term stability, and explicitly makes no +//! guarantees about the stability of its output across major, minor or patch version +//! bumps during `0.x`. +//! +//! If you want to capture or innterpret the output of `env_logger` programmatically +//! then you should use a custom format. +//! //! ## Specifying defaults for environment variables //! //! `env_logger` can read configuration from environment variables. From 3f0453590b54db7458240cca180f8d2ffc63bf10 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 11:13:29 +1000 Subject: [PATCH 12/17] refactor writer --- Cargo.toml | 2 +- src/fmt/mod.rs | 180 +---------------- src/fmt/{ => writer}/atty.rs | 0 src/fmt/writer/mod.rs | 185 ++++++++++++++++++ src/fmt/{ => writer}/termcolor/extern_impl.rs | 10 +- src/fmt/{ => writer}/termcolor/mod.rs | 0 src/fmt/{ => writer}/termcolor/shim_impl.rs | 10 +- 7 files changed, 201 insertions(+), 186 deletions(-) rename src/fmt/{ => writer}/atty.rs (100%) create mode 100644 src/fmt/writer/mod.rs rename src/fmt/{ => writer}/termcolor/extern_impl.rs (97%) rename src/fmt/{ => writer}/termcolor/mod.rs (100%) rename src/fmt/{ => writer}/termcolor/shim_impl.rs (78%) diff --git a/Cargo.toml b/Cargo.toml index d72ae9a1..a08d8980 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ name = "log-in-log" harness = false [features] -default = ["termcolor", "atty", "humantime", "regex"] +default = ["termcolor", "atty", "humantime", "regex"] \ No newline at end of file diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index bd607f1d..6e6ed8e2 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -37,15 +37,11 @@ use std::fmt::Display; use log::Record; -mod termcolor; -mod atty; +mod writer; mod humantime; -use self::termcolor::{Buffer, BufferWriter}; -use self::atty::{is_stdout, is_stderr}; - pub use self::humantime::pub_use_in_super::*; -pub use self::termcolor::pub_use_in_super::*; +pub use self::writer::pub_use_in_super::*; pub(super) mod pub_use_in_super { pub use super::{Target, WriteStyle, Formatter}; @@ -210,7 +206,7 @@ impl<'a> DefaultFormat<'a> { fn finish_header(&mut self) -> io::Result<()> { if self.written_header_value { let close_brace = self.subtle_style("]"); - write!(self.buf, "{}", close_brace) + write!(self.buf, "{} ", close_brace) } else { Ok(()) } @@ -246,126 +242,10 @@ pub struct Formatter { write_style: WriteStyle, } -/// Log target, either `stdout` or `stderr`. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Target { - /// Logs will be sent to standard output. - Stdout, - /// Logs will be sent to standard error. - Stderr, -} - -impl Default for Target { - fn default() -> Self { - Target::Stderr - } -} - -/// Whether or not to print styles to the target. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum WriteStyle { - /// Try to print styles, but don't force the issue. - Auto, - /// Try very hard to print styles. - Always, - /// Never print styles. - Never, -} - -impl Default for WriteStyle { - fn default() -> Self { - WriteStyle::Auto - } -} - -/// A terminal target with color awareness. -pub(crate) struct Writer { - inner: BufferWriter, - write_style: WriteStyle, -} - -impl Writer { - pub(crate) fn write_style(&self) -> WriteStyle { - self.write_style - } -} - -/// A builder for a terminal writer. -/// -/// The target and style choice can be configured before building. -pub(crate) struct Builder { - target: Target, - write_style: WriteStyle, -} - -impl Builder { - /// Initialize the writer builder with defaults. - pub fn new() -> Self { - Builder { - target: Default::default(), - write_style: Default::default(), - } - } - - /// Set the target to write to. - pub fn target(&mut self, target: Target) -> &mut Self { - self.target = target; - self - } - - /// Parses a style choice string. - /// - /// See the [Disabling colors] section for more details. - /// - /// [Disabling colors]: ../index.html#disabling-colors - pub fn parse(&mut self, write_style: &str) -> &mut Self { - self.write_style(parse_write_style(write_style)) - } - - /// Whether or not to print style characters when writing. - pub fn write_style(&mut self, write_style: WriteStyle) -> &mut Self { - self.write_style = write_style; - self - } - - /// Build a terminal writer. - pub fn build(&mut self) -> Writer { - let color_choice = match self.write_style { - WriteStyle::Auto => { - if match self.target { - Target::Stderr => is_stderr(), - Target::Stdout => is_stdout(), - } { - WriteStyle::Auto - } else { - WriteStyle::Never - } - }, - color_choice => color_choice, - }; - - let writer = match self.target { - Target::Stderr => BufferWriter::stderr(color_choice), - Target::Stdout => BufferWriter::stdout(color_choice), - }; - - Writer { - inner: writer, - write_style: self.write_style, - } - } -} - -impl Default for Builder { - fn default() -> Self { - Builder::new() - } -} - impl Formatter { pub(crate) fn new(writer: &Writer) -> Self { Formatter { - buf: Rc::new(RefCell::new(writer.inner.buffer())), + buf: Rc::new(RefCell::new(writer.buffer())), write_style: writer.write_style(), } } @@ -375,7 +255,7 @@ impl Formatter { } pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { - writer.inner.print(&self.buf.borrow()) + writer.print(&self.buf.borrow()) } pub(crate) fn clear(&mut self) { @@ -404,53 +284,3 @@ impl fmt::Debug for Formatter { f.debug_struct("Formatter").finish() } } - -impl fmt::Debug for Builder { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { - f.debug_struct("Logger") - .field("target", &self.target) - .field("write_style", &self.write_style) - .finish() - } -} - -fn parse_write_style(spec: &str) -> WriteStyle { - match spec { - "auto" => WriteStyle::Auto, - "always" => WriteStyle::Always, - "never" => WriteStyle::Never, - _ => Default::default(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_write_style_valid() { - let inputs = vec![ - ("auto", WriteStyle::Auto), - ("always", WriteStyle::Always), - ("never", WriteStyle::Never), - ]; - - for (input, expected) in inputs { - assert_eq!(expected, parse_write_style(input)); - } - } - - #[test] - fn parse_write_style_invalid() { - let inputs = vec![ - "", - "true", - "false", - "NEVER!!" - ]; - - for input in inputs { - assert_eq!(WriteStyle::Auto, parse_write_style(input)); - } - } -} \ No newline at end of file diff --git a/src/fmt/atty.rs b/src/fmt/writer/atty.rs similarity index 100% rename from src/fmt/atty.rs rename to src/fmt/writer/atty.rs diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs new file mode 100644 index 00000000..3694da11 --- /dev/null +++ b/src/fmt/writer/mod.rs @@ -0,0 +1,185 @@ +mod termcolor; +mod atty; + +use std::{fmt, io}; +use self::termcolor::{Buffer, BufferWriter}; +use self::atty::{is_stdout, is_stderr}; + +pub(in ::fmt) mod pub_use_in_super { + pub use super::termcolor::pub_use_in_super::*; + pub use super::*; +} + +/// Log target, either `stdout` or `stderr`. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Target { + /// Logs will be sent to standard output. + Stdout, + /// Logs will be sent to standard error. + Stderr, +} + +impl Default for Target { + fn default() -> Self { + Target::Stderr + } +} + +/// Whether or not to print styles to the target. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum WriteStyle { + /// Try to print styles, but don't force the issue. + Auto, + /// Try very hard to print styles. + Always, + /// Never print styles. + Never, +} + +impl Default for WriteStyle { + fn default() -> Self { + WriteStyle::Auto + } +} + +/// A terminal target with color awareness. +pub(crate) struct Writer { + inner: BufferWriter, + write_style: WriteStyle, +} + +impl Writer { + pub fn write_style(&self) -> WriteStyle { + self.write_style + } + + pub(in ::fmt) fn buffer(&self) -> Buffer { + self.inner.buffer() + } + + pub(in ::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> { + self.inner.print(buf) + } +} + +/// A builder for a terminal writer. +/// +/// The target and style choice can be configured before building. +pub(crate) struct Builder { + target: Target, + write_style: WriteStyle, +} + +impl Builder { + /// Initialize the writer builder with defaults. + pub fn new() -> Self { + Builder { + target: Default::default(), + write_style: Default::default(), + } + } + + /// Set the target to write to. + pub fn target(&mut self, target: Target) -> &mut Self { + self.target = target; + self + } + + /// Parses a style choice string. + /// + /// See the [Disabling colors] section for more details. + /// + /// [Disabling colors]: ../index.html#disabling-colors + pub fn parse(&mut self, write_style: &str) -> &mut Self { + self.write_style(parse_write_style(write_style)) + } + + /// Whether or not to print style characters when writing. + pub fn write_style(&mut self, write_style: WriteStyle) -> &mut Self { + self.write_style = write_style; + self + } + + /// Build a terminal writer. + pub fn build(&mut self) -> Writer { + let color_choice = match self.write_style { + WriteStyle::Auto => { + if match self.target { + Target::Stderr => is_stderr(), + Target::Stdout => is_stdout(), + } { + WriteStyle::Auto + } else { + WriteStyle::Never + } + }, + color_choice => color_choice, + }; + + let writer = match self.target { + Target::Stderr => BufferWriter::stderr(color_choice), + Target::Stdout => BufferWriter::stdout(color_choice), + }; + + Writer { + inner: writer, + write_style: self.write_style, + } + } +} + +impl Default for Builder { + fn default() -> Self { + Builder::new() + } +} + +impl fmt::Debug for Builder { + fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + f.debug_struct("Logger") + .field("target", &self.target) + .field("write_style", &self.write_style) + .finish() + } +} + +fn parse_write_style(spec: &str) -> WriteStyle { + match spec { + "auto" => WriteStyle::Auto, + "always" => WriteStyle::Always, + "never" => WriteStyle::Never, + _ => Default::default(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_write_style_valid() { + let inputs = vec![ + ("auto", WriteStyle::Auto), + ("always", WriteStyle::Always), + ("never", WriteStyle::Never), + ]; + + for (input, expected) in inputs { + assert_eq!(expected, parse_write_style(input)); + } + } + + #[test] + fn parse_write_style_invalid() { + let inputs = vec![ + "", + "true", + "false", + "NEVER!!" + ]; + + for input in inputs { + assert_eq!(WriteStyle::Auto, parse_write_style(input)); + } + } +} \ No newline at end of file diff --git a/src/fmt/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs similarity index 97% rename from src/fmt/termcolor/extern_impl.rs rename to src/fmt/writer/termcolor/extern_impl.rs index b51286ee..bcf4912a 100644 --- a/src/fmt/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -10,7 +10,7 @@ use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; use ::WriteStyle; use ::fmt::Formatter; -pub(in ::fmt) mod pub_use_in_super { +pub(in ::fmt::writer) mod pub_use_in_super { pub use super::*; } @@ -73,19 +73,19 @@ pub(in ::fmt) struct BufferWriter(termcolor::BufferWriter); pub(in ::fmt) struct Buffer(termcolor::Buffer); impl BufferWriter { - pub(in ::fmt) fn stderr(write_style: WriteStyle) -> Self { + pub(in ::fmt::writer) fn stderr(write_style: WriteStyle) -> Self { BufferWriter(termcolor::BufferWriter::stderr(write_style.into_color_choice())) } - pub(in ::fmt) fn stdout(write_style: WriteStyle) -> Self { + pub(in ::fmt::writer) fn stdout(write_style: WriteStyle) -> Self { BufferWriter(termcolor::BufferWriter::stdout(write_style.into_color_choice())) } - pub(in ::fmt) fn buffer(&self) -> Buffer { + pub(in ::fmt::writer) fn buffer(&self) -> Buffer { Buffer(self.0.buffer()) } - pub(in ::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> { + pub(in ::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { self.0.print(&buf.0) } } diff --git a/src/fmt/termcolor/mod.rs b/src/fmt/writer/termcolor/mod.rs similarity index 100% rename from src/fmt/termcolor/mod.rs rename to src/fmt/writer/termcolor/mod.rs diff --git a/src/fmt/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs similarity index 78% rename from src/fmt/termcolor/shim_impl.rs rename to src/fmt/writer/termcolor/shim_impl.rs index 2ab3f7ca..adc7b4a4 100644 --- a/src/fmt/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -2,7 +2,7 @@ use std::io::{self, Write}; use fmt::{WriteStyle, Target}; -pub(in ::fmt) mod pub_use_in_super { +pub(in ::fmt::writer) mod pub_use_in_super { } @@ -13,23 +13,23 @@ pub(in ::fmt) struct BufferWriter { pub(in ::fmt) struct Buffer(Vec); impl BufferWriter { - pub(in ::fmt) fn stderr(_: WriteStyle) -> Self { + pub(in ::fmt::writer) fn stderr(_: WriteStyle) -> Self { BufferWriter { target: Target::Stderr, } } - pub(in ::fmt) fn stdout(_: WriteStyle) -> Self { + pub(in ::fmt::writer) fn stdout(_: WriteStyle) -> Self { BufferWriter { target: Target::Stdout, } } - pub(in ::fmt) fn buffer(&self) -> Buffer { + pub(in ::fmt::writer) fn buffer(&self) -> Buffer { Buffer(Vec::new()) } - pub(in ::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> { + pub(in ::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { match self.target { Target::Stderr => { let stderr = io::stderr(); From 6bbe22ddcc10f0a42522e2250f77e132522ada9c Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 11:21:00 +1000 Subject: [PATCH 13/17] make shim termcolor cargo test friendly --- README.md | 2 +- src/fmt/writer/mod.rs | 4 +++- src/fmt/writer/termcolor/extern_impl.rs | 2 +- src/fmt/writer/termcolor/shim_impl.rs | 21 +++++++++++++-------- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b0bcaa80..2ccbff00 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Tests can use the `env_logger` crate to see log messages generated during that t log = "0.4.0" [dev-dependencies] -env_logger = "0.5.13" +env_logger = { version = "0.5.13", default-features = false } ``` ```rust diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 3694da11..c64707b4 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -2,7 +2,7 @@ mod termcolor; mod atty; use std::{fmt, io}; -use self::termcolor::{Buffer, BufferWriter}; +use self::termcolor::BufferWriter; use self::atty::{is_stdout, is_stderr}; pub(in ::fmt) mod pub_use_in_super { @@ -10,6 +10,8 @@ pub(in ::fmt) mod pub_use_in_super { pub use super::*; } +pub(in ::fmt) use self::termcolor::Buffer; + /// Log target, either `stdout` or `stderr`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Target { diff --git a/src/fmt/writer/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs index bcf4912a..f605edbc 100644 --- a/src/fmt/writer/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -69,7 +69,7 @@ impl Formatter { } } -pub(in ::fmt) struct BufferWriter(termcolor::BufferWriter); +pub(in ::fmt::writer) struct BufferWriter(termcolor::BufferWriter); pub(in ::fmt) struct Buffer(termcolor::Buffer); impl BufferWriter { diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index adc7b4a4..39390e14 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -1,4 +1,4 @@ -use std::io::{self, Write}; +use std::io; use fmt::{WriteStyle, Target}; @@ -6,7 +6,7 @@ pub(in ::fmt::writer) mod pub_use_in_super { } -pub(in ::fmt) struct BufferWriter { +pub(in ::fmt::writer) struct BufferWriter { target: Target, } @@ -30,16 +30,21 @@ impl BufferWriter { } pub(in ::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` match self.target { Target::Stderr => { - let stderr = io::stderr(); - let mut stderr = stderr.lock(); - stderr.write_all(&buf.0) + let log = String::from_utf8_lossy(&buf.0); + eprint!("{}", log); + + Ok(()) }, Target::Stdout => { - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - stdout.write_all(&buf.0) + let log = String::from_utf8_lossy(&buf.0); + print!("{}", log); + + Ok(()) }, } } From 145b71eac389fa55ec96be33a9d590b245212f2c Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 11:22:04 +1000 Subject: [PATCH 14/17] simplify print --- src/fmt/writer/termcolor/shim_impl.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index 39390e14..390f2c47 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -33,20 +33,14 @@ impl BufferWriter { // 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 { - Target::Stderr => { - let log = String::from_utf8_lossy(&buf.0); - eprint!("{}", log); - - Ok(()) - }, - Target::Stdout => { - let log = String::from_utf8_lossy(&buf.0); - print!("{}", log); + let log = String::from_utf8_lossy(&buf.0); - Ok(()) - }, + match self.target { + Target::Stderr => eprint!("{}", log), + Target::Stdout => print!("{}", log), } + + Ok(()) } } From d59949efef3d2cd8cfece16bcfe24a48352f7db5 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 18:50:20 +1000 Subject: [PATCH 15/17] more work on the fmt impls --- README.md | 4 +- src/fmt/humantime/extern_impl.rs | 2 +- src/fmt/humantime/shim_impl.rs | 2 +- src/fmt/mod.rs | 218 +++++++++++++++--------- src/fmt/writer/mod.rs | 12 +- src/fmt/writer/termcolor/extern_impl.rs | 7 +- src/fmt/writer/termcolor/shim_impl.rs | 7 +- src/lib.rs | 29 ++-- 8 files changed, 182 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 2ccbff00..5463365b 100644 --- a/README.md +++ b/README.md @@ -115,8 +115,8 @@ $ RUST_LOG=my_lib=info cargo test it_adds_one Running target/debug/my_lib-... running 1 test -[INFO 2017-11-09T02:12:24Z my_lib::tests] can log from the test too -[INFO 2017-11-09T02:12:24Z my_lib] add_one called with 2 +[2017-11-09T02:12:24Z INFO my_lib::tests] can log from the test too +[2017-11-09T02:12:24Z INFO my_lib] add_one called with 2 test tests::it_adds_one ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured diff --git a/src/fmt/humantime/extern_impl.rs b/src/fmt/humantime/extern_impl.rs index 53c47f63..596a2819 100644 --- a/src/fmt/humantime/extern_impl.rs +++ b/src/fmt/humantime/extern_impl.rs @@ -5,7 +5,7 @@ use humantime::{format_rfc3339_nanos, format_rfc3339_seconds}; use ::fmt::Formatter; -pub(in ::fmt) mod pub_use_in_super { +pub(in ::fmt) mod glob { pub use super::*; } diff --git a/src/fmt/humantime/shim_impl.rs b/src/fmt/humantime/shim_impl.rs index 3f5d4e39..0f753400 100644 --- a/src/fmt/humantime/shim_impl.rs +++ b/src/fmt/humantime/shim_impl.rs @@ -2,6 +2,6 @@ Timestamps aren't available when we don't have a `humantime` dependency. */ -pub(in ::fmt) mod pub_use_in_super { +pub(in ::fmt) mod glob { } diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 6e6ed8e2..712f52d4 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -30,63 +30,133 @@ //! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html use std::io::prelude::*; -use std::{io, fmt}; +use std::{io, fmt, mem}; use std::rc::Rc; use std::cell::RefCell; use std::fmt::Display; use log::Record; -mod writer; +pub(crate) mod writer; mod humantime; -pub use self::humantime::pub_use_in_super::*; -pub use self::writer::pub_use_in_super::*; +pub use self::humantime::glob::*; +pub use self::writer::glob::*; -pub(super) mod pub_use_in_super { +use self::writer::{Writer, Buffer}; + +pub(crate) mod glob { pub use super::{Target, WriteStyle, Formatter}; +} + +/// A formatter to write logs into. +/// +/// `Formatter` implements the standard [`Write`] trait for writing log records. +/// It also supports terminal colors, through the [`style`] method. +/// +/// # Examples +/// +/// Use the [`writeln`] macro to easily format a log record: +/// +/// ``` +/// use std::io::Write; +/// +/// let mut builder = env_logger::Builder::new(); +/// +/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())); +/// ``` +/// +/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html +/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html +/// [`style`]: #method.style +pub struct Formatter { + buf: Rc>, + write_style: WriteStyle, +} + +impl Formatter { + pub(crate) fn new(writer: &Writer) -> Self { + Formatter { + buf: Rc::new(RefCell::new(writer.buffer())), + write_style: writer.write_style(), + } + } + + pub(crate) fn write_style(&self) -> WriteStyle { + self.write_style + } + + pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { + writer.print(&self.buf.borrow()) + } + + pub(crate) fn clear(&mut self) { + self.buf.borrow_mut().clear() + } +} - #[cfg(feature = "termcolor")] - pub use super::Color; +impl Write for Formatter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buf.borrow_mut().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.buf.borrow_mut().flush() + } } -pub(super) struct Format { +impl fmt::Debug for Formatter { + fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + f.debug_struct("Formatter").finish() + } +} + +pub(crate) struct Builder { pub default_format_timestamp: bool, + pub default_format_timestamp_nanos: bool, pub default_format_module_path: bool, pub default_format_level: bool, - pub default_format_timestamp_nanos: bool, pub custom_format: Option io::Result<()> + Sync + Send>>, + built: bool, } -impl Default for Format { +impl Default for Builder { fn default() -> Self { - Format { + Builder { default_format_timestamp: true, + default_format_timestamp_nanos: false, default_format_module_path: true, default_format_level: true, - default_format_timestamp_nanos: false, custom_format: None, + built: false, } } } -impl Format { +impl Builder { /// Convert the format into a callable function. /// /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. /// If the `custom_format` is `None`, then a default format is returned. /// Any `default_format` switches set to `false` won't be written by the format. - pub fn into_boxed_fn(self) -> Box io::Result<()> + Sync + Send> { - if let Some(fmt) = self.custom_format { + pub fn build(&mut self) -> Box io::Result<()> + Sync + Send> { + assert!(!self.built, "attempt to re-use consumed builder"); + + let built = mem::replace(self, Builder { + built: true, + ..Default::default() + }); + + if let Some(fmt) = built.custom_format { fmt } else { Box::new(move |buf, record| { let fmt = DefaultFormat { - timestamp: self.default_format_level, - module_path: self.default_format_module_path, - level: self.default_format_level, - timestamp_nanos: self.default_format_timestamp_nanos, + timestamp: built.default_format_timestamp, + timestamp_nanos: built.default_format_timestamp_nanos, + module_path: built.default_format_module_path, + level: built.default_format_level, written_header_value: false, buf, }; @@ -165,7 +235,7 @@ impl<'a> DefaultFormat<'a> { } }; - self.write_header_value(format_args!("{:<5}", level)) + self.write_header_value(format_args!("{}", level)) } fn write_timestamp(&mut self) -> io::Result<()> { @@ -217,70 +287,66 @@ impl<'a> DefaultFormat<'a> { } } -/// A formatter to write logs into. -/// -/// `Formatter` implements the standard [`Write`] trait for writing log records. -/// It also supports terminal colors, through the [`style`] method. -/// -/// # Examples -/// -/// Use the [`writeln`] macro to easily format a log record: -/// -/// ``` -/// use std::io::Write; -/// -/// let mut builder = env_logger::Builder::new(); -/// -/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())); -/// ``` -/// -/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html -/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html -/// [`style`]: #method.style -pub struct Formatter { - buf: Rc>, - write_style: WriteStyle, -} +#[cfg(test)] +mod tests { + use super::*; -impl Formatter { - pub(crate) fn new(writer: &Writer) -> Self { - Formatter { - buf: Rc::new(RefCell::new(writer.buffer())), - write_style: writer.write_style(), - } - } + use log::{Level, Record}; - pub(crate) fn write_style(&self) -> WriteStyle { - self.write_style - } + fn write(fmt: DefaultFormat) -> String { + let buf = fmt.buf.buf.clone(); - pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { - writer.print(&self.buf.borrow()) - } + let record = Record::builder() + .args(format_args!("log message")) + .level(Level::Info) + .file(Some("test.rs")) + .line(Some(144)) + .module_path(Some("test::path")) + .build(); - pub(crate) fn clear(&mut self) { - self.buf.borrow_mut().clear() - } -} + fmt.write(&record).expect("failed to write record"); -impl Write for Formatter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.buf.borrow_mut().write(buf) + let buf = buf.borrow(); + String::from_utf8(buf.bytes().to_vec()).expect("failed to read record") } - fn flush(&mut self) -> io::Result<()> { - self.buf.borrow_mut().flush() - } -} + #[test] + fn default_format_with_header() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); -impl fmt::Debug for Writer { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { - f.debug_struct("Writer").finish() + let mut f = Formatter::new(&writer); + + let written = write(DefaultFormat { + timestamp: false, + timestamp_nanos: false, + module_path: true, + level: true, + written_header_value: false, + buf: &mut f, + }); + + assert_eq!("[INFO test::path] log message\n", written); } -} -impl fmt::Debug for Formatter { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { - f.debug_struct("Formatter").finish() + #[test] + fn default_format_no_header() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); + + let mut f = Formatter::new(&writer); + + let written = write(DefaultFormat { + timestamp: false, + timestamp_nanos: false, + module_path: false, + level: false, + written_header_value: false, + buf: &mut f, + }); + + assert_eq!("log message\n", written); } -} +} \ No newline at end of file diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index c64707b4..66eb6067 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -5,8 +5,8 @@ use std::{fmt, io}; use self::termcolor::BufferWriter; use self::atty::{is_stdout, is_stderr}; -pub(in ::fmt) mod pub_use_in_super { - pub use super::termcolor::pub_use_in_super::*; +pub(in ::fmt) mod glob { + pub use super::termcolor::glob::*; pub use super::*; } @@ -145,6 +145,12 @@ impl fmt::Debug for Builder { } } +impl fmt::Debug for Writer { + fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + f.debug_struct("Writer").finish() + } +} + fn parse_write_style(spec: &str) -> WriteStyle { match spec { "auto" => WriteStyle::Auto, @@ -184,4 +190,4 @@ mod tests { assert_eq!(WriteStyle::Auto, parse_write_style(input)); } } -} \ No newline at end of file +} diff --git a/src/fmt/writer/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs index f605edbc..1409cd23 100644 --- a/src/fmt/writer/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -10,7 +10,7 @@ use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; use ::WriteStyle; use ::fmt::Formatter; -pub(in ::fmt::writer) mod pub_use_in_super { +pub(in ::fmt::writer) mod glob { pub use super::*; } @@ -103,6 +103,11 @@ impl Buffer { self.0.flush() } + #[cfg(test)] + pub(in ::fmt) fn bytes(&self) -> &[u8] { + self.0.as_slice() + } + fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { self.0.set_color(spec) } diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index 390f2c47..b785dec0 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -2,7 +2,7 @@ use std::io; use fmt::{WriteStyle, Target}; -pub(in ::fmt::writer) mod pub_use_in_super { +pub(in ::fmt::writer) mod glob { } @@ -57,4 +57,9 @@ impl Buffer { pub(in ::fmt) fn flush(&mut self) -> io::Result<()> { Ok(()) } + + #[cfg(test)] + pub(in ::fmt) fn bytes(&self) -> &[u8] { + &self.0 + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 47ba36d0..ede27a61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,14 +43,14 @@ //! ```{.bash} //! $ RUST_LOG=info ./main //! [2017-11-09T02:12:24Z ERROR main] this is printed by default -//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! ```{.bash} //! $ RUST_LOG=debug ./main //! [2017-11-09T02:12:24Z DEBUG main] this is a debug message //! [2017-11-09T02:12:24Z ERROR main] this is printed by default -//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! You can also set the log level on a per module basis: @@ -58,7 +58,7 @@ //! ```{.bash} //! $ RUST_LOG=main=info ./main //! [2017-11-09T02:12:24Z ERROR main] this is printed by default -//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! And enable all logging: @@ -67,7 +67,7 @@ //! $ RUST_LOG=main ./main //! [2017-11-09T02:12:24Z DEBUG main] this is a debug message //! [2017-11-09T02:12:24Z ERROR main] this is printed by default -//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 +//! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! If the binary name contains hyphens, you will need to replace @@ -77,7 +77,7 @@ //! $ RUST_LOG=my_app ./my-app //! [2017-11-09T02:12:24Z DEBUG my_app] this is a debug message //! [2017-11-09T02:12:24Z ERROR my_app] this is printed by default -//! [2017-11-09T02:12:24Z INFO my_app] the answer was: 12 +//! [2017-11-09T02:12:24Z INFO my_app] the answer was: 12 //! ``` //! //! This is because Rust modules and crates cannot contain hyphens @@ -232,10 +232,8 @@ extern crate humantime; #[cfg(feature = "atty")] extern crate atty; -use std::env; +use std::{env, io}; use std::borrow::Cow; -use std::io; -use std::mem; use std::cell::RefCell; use log::{Log, LevelFilter, Record, SetLoggerError, Metadata}; @@ -243,7 +241,10 @@ use log::{Log, LevelFilter, Record, SetLoggerError, Metadata}; pub mod filter; pub mod fmt; -pub use self::fmt::pub_use_in_super::*; +pub use self::fmt::glob::*; + +use self::filter::Filter; +use self::fmt::writer::{self, Writer}; /// The default name for the environment variable to read filters from. pub const DEFAULT_FILTER_ENV: &'static str = "RUST_LOG"; @@ -293,8 +294,8 @@ struct Var<'a> { /// [`Builder::try_init()`]: struct.Builder.html#method.try_init /// [`Builder`]: struct.Builder.html pub struct Logger { - writer: fmt::Writer, - filter: filter::Filter, + writer: Writer, + filter: Filter, format: Box io::Result<()> + Sync + Send>, } @@ -329,8 +330,8 @@ pub struct Logger { #[derive(Default)] pub struct Builder { filter: filter::Builder, - writer: fmt::Builder, - format: fmt::Format, + writer: writer::Builder, + format: fmt::Builder, } impl Builder { @@ -670,7 +671,7 @@ impl Builder { Logger { writer: self.writer.build(), filter: self.filter.build(), - format: mem::replace(&mut self.format, Default::default()).into_boxed_fn(), + format: self.format.build(), } } } From faebd4457eb17ea8600694dd871f5b77adf00b42 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 18:57:18 +1000 Subject: [PATCH 16/17] catch attempts to re-use builders --- src/fmt/mod.rs | 4 ++-- src/fmt/writer/mod.rs | 5 +++++ src/lib.rs | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 712f52d4..143a61b0 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -235,7 +235,7 @@ impl<'a> DefaultFormat<'a> { } }; - self.write_header_value(format_args!("{}", level)) + self.write_header_value(format_args!("{:<5}", level)) } fn write_timestamp(&mut self) -> io::Result<()> { @@ -327,7 +327,7 @@ mod tests { buf: &mut f, }); - assert_eq!("[INFO test::path] log message\n", written); + assert_eq!("[INFO test::path] log message\n", written); } #[test] diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 66eb6067..d628187e 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -70,6 +70,7 @@ impl Writer { pub(crate) struct Builder { target: Target, write_style: WriteStyle, + built: bool, } impl Builder { @@ -78,6 +79,7 @@ impl Builder { Builder { target: Default::default(), write_style: Default::default(), + built: false, } } @@ -104,6 +106,9 @@ impl Builder { /// Build a terminal writer. pub fn build(&mut self) -> Writer { + assert!(!self.built, "attempt to re-use consumed builder"); + self.built = true; + let color_choice = match self.write_style { WriteStyle::Auto => { if match self.target { diff --git a/src/lib.rs b/src/lib.rs index ede27a61..aa8b4201 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,6 +332,7 @@ pub struct Builder { filter: filter::Builder, writer: writer::Builder, format: fmt::Builder, + built: bool, } impl Builder { @@ -668,6 +669,9 @@ impl Builder { /// The returned logger implements the `Log` trait and can be installed manually /// or nested within another logger. pub fn build(&mut self) -> Logger { + assert!(!self.built, "attempt to re-use consumed builder"); + self.built = true; + Logger { writer: self.writer.build(), filter: self.filter.build(), From affbf9a7b7658ee37fd2aedc47df632ec1efec8f Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 4 Nov 2018 18:59:14 +1000 Subject: [PATCH 17/17] fix a typo --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5463365b..67c19aa1 100644 --- a/README.md +++ b/README.md @@ -143,4 +143,4 @@ builder.init(); The default format won't optimise for long-term stability, and explicitly makes no guarantees about the stability of its output across major, minor or patch version bumps during `0.x`. -If you want to capture or innterpret the output of `env_logger` programmatically then you should use a custom format. \ No newline at end of file +If you want to capture or interpret the output of `env_logger` programmatically then you should use a custom format. \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aa8b4201..89acf0da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,7 +183,7 @@ //! guarantees about the stability of its output across major, minor or patch version //! bumps during `0.x`. //! -//! If you want to capture or innterpret the output of `env_logger` programmatically +//! If you want to capture or interpret the output of `env_logger` programmatically //! then you should use a custom format. //! //! ## Specifying defaults for environment variables