diff --git a/.travis.yml b/.travis.yml index 5a6e0741..af8f18d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: rust sudo: false rust: - - 1.24.1 + - 1.31.0 - stable - beta - nightly diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f849eeff --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +Changes to this crate are tracked via [GitHub Releases][releases]. + +[releases]: https://github.com/sebasmagri/env_logger/releases diff --git a/Cargo.toml b/Cargo.toml index a7a3a0db..d0407187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "env_logger" -version = "0.6.0" # remember to update html_root_url +edition = "2018" +version = "0.7.0" # remember to update html_root_url authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -19,10 +20,10 @@ members = [ ] [dependencies] -log = { version = "0.4", features = ["std"] } +log = { version = "0.4.8", features = ["std"] } regex = { version = "1.0.3", optional = true } termcolor = { version = "1.0.2", optional = true } -humantime = { version = "1.1", optional = true } +humantime = { version = "1.3", optional = true } atty = { version = "0.2.5", optional = true } [[test]] @@ -38,4 +39,4 @@ name = "init-twice-retains-filter" harness = false [features] -default = ["termcolor", "atty", "humantime", "regex"] \ No newline at end of file +default = ["termcolor", "atty", "humantime", "regex"] diff --git a/README.md b/README.md index 496c5491..9a739fd6 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It must be added along with `log` to the project dependencies: ```toml [dependencies] log = "0.4.0" -env_logger = "0.6.0" +env_logger = "0.7.0" ``` `env_logger` must be initialized as early as possible in the project. After it's initialized, you can use the `log` macros to do actual logging. @@ -24,7 +24,6 @@ env_logger = "0.6.0" ```rust #[macro_use] extern crate log; -extern crate env_logger; fn main() { env_logger::init(); @@ -43,6 +42,8 @@ $ RUST_LOG=info ./main [2018-11-03T06:09:06Z INFO default] starting up ``` +`env_logger` can be configured in other ways besides an environment variable. See [the examples](https://github.com/sebasmagri/env_logger/tree/master/examples) for more approaches. + ### In tests Tests can use the `env_logger` crate to see log messages generated during that test: @@ -52,7 +53,7 @@ Tests can use the `env_logger` crate to see log messages generated during that t log = "0.4.0" [dev-dependencies] -env_logger = { version = "0.6.0", default-features = false } +env_logger = "0.7.0" ``` ```rust @@ -67,18 +68,23 @@ fn add_one(num: i32) -> i32 { #[cfg(test)] mod tests { use super::*; - extern crate env_logger; + + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + } #[test] fn it_adds_one() { - let _ = env_logger::try_init(); + init(); + info!("can log from the test too"); assert_eq!(3, add_one(2)); } #[test] fn it_handles_negative_numbers() { - let _ = env_logger::try_init(); + init(); + info!("logging from another test"); assert_eq!(-7, add_one(-8)); } @@ -141,4 +147,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 interpret 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. diff --git a/ci/Cargo.toml b/ci/Cargo.toml index 01cd779a..87258b6b 100644 --- a/ci/Cargo.toml +++ b/ci/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "ci" +edition = "2018" version = "0.0.0" authors = ["The Rust Project Developers"] publish = false diff --git a/ci/src/main.rs b/ci/src/main.rs index 7464599b..d3619ca1 100644 --- a/ci/src/main.rs +++ b/ci/src/main.rs @@ -1,13 +1,8 @@ -mod task; mod permute; +mod task; fn main() { - let features = [ - "termcolor", - "humantime", - "atty", - "regex", - ]; + let features = ["termcolor", "humantime", "atty", "regex"]; // Run a default build if !task::test(Default::default()) { @@ -25,12 +20,13 @@ fn main() { // Run a set of permutations let failed = permute::all(&features) .into_iter() - .filter(|features| + .filter(|features| { !task::test(task::TestArgs { features: features.clone(), default_features: false, lib_only: true, - })) + }) + }) .collect::>(); if failed.len() > 0 { diff --git a/ci/src/permute.rs b/ci/src/permute.rs index b78a2ec4..059d0864 100644 --- a/ci/src/permute.rs +++ b/ci/src/permute.rs @@ -1,6 +1,9 @@ use std::collections::BTreeSet; -pub fn all(input: &[T]) -> BTreeSet> where T: Ord + Eq + Clone { +pub fn all(input: &[T]) -> BTreeSet> +where + T: Ord + Eq + Clone, +{ let mut permutations = BTreeSet::new(); if input.len() == 0 { diff --git a/ci/src/task.rs b/ci/src/task.rs index aebc77f3..2b6fab91 100644 --- a/ci/src/task.rs +++ b/ci/src/task.rs @@ -1,8 +1,5 @@ use std::collections::BTreeSet; -use std::process::{ - Command, - Stdio, -}; +use std::process::{Command, Stdio}; pub type Feature = &'static str; @@ -45,7 +42,7 @@ pub fn test(args: TestArgs) -> bool { let features = args.features_string(); let mut command = Command::new("cargo"); - + command .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) @@ -65,13 +62,14 @@ pub fn test(args: TestArgs) -> bool { } println!("running {:?}", command); - - let status = command - .status() - .expect("Failed to execute command"); + + let status = command.status().expect("Failed to execute command"); if !status.success() { - eprintln!("test execution failed for features: {}", features.as_ref().map(AsRef::as_ref).unwrap_or("")); + eprintln!( + "test execution failed for features: {}", + features.as_ref().map(AsRef::as_ref).unwrap_or("") + ); false } else { true diff --git a/examples/custom_default_format.rs b/examples/custom_default_format.rs index d1a45b60..43979247 100644 --- a/examples/custom_default_format.rs +++ b/examples/custom_default_format.rs @@ -19,9 +19,8 @@ If you want to control the logging output completely, see the `custom_logger` ex #[macro_use] extern crate log; -extern crate env_logger; -use env_logger::{Env, Builder}; +use env_logger::{Builder, Env}; fn init_logger() { let env = Env::default() @@ -30,9 +29,7 @@ fn init_logger() { let mut builder = Builder::from_env(env); - builder - .default_format_level(false) - .default_format_timestamp_nanos(true); + builder.format_level(false).format_timestamp_nanos(); builder.init(); } diff --git a/examples/custom_format.rs b/examples/custom_format.rs index c4563f50..df5a8e5b 100644 --- a/examples/custom_format.rs +++ b/examples/custom_format.rs @@ -17,38 +17,37 @@ $ export MY_LOG_STYLE=never If you want to control the logging output completely, see the `custom_logger` example. */ -#[macro_use] -extern crate log; -extern crate env_logger; - -use std::io::Write; - -use env_logger::{Env, Builder, fmt}; - -fn init_logger() { - let env = Env::default() - .filter("MY_LOG_LEVEL") - .write_style("MY_LOG_STYLE"); - - let mut builder = Builder::from_env(env); - - // Use a different format for writing log records - // The colors are only available when the `termcolor` dependency is (which it is by default) - #[cfg(feature = "termcolor")] - builder.format(|buf, record| { - let mut style = buf.style(); - style.set_bg(fmt::Color::Yellow).set_bold(true); - - let timestamp = buf.timestamp(); - - writeln!(buf, "My formatted log ({}): {}", timestamp, style.value(record.args())) - }); - - builder.init(); -} - +#[cfg(all(feature = "termcolor", feature = "humantime"))] fn main() { + use env_logger::{fmt, Builder, Env}; + use std::io::Write; + + fn init_logger() { + let env = Env::default() + .filter("MY_LOG_LEVEL") + .write_style("MY_LOG_STYLE"); + + Builder::from_env(env) + .format(|buf, record| { + let mut style = buf.style(); + style.set_bg(fmt::Color::Yellow).set_bold(true); + + let timestamp = buf.timestamp(); + + writeln!( + buf, + "My formatted log ({}): {}", + timestamp, + style.value(record.args()) + ) + }) + .init(); + } + init_logger(); - info!("a log from `MyLogger`"); + log::info!("a log from `MyLogger`"); } + +#[cfg(not(all(feature = "termcolor", feature = "humantime")))] +fn main() {} diff --git a/examples/custom_logger.rs b/examples/custom_logger.rs index 792c9c8e..85de45b2 100644 --- a/examples/custom_logger.rs +++ b/examples/custom_logger.rs @@ -12,12 +12,12 @@ If you only want to change the way logs are formatted, look at the `custom_forma #[macro_use] extern crate log; -extern crate env_logger; + use env_logger::filter::Filter; use log::{Log, Metadata, Record, SetLoggerError}; struct MyLogger { - inner: Filter + inner: Filter, } impl MyLogger { @@ -26,7 +26,7 @@ impl MyLogger { let mut builder = Builder::from_env("MY_LOG_LEVEL"); MyLogger { - inner: builder.build() + inner: builder.build(), } } @@ -50,7 +50,7 @@ impl Log for MyLogger { } } - fn flush(&self) { } + fn flush(&self) {} } fn main() { diff --git a/examples/default.rs b/examples/default.rs index 5d799fe0..67bb0307 100644 --- a/examples/default.rs +++ b/examples/default.rs @@ -17,11 +17,13 @@ $ export MY_LOG_STYLE=never #[macro_use] extern crate log; -extern crate env_logger; use env_logger::Env; 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") .write_style_or("MY_LOG_STYLE", "always"); diff --git a/examples/direct_logger.rs b/examples/direct_logger.rs index 410230bc..4ba023fa 100644 --- a/examples/direct_logger.rs +++ b/examples/direct_logger.rs @@ -4,9 +4,6 @@ Using `env_logger::Logger` and the `log::Log` trait directly. This example doesn't rely on environment variables, or having a static logger installed. */ -extern crate log; -extern crate env_logger; - fn record() -> log::Record<'static> { let error_metadata = log::MetadataBuilder::new() .target("myApp") @@ -34,7 +31,7 @@ fn main() { .filter(None, log::LevelFilter::Error) .write_style(env_logger::WriteStyle::Never) .build(); - + stylish_logger.log(&record()); unstylish_logger.log(&record()); -} \ No newline at end of file +} diff --git a/examples/filters_from_code.rs b/examples/filters_from_code.rs new file mode 100644 index 00000000..4137c918 --- /dev/null +++ b/examples/filters_from_code.rs @@ -0,0 +1,18 @@ +/*! +Specify logging filters in code instead of using an environment variable. +*/ + +#[macro_use] +extern crate log; + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Trace) + .init(); + + trace!("some trace log"); + debug!("some debug log"); + info!("some information log"); + warn!("some warning log"); + error!("some error log"); +} diff --git a/src/filter/mod.rs b/src/filter/mod.rs index 65f413dd..a994f4dc 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -1,15 +1,15 @@ //! Filtering for log records. -//! +//! //! This module contains the log filtering used by `env_logger` to match records. -//! You can use the `Filter` type in your own logger implementation to use the same -//! filter parsing and matching as `env_logger`. For more details about the format +//! You can use the `Filter` type in your own logger implementation to use the same +//! filter parsing and matching as `env_logger`. For more details about the format //! for directive strings see [Enabling Logging]. -//! +//! //! ## Using `env_logger` in your own logger //! //! You can use `env_logger`'s filtering functionality with your own logger. -//! Call [`Builder::parse`] to parse directives from a string when constructing -//! your logger. Call [`Filter::matches`] to check whether a record should be +//! Call [`Builder::parse`] to parse directives from a string when constructing +//! your logger. Call [`Filter::matches`] to check whether a record should be //! logged based on the parsed filters when log records are received. //! //! ``` @@ -54,15 +54,15 @@ //! } //! # fn main() {} //! ``` -//! +//! //! [Enabling Logging]: ../index.html#enabling-logging //! [`Builder::parse`]: struct.Builder.html#method.parse //! [`Filter::matches`]: struct.Filter.html#method.matches +use log::{Level, LevelFilter, Metadata, Record}; use std::env; -use std::mem; use std::fmt; -use log::{Level, LevelFilter, Record, Metadata}; +use std::mem; #[cfg(feature = "regex")] #[path = "regex.rs"] @@ -73,11 +73,11 @@ mod inner; mod inner; /// A log filter. -/// +/// /// This struct can be used to determine whether or not a log record /// should be written to the output. /// Use the [`Builder`] type to parse and construct a `Filter`. -/// +/// /// [`Builder`]: struct.Builder.html pub struct Filter { directives: Vec, @@ -85,10 +85,10 @@ pub struct Filter { } /// A builder for a log filter. -/// +/// /// It can be used to parse a set of directives from a string before building /// a [`Filter`] instance. -/// +/// /// ## Example /// /// ``` @@ -111,7 +111,7 @@ pub struct Filter { /// let filter = builder.build(); /// } /// ``` -/// +/// /// [`Filter`]: struct.Filter.html pub struct Builder { directives: Vec, @@ -148,7 +148,8 @@ impl Filter { /// } /// ``` pub fn filter(&self) -> LevelFilter { - self.directives.iter() + self.directives + .iter() .map(|d| d.level) .max() .unwrap_or(LevelFilter::Off) @@ -213,9 +214,7 @@ impl Builder { /// /// The given module (if any) will log at most the specified level provided. /// If no module is provided then the filter will apply to all log messages. - pub fn filter(&mut self, - module: Option<&str>, - level: LevelFilter) -> &mut Self { + pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self { self.directives.push(Directive { name: module.map(|s| s.to_string()), level, @@ -226,7 +225,7 @@ impl Builder { /// Parses the directives string. /// /// See the [Enabling Logging] section for more details. - /// + /// /// [Enabling Logging]: ../index.html#enabling-logging pub fn parse(&mut self, filters: &str) -> &mut Self { let (directives, filter) = parse_spec(filters); @@ -274,7 +273,7 @@ impl Default for Builder { } impl fmt::Debug for Filter { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Filter") .field("filter", &self.filter) .field("directives", &self.directives) @@ -283,16 +282,14 @@ impl fmt::Debug for Filter { } impl fmt::Debug for Builder { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.built { - f.debug_struct("Filter") - .field("built", &true) - .finish() + f.debug_struct("Filter").field("built", &true).finish() } else { f.debug_struct("Filter") - .field("filter", &self.filter) - .field("directives", &self.directives) - .finish() + .field("filter", &self.filter) + .field("directives", &self.directives) + .finish() } } } @@ -306,68 +303,75 @@ fn parse_spec(spec: &str) -> (Vec, Option) { let mods = parts.next(); let filter = parts.next(); if parts.next().is_some() { - println!("warning: invalid logging spec '{}', \ - ignoring it (too many '/'s)", spec); + eprintln!( + "warning: invalid logging spec '{}', \ + ignoring it (too many '/'s)", + spec + ); return (dirs, None); } - mods.map(|m| { for s in m.split(',') { - if s.len() == 0 { continue } - let mut parts = s.split('='); - let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) { - (Some(part0), None, None) => { - // if the single argument is a log-level string or number, - // treat that as a global fallback - match part0.parse() { - Ok(num) => (num, None), - Err(_) => (LevelFilter::max(), Some(part0)), - } + mods.map(|m| { + for s in m.split(',') { + if s.len() == 0 { + continue; } - (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)), - (Some(part0), Some(part1), None) => { - match part1.parse() { - Ok(num) => (num, Some(part0)), + let mut parts = s.split('='); + let (log_level, name) = + match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) { + (Some(part0), None, None) => { + // if the single argument is a log-level string or number, + // treat that as a global fallback + match part0.parse() { + Ok(num) => (num, None), + Err(_) => (LevelFilter::max(), Some(part0)), + } + } + (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)), + (Some(part0), Some(part1), None) => match part1.parse() { + Ok(num) => (num, Some(part0)), + _ => { + eprintln!( + "warning: invalid logging spec '{}', \ + ignoring it", + part1 + ); + continue; + } + }, _ => { - println!("warning: invalid logging spec '{}', \ - ignoring it", part1); - continue + eprintln!( + "warning: invalid logging spec '{}', \ + ignoring it", + s + ); + continue; } - } - }, - _ => { - println!("warning: invalid logging spec '{}', \ - ignoring it", s); - continue - } - }; - dirs.push(Directive { - name: name.map(|s| s.to_string()), - level: log_level, - }); - }}); - - let filter = filter.map_or(None, |filter| { - match inner::Filter::new(filter) { - Ok(re) => Some(re), - Err(e) => { - println!("warning: invalid regex filter - {}", e); - None - } + }; + dirs.push(Directive { + name: name.map(|s| s.to_string()), + level: log_level, + }); + } + }); + + let filter = filter.map_or(None, |filter| match inner::Filter::new(filter) { + Ok(re) => Some(re), + Err(e) => { + eprintln!("warning: invalid regex filter - {}", e); + None } }); return (dirs, filter); } - // Check whether a level and target are enabled by the set of directives. fn enabled(directives: &[Directive], level: Level, target: &str) -> bool { // Search for the longest match, the vector is assumed to be pre-sorted. for directive in directives.iter().rev() { match directive.name { - Some(ref name) if !target.starts_with(&**name) => {}, - Some(..) | None => { - return level <= directive.level - } + Some(ref name) if !target.starts_with(&**name) => {} + Some(..) | None => return level <= directive.level, } } false @@ -377,7 +381,7 @@ fn enabled(directives: &[Directive], level: Level, target: &str) -> bool { mod tests { use log::{Level, LevelFilter}; - use super::{Builder, Filter, Directive, parse_spec, enabled}; + use super::{enabled, parse_spec, Builder, Directive, Filter}; fn make_logger_filter(dirs: Vec) -> Filter { let mut logger = Builder::new().build(); @@ -395,10 +399,10 @@ mod tests { #[test] fn filter_beginning_longest_match() { let logger = Builder::new() - .filter(Some("crate2"), LevelFilter::Info) - .filter(Some("crate2::mod"), LevelFilter::Debug) - .filter(Some("crate1::mod1"), LevelFilter::Warn) - .build(); + .filter(Some("crate2"), LevelFilter::Info) + .filter(Some("crate2::mod"), LevelFilter::Debug) + .filter(Some("crate1::mod1"), LevelFilter::Warn) + .build(); assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1")); assert!(!enabled(&logger.directives, Level::Debug, "crate2")); } @@ -415,12 +419,12 @@ mod tests { let logger = make_logger_filter(vec![ Directive { name: Some("crate2".to_string()), - level: LevelFilter::Info + level: LevelFilter::Info, }, Directive { name: Some("crate1::mod1".to_string()), - level: LevelFilter::Warn - } + level: LevelFilter::Warn, + }, ]); assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1")); assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1")); @@ -431,8 +435,14 @@ mod tests { #[test] fn no_match() { let logger = make_logger_filter(vec![ - Directive { name: Some("crate2".to_string()), level: LevelFilter::Info }, - Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn } + Directive { + name: Some("crate2".to_string()), + level: LevelFilter::Info, + }, + Directive { + name: Some("crate1::mod1".to_string()), + level: LevelFilter::Warn, + }, ]); assert!(!enabled(&logger.directives, Level::Warn, "crate3")); } @@ -440,8 +450,14 @@ mod tests { #[test] fn match_beginning() { let logger = make_logger_filter(vec![ - Directive { name: Some("crate2".to_string()), level: LevelFilter::Info }, - Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn } + Directive { + name: Some("crate2".to_string()), + level: LevelFilter::Info, + }, + Directive { + name: Some("crate1::mod1".to_string()), + level: LevelFilter::Warn, + }, ]); assert!(enabled(&logger.directives, Level::Info, "crate2::mod1")); } @@ -449,9 +465,18 @@ mod tests { #[test] fn match_beginning_longest_match() { let logger = make_logger_filter(vec![ - Directive { name: Some("crate2".to_string()), level: LevelFilter::Info }, - Directive { name: Some("crate2::mod".to_string()), level: LevelFilter::Debug }, - Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn } + Directive { + name: Some("crate2".to_string()), + level: LevelFilter::Info, + }, + Directive { + name: Some("crate2::mod".to_string()), + level: LevelFilter::Debug, + }, + Directive { + name: Some("crate1::mod1".to_string()), + level: LevelFilter::Warn, + }, ]); assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1")); assert!(!enabled(&logger.directives, Level::Debug, "crate2")); @@ -460,8 +485,14 @@ mod tests { #[test] fn match_default() { let logger = make_logger_filter(vec![ - Directive { name: None, level: LevelFilter::Info }, - Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn } + Directive { + name: None, + level: LevelFilter::Info, + }, + Directive { + name: Some("crate1::mod1".to_string()), + level: LevelFilter::Warn, + }, ]); assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1")); assert!(enabled(&logger.directives, Level::Info, "crate2::mod2")); @@ -470,8 +501,14 @@ mod tests { #[test] fn zero_level() { let logger = make_logger_filter(vec![ - Directive { name: None, level: LevelFilter::Info }, - Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Off } + Directive { + name: None, + level: LevelFilter::Info, + }, + Directive { + name: Some("crate1::mod1".to_string()), + level: LevelFilter::Off, + }, ]); assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1")); assert!(enabled(&logger.directives, Level::Info, "crate2::mod2")); diff --git a/src/filter/regex.rs b/src/filter/regex.rs index a0426541..fb21528a 100644 --- a/src/filter/regex.rs +++ b/src/filter/regex.rs @@ -11,7 +11,7 @@ pub struct Filter { impl Filter { pub fn new(spec: &str) -> Result { - match Regex::new(spec){ + match Regex::new(spec) { Ok(r) => Ok(Filter { inner: r }), Err(e) => Err(e.to_string()), } diff --git a/src/filter/string.rs b/src/filter/string.rs index 96d7ecca..ea476e42 100644 --- a/src/filter/string.rs +++ b/src/filter/string.rs @@ -7,7 +7,9 @@ pub struct Filter { impl Filter { pub fn new(spec: &str) -> Result { - Ok(Filter { inner: spec.to_string() }) + Ok(Filter { + inner: spec.to_string(), + }) } pub fn is_match(&self, s: &str) -> bool { diff --git a/src/fmt/humantime/extern_impl.rs b/src/fmt/humantime/extern_impl.rs index 596a2819..19dec1b6 100644 --- a/src/fmt/humantime/extern_impl.rs +++ b/src/fmt/humantime/extern_impl.rs @@ -1,11 +1,13 @@ use std::fmt; use std::time::SystemTime; -use humantime::{format_rfc3339_nanos, format_rfc3339_seconds}; +use humantime::{ + format_rfc3339_micros, format_rfc3339_millis, format_rfc3339_nanos, format_rfc3339_seconds, +}; -use ::fmt::Formatter; +use crate::fmt::{Formatter, TimestampPrecision}; -pub(in ::fmt) mod glob { +pub(in crate::fmt) mod glob { pub use super::*; } @@ -30,12 +32,46 @@ impl Formatter { /// /// [`Timestamp`]: struct.Timestamp.html pub fn timestamp(&self) -> Timestamp { - Timestamp(SystemTime::now()) + Timestamp { + time: SystemTime::now(), + precision: TimestampPrecision::Seconds, + } + } + + /// Get a [`Timestamp`] for the current date and time in UTC with full + /// second precision. + pub fn timestamp_seconds(&self) -> Timestamp { + Timestamp { + time: SystemTime::now(), + precision: TimestampPrecision::Seconds, + } + } + + /// Get a [`Timestamp`] for the current date and time in UTC with + /// millisecond precision. + pub fn timestamp_millis(&self) -> Timestamp { + Timestamp { + time: SystemTime::now(), + precision: TimestampPrecision::Millis, + } } - /// Get a [`PreciseTimestamp`] for the current date and time in UTC with nanos. - pub fn precise_timestamp(&self) -> PreciseTimestamp { - PreciseTimestamp(SystemTime::now()) + /// Get a [`Timestamp`] for the current date and time in UTC with + /// microsecond precision. + pub fn timestamp_micros(&self) -> Timestamp { + Timestamp { + time: SystemTime::now(), + precision: TimestampPrecision::Micros, + } + } + + /// Get a [`Timestamp`] for the current date and time in UTC with + /// nanosecond precision. + pub fn timestamp_nanos(&self) -> Timestamp { + Timestamp { + time: SystemTime::now(), + precision: TimestampPrecision::Nanos, + } } } @@ -46,13 +82,10 @@ impl Formatter { /// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt /// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html /// [`Formatter`]: struct.Formatter.html -pub struct Timestamp(SystemTime); - -/// An [RFC3339] formatted timestamp with nanos. -/// -/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt -#[derive(Debug)] -pub struct PreciseTimestamp(SystemTime); +pub struct Timestamp { + time: SystemTime, + precision: TimestampPrecision, +} impl fmt::Debug for Timestamp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -66,19 +99,20 @@ impl fmt::Debug for Timestamp { } f.debug_tuple("Timestamp") - .field(&TimestampValue(&self)) - .finish() + .field(&TimestampValue(&self)) + .finish() } } impl fmt::Display for Timestamp { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { - format_rfc3339_seconds(self.0).fmt(f) - } -} + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let formatter = match self.precision { + TimestampPrecision::Seconds => format_rfc3339_seconds, + TimestampPrecision::Millis => format_rfc3339_millis, + TimestampPrecision::Micros => format_rfc3339_micros, + TimestampPrecision::Nanos => format_rfc3339_nanos, + }; -impl fmt::Display for PreciseTimestamp { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { - format_rfc3339_nanos(self.0).fmt(f) + formatter(self.time).fmt(f) } -} \ No newline at end of file +} diff --git a/src/fmt/humantime/mod.rs b/src/fmt/humantime/mod.rs index c4f7599c..ac23ae24 100644 --- a/src/fmt/humantime/mod.rs +++ b/src/fmt/humantime/mod.rs @@ -8,4 +8,4 @@ Its public API is available when the `humantime` crate is available. #[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")] mod imp; -pub(in ::fmt) use self::imp::*; +pub(in crate::fmt) use self::imp::*; diff --git a/src/fmt/humantime/shim_impl.rs b/src/fmt/humantime/shim_impl.rs index 0f753400..906bf9e4 100644 --- a/src/fmt/humantime/shim_impl.rs +++ b/src/fmt/humantime/shim_impl.rs @@ -2,6 +2,4 @@ Timestamps aren't available when we don't have a `humantime` dependency. */ -pub(in ::fmt) mod glob { - -} +pub(in crate::fmt) mod glob {} diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 635995e2..e699e214 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -29,24 +29,48 @@ //! [`Builder::format`]: ../struct.Builder.html#method.format //! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html -use std::io::prelude::*; -use std::{io, fmt, mem}; -use std::rc::Rc; use std::cell::RefCell; use std::fmt::Display; +use std::io::prelude::*; +use std::rc::Rc; +use std::{fmt, io, mem}; use log::Record; -pub(crate) mod writer; mod humantime; +pub(crate) mod writer; pub use self::humantime::glob::*; pub use self::writer::glob::*; -use self::writer::{Writer, Buffer}; +use self::writer::{Buffer, Writer}; pub(crate) mod glob { - pub use super::{Target, WriteStyle}; + pub use super::{Target, TimestampPrecision, WriteStyle}; +} + +/// Formatting precision of timestamps. +/// +/// Seconds give precision of full seconds, milliseconds give thousands of a +/// second (3 decimal digits), microseconds are millionth of a second (6 decimal +/// digits) and nanoseconds are billionth of a second (9 decimal digits). +#[derive(Copy, Clone, Debug)] +pub enum TimestampPrecision { + /// Full second precision (0 decimal digits) + Seconds, + /// Millisecond precision (3 decimal digits) + Millis, + /// Microsecond precision (6 decimal digits) + Micros, + /// Nanosecond precision (9 decimal digits) + Nanos, +} + +/// The default timestamp precision is seconds. +impl Default for TimestampPrecision { + fn default() -> Self { + TimestampPrecision::Seconds + } } /// A formatter to write logs into. @@ -107,16 +131,17 @@ impl Write for Formatter { } impl fmt::Debug for Formatter { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + 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 format_timestamp: Option, + pub format_module_path: bool, + pub format_level: bool, + pub format_indent: Option, + #[allow(unknown_lints, bare_trait_objects)] pub custom_format: Option io::Result<()> + Sync + Send>>, built: bool, } @@ -124,10 +149,10 @@ pub(crate) struct Builder { impl Default for Builder { fn default() -> Self { Builder { - default_format_timestamp: true, - default_format_timestamp_nanos: false, - default_format_module_path: true, - default_format_level: true, + format_timestamp: Some(Default::default()), + format_module_path: true, + format_level: true, + format_indent: Some(4), custom_format: None, built: false, } @@ -136,29 +161,32 @@ impl Default for Builder { 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. + #[allow(unknown_lints, bare_trait_objects)] 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() - }); + let built = mem::replace( + self, + Builder { + built: true, + ..Default::default() + }, + ); if let Some(fmt) = built.custom_format { fmt - } - else { + } else { Box::new(move |buf, record| { let fmt = DefaultFormat { - timestamp: built.default_format_timestamp, - timestamp_nanos: built.default_format_timestamp_nanos, - module_path: built.default_format_module_path, - level: built.default_format_level, + timestamp: built.format_timestamp, + module_path: built.format_module_path, + level: built.format_level, written_header_value: false, + indent: built.format_indent, buf, }; @@ -174,14 +202,14 @@ type SubtleStyle = StyledValue<'static, &'static str>; type SubtleStyle = &'static str; /// The default format. -/// +/// /// This format needs to work with any combination of crate features. struct DefaultFormat<'a> { - timestamp: bool, + timestamp: Option, module_path: bool, level: bool, - timestamp_nanos: bool, written_header_value: bool, + indent: Option, buf: &'a mut Formatter, } @@ -198,7 +226,8 @@ impl<'a> DefaultFormat<'a> { fn subtle_style(&self, text: &'static str) -> SubtleStyle { #[cfg(feature = "termcolor")] { - self.buf.style() + self.buf + .style() .set_color(Color::Black) .set_intense(true) .into_value(text) @@ -225,7 +254,7 @@ impl<'a> DefaultFormat<'a> { fn write_level(&mut self, record: &Record) -> io::Result<()> { if !self.level { - return Ok(()) + return Ok(()); } let level = { @@ -245,29 +274,29 @@ impl<'a> DefaultFormat<'a> { 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) - } + use self::TimestampPrecision::*; + let ts = match self.timestamp { + None => return Ok(()), + Some(Seconds) => self.buf.timestamp_seconds(), + Some(Millis) => self.buf.timestamp_millis(), + Some(Micros) => self.buf.timestamp_micros(), + Some(Nanos) => self.buf.timestamp_nanos(), + }; + + self.write_header_value(ts) } #[cfg(not(feature = "humantime"))] { + // Trick the compiler to think we have used self.timestamp + // Workaround for "field is never used: `timestamp`" compiler nag. let _ = self.timestamp; - let _ = self.timestamp_nanos; Ok(()) } } fn write_module_path(&mut self, record: &Record) -> io::Result<()> { if !self.module_path { - return Ok(()) + return Ok(()); } if let Some(module_path) = record.module_path() { @@ -287,7 +316,51 @@ impl<'a> DefaultFormat<'a> { } fn write_args(&mut self, record: &Record) -> io::Result<()> { - writeln!(self.buf, "{}", record.args()) + match self.indent { + // Fast path for no indentation + None => writeln!(self.buf, "{}", record.args()), + + Some(indent_count) => { + // Create a wrapper around the buffer only if we have to actually indent the message + + struct IndentWrapper<'a, 'b: 'a> { + fmt: &'a mut DefaultFormat<'b>, + indent_count: usize, + } + + impl<'a, 'b> Write for IndentWrapper<'a, 'b> { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut first = true; + for chunk in buf.split(|&x| x == b'\n') { + if !first { + write!(self.fmt.buf, "\n{:width$}", "", width = self.indent_count)?; + } + self.fmt.buf.write_all(chunk)?; + first = false; + } + + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.fmt.buf.flush() + } + } + + // The explicit scope here is just to make older versions of Rust happy + { + let mut wrapper = IndentWrapper { + fmt: self, + indent_count, + }; + write!(wrapper, "{}", record.args())?; + } + + writeln!(self.buf)?; + + Ok(()) + } + } } } @@ -301,7 +374,7 @@ mod tests { let buf = fmt.buf.buf.clone(); let record = Record::builder() - .args(format_args!("log message")) + .args(format_args!("log\nmessage")) .level(Level::Info) .file(Some("test.rs")) .line(Some(144)) @@ -315,7 +388,7 @@ mod tests { } #[test] - fn default_format_with_header() { + fn format_with_header() { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); @@ -323,19 +396,19 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: true, level: true, written_header_value: false, + indent: None, buf: &mut f, }); - assert_eq!("[INFO test::path] log message\n", written); + assert_eq!("[INFO test::path] log\nmessage\n", written); } #[test] - fn default_format_no_header() { + fn format_no_header() { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); @@ -343,14 +416,74 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: false, level: false, written_header_value: false, + indent: None, buf: &mut f, }); - assert_eq!("log message\n", written); + assert_eq!("log\nmessage\n", written); } -} \ No newline at end of file + + #[test] + fn format_indent_spaces() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); + + let mut f = Formatter::new(&writer); + + let written = write(DefaultFormat { + timestamp: None, + module_path: true, + level: true, + written_header_value: false, + indent: Some(4), + buf: &mut f, + }); + + assert_eq!("[INFO test::path] log\n message\n", written); + } + + #[test] + fn format_indent_zero_spaces() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); + + let mut f = Formatter::new(&writer); + + let written = write(DefaultFormat { + timestamp: None, + module_path: true, + level: true, + written_header_value: false, + indent: Some(0), + buf: &mut f, + }); + + assert_eq!("[INFO test::path] log\nmessage\n", written); + } + + #[test] + fn format_indent_spaces_no_header() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); + + let mut f = Formatter::new(&writer); + + let written = write(DefaultFormat { + timestamp: None, + module_path: false, + level: false, + written_header_value: false, + indent: Some(4), + buf: &mut f, + }); + + assert_eq!("log\n message\n", written); + } +} diff --git a/src/fmt/writer/atty.rs b/src/fmt/writer/atty.rs index c441cf08..f6718413 100644 --- a/src/fmt/writer/atty.rs +++ b/src/fmt/writer/atty.rs @@ -11,24 +11,24 @@ from being printed. mod imp { use atty; - pub(in ::fmt) fn is_stdout() -> bool { + pub(in crate::fmt) fn is_stdout() -> bool { atty::is(atty::Stream::Stdout) } - pub(in ::fmt) fn is_stderr() -> bool { + pub(in crate::fmt) fn is_stderr() -> bool { atty::is(atty::Stream::Stderr) } } #[cfg(not(feature = "atty"))] mod imp { - pub(in ::fmt) fn is_stdout() -> bool { + pub(in crate::fmt) fn is_stdout() -> bool { false } - pub(in ::fmt) fn is_stderr() -> bool { + pub(in crate::fmt) fn is_stderr() -> bool { false } } -pub(in ::fmt) use self::imp::*; +pub(in crate::fmt) use self::imp::*; diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index d628187e..6ee63a39 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -1,16 +1,16 @@ -mod termcolor; mod atty; +mod termcolor; -use std::{fmt, io}; +use self::atty::{is_stderr, is_stdout}; use self::termcolor::BufferWriter; -use self::atty::{is_stdout, is_stderr}; +use std::{fmt, io}; -pub(in ::fmt) mod glob { +pub(in crate::fmt) mod glob { pub use super::termcolor::glob::*; pub use super::*; } -pub(in ::fmt) use self::termcolor::Buffer; +pub(in crate::fmt) use self::termcolor::Buffer; /// Log target, either `stdout` or `stderr`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -55,11 +55,11 @@ impl Writer { self.write_style } - pub(in ::fmt) fn buffer(&self) -> Buffer { + pub(in crate::fmt) fn buffer(&self) -> Buffer { self.inner.buffer() } - pub(in ::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> { + pub(in crate::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> { self.inner.print(buf) } } @@ -70,21 +70,23 @@ impl Writer { pub(crate) struct Builder { target: Target, write_style: WriteStyle, + is_test: bool, built: bool, } impl Builder { /// Initialize the writer builder with defaults. - pub fn new() -> Self { + pub(crate) fn new() -> Self { Builder { target: Default::default(), write_style: Default::default(), + is_test: false, built: false, } } /// Set the target to write to. - pub fn target(&mut self, target: Target) -> &mut Self { + pub(crate) fn target(&mut self, target: Target) -> &mut Self { self.target = target; self } @@ -94,18 +96,24 @@ impl Builder { /// See the [Disabling colors] section for more details. /// /// [Disabling colors]: ../index.html#disabling-colors - pub fn parse(&mut self, write_style: &str) -> &mut Self { + pub(crate) fn parse_write_style(&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 { + pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self { self.write_style = write_style; self } + /// Whether or not to capture logs for `cargo test`. + pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self { + self.is_test = is_test; + self + } + /// Build a terminal writer. - pub fn build(&mut self) -> Writer { + pub(crate) fn build(&mut self) -> Writer { assert!(!self.built, "attempt to re-use consumed builder"); self.built = true; @@ -119,13 +127,13 @@ impl Builder { } else { WriteStyle::Never } - }, + } color_choice => color_choice, }; let writer = match self.target { - Target::Stderr => BufferWriter::stderr(color_choice), - Target::Stdout => BufferWriter::stdout(color_choice), + Target::Stderr => BufferWriter::stderr(self.is_test, color_choice), + Target::Stdout => BufferWriter::stdout(self.is_test, color_choice), }; Writer { @@ -142,16 +150,16 @@ impl Default for Builder { } impl fmt::Debug for Builder { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Logger") - .field("target", &self.target) - .field("write_style", &self.write_style) - .finish() + .field("target", &self.target) + .field("write_style", &self.write_style) + .finish() } } impl fmt::Debug for Writer { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Writer").finish() } } @@ -184,12 +192,7 @@ mod tests { #[test] fn parse_write_style_invalid() { - let inputs = vec![ - "", - "true", - "false", - "NEVER!!" - ]; + let inputs = vec!["", "true", "false", "NEVER!!"]; for input in inputs { assert_eq!(WriteStyle::Auto, parse_write_style(input)); diff --git a/src/fmt/writer/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs index 236e163b..2d38e375 100644 --- a/src/fmt/writer/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -1,16 +1,15 @@ use std::borrow::Cow; +use std::cell::RefCell; use std::fmt; use std::io::{self, Write}; -use std::cell::RefCell; use std::rc::Rc; use log::Level; use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; -use ::WriteStyle; -use ::fmt::Formatter; +use crate::fmt::{Formatter, Target, WriteStyle}; -pub(in ::fmt::writer) mod glob { +pub(in crate::fmt::writer) mod glob { pub use super::*; } @@ -47,7 +46,7 @@ impl Formatter { } /// Get the default [`Style`] for the given level. - /// + /// /// The style can be used to print other values besides the level. pub fn default_level_style(&self, level: Level) -> Style { let mut level_style = self.style(); @@ -62,58 +61,97 @@ impl Formatter { } /// Get a printable [`Style`] for the given level. - /// + /// /// The style can only be used to print the level. pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> { self.default_level_style(level).into_value(level) } } -pub(in ::fmt::writer) struct BufferWriter(termcolor::BufferWriter); -pub(in ::fmt) struct Buffer(termcolor::Buffer); +pub(in crate::fmt::writer) struct BufferWriter { + inner: termcolor::BufferWriter, + test_target: Option, +} + +pub(in crate::fmt) struct Buffer { + inner: termcolor::Buffer, + test_target: Option, +} impl BufferWriter { - pub(in ::fmt::writer) fn stderr(write_style: WriteStyle) -> Self { - BufferWriter(termcolor::BufferWriter::stderr(write_style.into_color_choice())) + 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 }, + } } - pub(in ::fmt::writer) fn stdout(write_style: WriteStyle) -> Self { - BufferWriter(termcolor::BufferWriter::stdout(write_style.into_color_choice())) + 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 }, + } } - pub(in ::fmt::writer) fn buffer(&self) -> Buffer { - Buffer(self.0.buffer()) + pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { + Buffer { + inner: self.inner.buffer(), + test_target: self.test_target, + } } - pub(in ::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { - self.0.print(&buf.0) + pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { + 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` + let log = String::from_utf8_lossy(buf.bytes()); + + match target { + Target::Stderr => eprint!("{}", log), + Target::Stdout => print!("{}", log), + } + + Ok(()) + } else { + self.inner.print(&buf.inner) + } } } impl Buffer { - pub(in ::fmt) fn clear(&mut self) { - self.0.clear() + pub(in crate::fmt) fn clear(&mut self) { + self.inner.clear() } - pub(in ::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) + pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) } - pub(in ::fmt) fn flush(&mut self) -> io::Result<()> { - self.0.flush() + pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { + self.inner.flush() } - #[cfg(test)] - pub(in ::fmt) fn bytes(&self) -> &[u8] { - self.0.as_slice() + pub(in crate::fmt) fn bytes(&self) -> &[u8] { + self.inner.as_slice() } fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.0.set_color(spec) + // Ignore styles for test captured logs because they can't be printed + if self.test_target.is_none() { + self.inner.set_color(spec) + } else { + Ok(()) + } } fn reset(&mut self) -> io::Result<()> { - self.0.reset() + // Ignore styles for test captured logs because they can't be printed + if self.test_target.is_none() { + self.inner.reset() + } else { + Ok(()) + } } } @@ -327,7 +365,7 @@ impl Style { pub fn value(&self, value: T) -> StyledValue { StyledValue { style: Cow::Borrowed(self), - value + value, } } @@ -335,7 +373,7 @@ impl Style { pub(crate) fn into_value(&mut self, value: T) -> StyledValue<'static, T> { StyledValue { style: Cow::Owned(self.clone()), - value + value, } } } @@ -345,7 +383,11 @@ impl<'a, T> StyledValue<'a, T> { where F: FnOnce() -> fmt::Result, { - self.style.buf.borrow_mut().set_color(&self.style.spec).map_err(|_| fmt::Error)?; + self.style + .buf + .borrow_mut() + .set_color(&self.style.spec) + .map_err(|_| fmt::Error)?; // Always try to reset the terminal style, even if writing failed let write = f(); @@ -356,7 +398,7 @@ impl<'a, T> StyledValue<'a, T> { } impl fmt::Debug for Style { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Style").field("spec", &self.spec).finish() } } @@ -382,7 +424,8 @@ impl_styled_value_fmt!( fmt::UpperHex, fmt::LowerHex, fmt::UpperExp, - fmt::LowerExp); + fmt::LowerExp +); // The `Color` type is copied from https://github.com/BurntSushi/ripgrep/tree/master/termcolor diff --git a/src/fmt/writer/termcolor/mod.rs b/src/fmt/writer/termcolor/mod.rs index df0f7859..f3e6768c 100644 --- a/src/fmt/writer/termcolor/mod.rs +++ b/src/fmt/writer/termcolor/mod.rs @@ -9,4 +9,4 @@ The terminal printing is shimmed when the `termcolor` crate is not available. #[cfg_attr(not(feature = "termcolor"), path = "shim_impl.rs")] mod imp; -pub(in ::fmt) use self::imp::*; +pub(in crate::fmt) use self::imp::*; diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index b785dec0..563f8ad4 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -1,35 +1,33 @@ use std::io; -use fmt::{WriteStyle, Target}; +use crate::fmt::{Target, WriteStyle}; -pub(in ::fmt::writer) mod glob { - -} +pub(in crate::fmt::writer) mod glob {} -pub(in ::fmt::writer) struct BufferWriter { +pub(in crate::fmt::writer) struct BufferWriter { target: Target, } -pub(in ::fmt) struct Buffer(Vec); +pub(in crate::fmt) struct Buffer(Vec); impl BufferWriter { - pub(in ::fmt::writer) fn stderr(_: WriteStyle) -> Self { + pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { target: Target::Stderr, } } - pub(in ::fmt::writer) fn stdout(_: WriteStyle) -> Self { + pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { target: Target::Stdout, } } - pub(in ::fmt::writer) fn buffer(&self) -> Buffer { + pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { Buffer(Vec::new()) } - pub(in ::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { + 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` @@ -45,21 +43,21 @@ impl BufferWriter { } impl Buffer { - pub(in ::fmt) fn clear(&mut self) { + pub(in crate::fmt) fn clear(&mut self) { self.0.clear(); } - pub(in ::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { + pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { self.0.extend(buf); Ok(buf.len()) } - pub(in ::fmt) fn flush(&mut self) -> io::Result<()> { + pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { Ok(()) } #[cfg(test)] - pub(in ::fmt) fn bytes(&self) -> &[u8] { + pub(in crate::fmt) fn bytes(&self) -> &[u8] { &self.0 } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 355b687f..3679745c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ //! //! ``` //! #[macro_use] extern crate log; -//! extern crate env_logger; //! //! use log::Level; //! @@ -140,6 +139,34 @@ //! warn for hello. In both cases the log message must include a single digit //! number followed by 'scopes'. //! +//! ## Capturing logs in tests +//! +//! Records logged during `cargo test` will not be captured by the test harness by default. +//! The [`Builder::is_test`] method can be used in unit tests to ensure logs will be captured: +//! +//! ``` +//! # #[macro_use] extern crate log; +//! # fn main() {} +//! #[cfg(test)] +//! mod tests { +//! fn init() { +//! let _ = env_logger::builder().is_test(true).try_init(); +//! } +//! +//! #[test] +//! fn it_works() { +//! init(); +//! +//! info!("This record will be captured by `cargo test`"); +//! +//! assert_eq!(2, 1 + 1); +//! } +//! } +//! ``` +//! +//! Enabling test capturing comes at the expense of color and other style support +//! and may have performance implications. +//! //! ## Disabling colors //! //! Colors and other styles can be configured with the `RUST_LOG_STYLE` @@ -150,91 +177,78 @@ //! * `always` will always print style characters even if they aren't supported by the terminal. //! This includes emitting ANSI colors on Windows if the console API is unavailable. //! * `never` will never print style characters. -//! +//! //! ## Tweaking the default format -//! +//! //! Parts of the default format can be excluded from the log output using the [`Builder`]. //! The following example excludes the timestamp from the log output: -//! -//! ``` -//! use env_logger::Builder; //! -//! Builder::from_default_env() -//! .default_format_timestamp(false) +//! ``` +//! env_logger::builder() +//! .format_timestamp(None) //! .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 +//! +//! 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 interpret 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. -//! +//! //! ### Using a custom format -//! +//! //! Custom formats can be provided as closures to the [`Builder`]. //! These closures take a [`Formatter`] and `log::Record` as arguments: -//! +//! //! ``` //! use std::io::Write; -//! use env_logger::Builder; //! -//! Builder::from_default_env() +//! env_logger::builder() //! .format(|buf, record| { //! writeln!(buf, "{}: {}", record.level(), record.args()) //! }) //! .init(); //! ``` -//! +//! //! See the [`fmt`] module for more details about custom formats. -//! +//! //! ## Specifying defaults for environment variables -//! +//! //! `env_logger` can read configuration from environment variables. //! If these variables aren't present, the default value to use can be tweaked with the [`Env`] type. //! The following example defaults to log `warn` and above if the `RUST_LOG` environment variable //! isn't set: -//! +//! //! ``` -//! use env_logger::{Builder, Env}; +//! use env_logger::Env; //! -//! Builder::from_env(Env::default().default_filter_or("warn")).init(); +//! env_logger::from_env(Env::default().default_filter_or("warn")).init(); //! ``` -//! +//! //! [log-crate-url]: https://docs.rs/log/ //! [`Builder`]: struct.Builder.html +//! [`Builder::is_test`]: struct.Builder.html#method.is_test //! [`Env`]: struct.Env.html //! [`fmt`]: fmt/index.html -#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "http://www.rust-lang.org/favicon.ico", - html_root_url = "https://docs.rs/env_logger/0.6.0")] +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/static/images/favicon.ico", + html_root_url = "https://docs.rs/env_logger/0.7.0" +)] #![cfg_attr(test, deny(warnings))] - // When compiled for the rustc compiler itself we want to make sure that this is // an unstable crate #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] - #![deny(missing_debug_implementations, missing_docs, warnings)] -extern crate log; - -#[cfg(feature = "termcolor")] -extern crate termcolor; -#[cfg(feature = "humantime")] -extern crate humantime; -#[cfg(feature = "atty")] -extern crate atty; - -use std::{env, io}; -use std::borrow::Cow; -use std::cell::RefCell; +use std::{borrow::Cow, cell::RefCell, env, io}; -use log::{Log, LevelFilter, Record, SetLoggerError, Metadata}; +use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; pub mod filter; pub mod fmt; @@ -242,8 +256,8 @@ pub mod fmt; pub use self::fmt::glob::*; use self::filter::Filter; -use self::fmt::Formatter; use self::fmt::writer::{self, Writer}; +use self::fmt::Formatter; /// The default name for the environment variable to read filters from. pub const DEFAULT_FILTER_ENV: &'static str = "RUST_LOG"; @@ -295,6 +309,7 @@ struct Var<'a> { pub struct Logger { writer: Writer, filter: Filter, + #[allow(unknown_lints, bare_trait_objects)] format: Box io::Result<()> + Sync + Send>, } @@ -306,9 +321,7 @@ pub struct Logger { /// # Examples /// /// ``` -/// #[macro_use] -/// extern crate log; -/// extern crate env_logger; +/// #[macro_use] extern crate log; /// /// use std::env; /// use std::io::Write; @@ -336,30 +349,28 @@ pub struct Builder { impl Builder { /// Initializes the log builder with defaults. - /// + /// /// **NOTE:** This method won't read from any environment variables. /// Use the [`filter`] and [`write_style`] methods to configure the builder /// or use [`from_env`] or [`from_default_env`] instead. - /// + /// /// # Examples - /// + /// /// Create a new builder and configure filters and style: - /// + /// /// ``` - /// # extern crate log; - /// # extern crate env_logger; /// # fn main() { /// use log::LevelFilter; /// use env_logger::{Builder, WriteStyle}; - /// + /// /// let mut builder = Builder::new(); - /// + /// /// builder.filter(None, LevelFilter::Info) /// .write_style(WriteStyle::Always) /// .init(); /// # } /// ``` - /// + /// /// [`filter`]: #method.filter /// [`write_style`]: #method.write_style /// [`from_env`]: #method.from_env @@ -374,13 +385,13 @@ impl Builder { /// passing in. /// /// # Examples - /// + /// /// Initialise a logger reading the log filter from an environment variable /// called `MY_LOG`: - /// + /// /// ``` /// use env_logger::Builder; - /// + /// /// let mut builder = Builder::from_env("MY_LOG"); /// builder.init(); /// ``` @@ -398,13 +409,13 @@ impl Builder { /// ``` pub fn from_env<'a, E>(env: E) -> Self where - E: Into> + E: Into>, { let mut builder = Builder::new(); let env = env.into(); if let Some(s) = env.get_filter() { - builder.parse(&s); + builder.parse_filters(&s); } if let Some(s) = env.get_write_style() { @@ -415,18 +426,18 @@ impl Builder { } /// Initializes the log builder from the environment using default variable names. - /// + /// /// This method is a convenient way to call `from_env(Env::default())` without /// having to use the `Env` type explicitly. The builder will use the /// [default environment variables]. - /// + /// /// # Examples - /// + /// /// Initialise a logger using the default environment variables: - /// + /// /// ``` /// use env_logger::Builder; - /// + /// /// let mut builder = Builder::from_default_env(); /// builder.init(); /// ``` @@ -445,17 +456,17 @@ impl Builder { /// `Formatter` so that implementations can use the [`std::fmt`] macros /// to format and output without intermediate heap allocations. The default /// `env_logger` formatter takes advantage of this. - /// + /// /// # Examples - /// + /// /// Use a custom format to write only the log message: - /// + /// /// ``` /// use std::io::Write; /// use env_logger::Builder; - /// + /// /// let mut builder = Builder::new(); - /// + /// /// builder.format(|buf, record| writeln!(buf, "{}", record.args())); /// ``` /// @@ -463,44 +474,66 @@ impl Builder { /// [`String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html pub fn format(&mut self, format: F) -> &mut Self - where F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send + where + F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send, { self.format.custom_format = Some(Box::new(format)); self } /// Use the default format. - /// + /// /// This method will clear any custom format set on the builder. pub fn default_format(&mut self) -> &mut Self { - self.format.custom_format = None; + self.format = Default::default(); self } /// Whether or not to write the level in the default format. - pub fn default_format_level(&mut self, write: bool) -> &mut Self { - self.format.default_format_level = write; + pub fn format_level(&mut self, write: bool) -> &mut Self { + self.format.format_level = write; self } /// Whether or not to write the module path in the default format. - pub fn default_format_module_path(&mut self, write: bool) -> &mut Self { - self.format.default_format_module_path = write; + pub fn format_module_path(&mut self, write: bool) -> &mut Self { + self.format.format_module_path = write; self } - /// Whether or not to write the timestamp in the default format. - pub fn default_format_timestamp(&mut self, write: bool) -> &mut Self { - self.format.default_format_timestamp = write; + /// Configures the amount of spaces to use to indent multiline log records. + /// A value of `None` disables any kind of indentation. + pub fn format_indent(&mut self, indent: Option) -> &mut Self { + self.format.format_indent = indent; self } - /// Whether or not to write the timestamp with nanos. - pub fn default_format_timestamp_nanos(&mut self, write: bool) -> &mut Self { - self.format.default_format_timestamp_nanos = write; + /// Configures if timestamp should be included and in what precision. + pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { + self.format.format_timestamp = timestamp; self } + /// Configures the timestamp to use second precision. + pub fn format_timestamp_secs(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Seconds)) + } + + /// Configures the timestamp to use millisecond precision. + pub fn format_timestamp_millis(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Millis)) + } + + /// Configures the timestamp to use microsecond precision. + pub fn format_timestamp_micros(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Micros)) + } + + /// Configures the timestamp to use nanosecond precision. + pub fn format_timestamp_nanos(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampPrecision::Nanos)) + } + /// Adds a directive to the filter for a specific module. /// /// # Examples @@ -508,8 +541,6 @@ impl Builder { /// Only include messages for warning and above for logs in `path::to::module`: /// /// ``` - /// # extern crate log; - /// # extern crate env_logger; /// # fn main() { /// use log::LevelFilter; /// use env_logger::Builder; @@ -531,8 +562,6 @@ impl Builder { /// Only include messages for warning and above for logs in `path::to::module`: /// /// ``` - /// # extern crate log; - /// # extern crate env_logger; /// # fn main() { /// use log::LevelFilter; /// use env_logger::Builder; @@ -551,26 +580,22 @@ impl Builder { /// /// The given module (if any) will log at most the specified level provided. /// If no module is provided then the filter will apply to all log messages. - /// + /// /// # Examples - /// + /// /// Only include messages for warning and above for logs in `path::to::module`: - /// + /// /// ``` - /// # extern crate log; - /// # extern crate env_logger; /// # fn main() { /// use log::LevelFilter; /// use env_logger::Builder; - /// + /// /// let mut builder = Builder::new(); - /// + /// /// builder.filter(Some("path::to::module"), LevelFilter::Info); /// # } /// ``` - pub fn filter(&mut self, - module: Option<&str>, - level: LevelFilter) -> &mut Self { + pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self { self.filter.filter(module, level); self } @@ -579,7 +604,7 @@ impl Builder { /// environment variable. /// /// See the module documentation for more details. - pub fn parse(&mut self, filters: &str) -> &mut Self { + pub fn parse_filters(&mut self, filters: &str) -> &mut Self { self.filter.parse(filters); self } @@ -587,16 +612,16 @@ impl Builder { /// Sets the target for the log output. /// /// Env logger can log to either stdout or stderr. The default is stderr. - /// + /// /// # Examples - /// + /// /// Write log message to `stdout`: - /// + /// /// ``` /// use env_logger::{Builder, Target}; - /// + /// /// let mut builder = Builder::new(); - /// + /// /// builder.target(Target::Stdout); /// ``` pub fn target(&mut self, target: fmt::Target) -> &mut Self { @@ -608,16 +633,16 @@ impl Builder { /// /// This can be useful in environments that don't support control characters /// for setting colors. - /// + /// /// # Examples - /// + /// /// Never attempt to write styles: - /// + /// /// ``` /// use env_logger::{Builder, WriteStyle}; - /// + /// /// let mut builder = Builder::new(); - /// + /// /// builder.write_style(WriteStyle::Never); /// ``` pub fn write_style(&mut self, write_style: fmt::WriteStyle) -> &mut Self { @@ -630,7 +655,16 @@ impl Builder { /// /// See the module documentation for more details. pub fn parse_write_style(&mut self, write_style: &str) -> &mut Self { - self.writer.parse(write_style); + self.writer.parse_write_style(write_style); + self + } + + /// Sets whether or not the logger will be used in unit tests. + /// + /// If `is_test` is `true` then the logger will allow the testing framework to + /// capture log records rather than printing them to the terminal directly. + pub fn is_test(&mut self, is_test: bool) -> &mut Self { + self.writer.is_test(is_test); self } @@ -666,7 +700,8 @@ impl Builder { /// This function will panic if it is called more than once, or if another /// library has already initialized a global logger. pub fn init(&mut self) { - self.try_init().expect("Builder::init should not be called after logger initialized"); + self.try_init() + .expect("Builder::init should not be called after logger initialized"); } /// Build an env logger. @@ -713,8 +748,8 @@ impl Logger { /// let logger = Logger::from_env(env); /// ``` pub fn from_env<'a, E>(env: E) -> Self - where - E: Into> + where + E: Into>, { Builder::from_env(env).build() } @@ -794,14 +829,15 @@ impl Log for Logger { if formatter.write_style() != self.writer.write_style() { *formatter = Formatter::new(&self.writer) } - }, - ref mut tl_buf => *tl_buf = Some(Formatter::new(&self.writer)) + } + ref mut tl_buf => *tl_buf = Some(Formatter::new(&self.writer)), } // The format is guaranteed to be `Some` by this point let mut formatter = tl_buf.as_mut().unwrap(); - let _ = (self.format)(&mut formatter, record).and_then(|_| formatter.print(&self.writer)); + let _ = (self.format)(&mut formatter, record) + .and_then(|_| formatter.print(&self.writer)); // Always clear the buffer afterwards formatter.clear(); @@ -821,7 +857,7 @@ impl<'a> Env<'a> { /// Specify an environment variable to read the filter from. pub fn filter(mut self, filter_env: E) -> Self where - E: Into> + E: Into>, { self.filter = Var::new(filter_env); @@ -842,7 +878,7 @@ impl<'a> Env<'a> { } /// Use the default environment variable to read the filter from. - /// + /// /// If the variable is not set, the default value will be used. pub fn default_filter_or(mut self, default: V) -> Self where @@ -860,7 +896,7 @@ impl<'a> Env<'a> { /// Specify an environment variable to read the style from. pub fn write_style(mut self, write_style_env: E) -> Self where - E: Into> + E: Into>, { self.write_style = Var::new(write_style_env); @@ -871,9 +907,9 @@ impl<'a> Env<'a> { /// /// If the variable is not set, the default value will be used. pub fn write_style_or(mut self, write_style_env: E, default: V) -> Self - where - E: Into>, - V: Into>, + where + E: Into>, + V: Into>, { self.write_style = Var::new_with_default(write_style_env, default); @@ -884,8 +920,8 @@ impl<'a> Env<'a> { /// /// If the variable is not set, the default value will be used. pub fn default_write_style_or(mut self, default: V) -> Self - where - V: Into>, + where + V: Into>, { self.write_style = Var::new_with_default(DEFAULT_WRITE_STYLE_ENV, default); @@ -899,8 +935,8 @@ impl<'a> Env<'a> { impl<'a> Var<'a> { fn new(name: E) -> Self - where - E: Into>, + where + E: Into>, { Var { name: name.into(), @@ -922,15 +958,13 @@ impl<'a> Var<'a> { fn get(&self) -> Option { env::var(&*self.name) .ok() - .or_else(|| self.default - .to_owned() - .map(|v| v.into_owned())) + .or_else(|| self.default.to_owned().map(|v| v.into_owned())) } } impl<'a, T> From for Env<'a> where - T: Into> + T: Into>, { fn from(filter_env: T) -> Self { Env::default().filter(filter_env.into()) @@ -947,28 +981,26 @@ impl<'a> Default for Env<'a> { } mod std_fmt_impls { - use std::fmt; use super::*; + use std::fmt; - impl fmt::Debug for Logger{ - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + impl fmt::Debug for Logger { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Logger") .field("filter", &self.filter) .finish() } } - impl fmt::Debug for Builder{ - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + impl fmt::Debug for Builder { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.built { - f.debug_struct("Logger") - .field("built", &true) - .finish() + f.debug_struct("Logger").field("built", &true).finish() } else { f.debug_struct("Logger") - .field("filter", &self.filter) - .field("writer", &self.writer) - .finish() + .field("filter", &self.filter) + .field("writer", &self.writer) + .finish() } } } @@ -1031,7 +1063,7 @@ pub fn init() { /// library has already initialized a global logger. pub fn try_init_from_env<'a, E>(env: E) -> Result<(), SetLoggerError> where - E: Into> + E: Into>, { let mut builder = Builder::from_env(env); @@ -1063,9 +1095,27 @@ where /// library has already initialized a global logger. pub fn init_from_env<'a, E>(env: E) where - E: Into> + E: Into>, +{ + try_init_from_env(env) + .expect("env_logger::init_from_env should not be called after logger initialized"); +} + +/// Create a new builder with the default environment variables. +/// +/// The builder can be configured before being initialized. +pub fn builder() -> Builder { + Builder::from_default_env() +} + +/// Create a builder from the given environment variables. +/// +/// The builder can be configured before being initialized. +pub fn from_env<'a, E>(env: E) -> Builder +where + E: Into>, { - try_init_from_env(env).expect("env_logger::init_from_env should not be called after logger initialized"); + Builder::from_env(env) } #[cfg(test)] @@ -1085,7 +1135,10 @@ mod tests { fn env_get_filter_reads_from_default_if_var_not_set() { env::remove_var("env_get_filter_reads_from_default_if_var_not_set"); - let env = Env::new().filter_or("env_get_filter_reads_from_default_if_var_not_set", "from default"); + let env = Env::new().filter_or( + "env_get_filter_reads_from_default_if_var_not_set", + "from default", + ); assert_eq!(Some("from default".to_owned()), env.get_filter()); } @@ -1094,7 +1147,8 @@ mod tests { fn env_get_write_style_reads_from_var_if_set() { env::set_var("env_get_write_style_reads_from_var_if_set", "from var"); - let env = Env::new().write_style_or("env_get_write_style_reads_from_var_if_set", "from default"); + let env = + Env::new().write_style_or("env_get_write_style_reads_from_var_if_set", "from default"); assert_eq!(Some("from var".to_owned()), env.get_write_style()); } @@ -1103,7 +1157,10 @@ mod tests { fn env_get_write_style_reads_from_default_if_var_not_set() { env::remove_var("env_get_write_style_reads_from_default_if_var_not_set"); - let env = Env::new().write_style_or("env_get_write_style_reads_from_default_if_var_not_set", "from default"); + let env = Env::new().write_style_or( + "env_get_write_style_reads_from_default_if_var_not_set", + "from default", + ); assert_eq!(Some("from default".to_owned()), env.get_write_style()); } diff --git a/tests/init-twice-retains-filter.rs b/tests/init-twice-retains-filter.rs index 6ab08e99..673da3fd 100644 --- a/tests/init-twice-retains-filter.rs +++ b/tests/init-twice-retains-filter.rs @@ -1,8 +1,8 @@ -extern crate log; extern crate env_logger; +extern crate log; -use std::process; use std::env; +use std::process; use std::str; fn main() { @@ -15,12 +15,12 @@ fn main() { // Init again using a different max level // This shouldn't clobber the level that was previously set env_logger::Builder::new() - .parse("info") + .parse_filters("info") .try_init() .unwrap_err(); assert_eq!(log::LevelFilter::Debug, log::max_level()); - return + return; } let exe = env::current_exe().unwrap(); @@ -30,7 +30,7 @@ fn main() { .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); if out.status.success() { - return + return; } println!("test failed: {}", out.status); diff --git a/tests/log-in-log.rs b/tests/log-in-log.rs index 6b2c47e7..89517ff3 100644 --- a/tests/log-in-log.rs +++ b/tests/log-in-log.rs @@ -1,9 +1,10 @@ -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate env_logger; -use std::process; -use std::fmt; use std::env; +use std::fmt; +use std::process; use std::str; struct Foo; @@ -28,7 +29,7 @@ fn main() { .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); if out.status.success() { - return + return; } println!("test failed: {}", out.status); diff --git a/tests/regexp_filter.rs b/tests/regexp_filter.rs index d23e9223..40178bac 100644 --- a/tests/regexp_filter.rs +++ b/tests/regexp_filter.rs @@ -1,8 +1,9 @@ -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate env_logger; -use std::process; use std::env; +use std::process; use std::str; fn main() { @@ -25,7 +26,9 @@ fn run_child(rust_log: String) -> bool { .env("RUST_LOG", rust_log) .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); - str::from_utf8(out.stderr.as_ref()).unwrap().contains("XYZ Message") + str::from_utf8(out.stderr.as_ref()) + .unwrap() + .contains("XYZ Message") } fn assert_message_printed(rust_log: &str) { @@ -36,7 +39,10 @@ fn assert_message_printed(rust_log: &str) { fn assert_message_not_printed(rust_log: &str) { if run_child(rust_log.to_string()) { - panic!("RUST_LOG={} should not allow the test log message", rust_log) + panic!( + "RUST_LOG={} should not allow the test log message", + rust_log + ) } }