diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:00:14 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:00:14 +0000 |
commit | e79c3a39720482d94bd5e906fd018835ca675f14 (patch) | |
tree | dfc3b3908fa097798973ffea9e1bb600e9646bdb | |
parent | becfaa56a65edcb8a11a4e86e9931fd15f1dd9b1 (diff) | |
parent | 48a125a2ebdd03b00350bb00d56e6c2c660c56c0 (diff) | |
download | env_logger-e79c3a39720482d94bd5e906fd018835ca675f14.tar.gz |
Snap for 8564071 from 48a125a2ebdd03b00350bb00d56e6c2c660c56c0 to mainline-resolv-releaseaml_res_331820000aml_res_331611010aml_res_331512000aml_res_331314010aml_res_331114000aml_res_331011050aml_res_330910000aml_res_330810000android13-mainline-resolv-release
Change-Id: I2e64cfe30f21490ef09ffdffa5ef7e342c985d29
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 83 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | Cargo.toml.orig | 2 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | TEST_MAPPING | 114 | ||||
-rw-r--r-- | cargo2android.json | 8 | ||||
-rw-r--r-- | src/filter/mod.rs | 25 | ||||
-rw-r--r-- | src/fmt/mod.rs | 125 | ||||
-rw-r--r-- | src/fmt/writer/mod.rs | 99 | ||||
-rw-r--r-- | src/fmt/writer/termcolor/extern_impl.rs | 74 | ||||
-rw-r--r-- | src/fmt/writer/termcolor/shim_impl.rs | 32 | ||||
-rw-r--r-- | src/lib.rs | 65 |
14 files changed, 478 insertions, 165 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 8b186b2..b5ebece 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "67adcba945148ef3bc1a867832f1422779d626cc" + "sha1": "04856ac38baf422baebb40729cf51127a75b82ed" } } @@ -37,34 +37,29 @@ license { ], } -rust_defaults { - name: "env_logger_defaults", +rust_test { + name: "env_logger_test_src_lib", + host_supported: true, crate_name: "env_logger", + cargo_env_compat: true, + cargo_pkg_version: "0.9.0", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, + test_options: { + unit_test: true, + }, edition: "2018", rustlibs: [ "liblog_rust", ], } -rust_test_host { - name: "env_logger_host_test_src_lib", - defaults: ["env_logger_defaults"], - test_options: { - unit_test: true, - }, -} - -rust_test { - name: "env_logger_device_test_src_lib", - defaults: ["env_logger_defaults"], -} - rust_defaults { - name: "env_logger_defaults_env_logger", + name: "env_logger_test_defaults", crate_name: "env_logger", + cargo_env_compat: true, + cargo_pkg_version: "0.9.0", test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", @@ -75,9 +70,10 @@ rust_defaults { ], } -rust_test_host { - name: "env_logger_host_test_tests_init-twice-retains-filter", - defaults: ["env_logger_defaults_env_logger"], +rust_test { + name: "env_logger_test_tests_init-twice-retains-filter", + defaults: ["env_logger_test_defaults"], + host_supported: true, srcs: ["tests/init-twice-retains-filter.rs"], test_options: { unit_test: true, @@ -85,14 +81,9 @@ rust_test_host { } rust_test { - name: "env_logger_device_test_tests_init-twice-retains-filter", - defaults: ["env_logger_defaults_env_logger"], - srcs: ["tests/init-twice-retains-filter.rs"], -} - -rust_test_host { - name: "env_logger_host_test_tests_log-in-log", - defaults: ["env_logger_defaults_env_logger"], + name: "env_logger_test_tests_log-in-log", + defaults: ["env_logger_test_defaults"], + host_supported: true, srcs: ["tests/log-in-log.rs"], test_options: { unit_test: true, @@ -100,14 +91,9 @@ rust_test_host { } rust_test { - name: "env_logger_device_test_tests_log-in-log", - defaults: ["env_logger_defaults_env_logger"], - srcs: ["tests/log-in-log.rs"], -} - -rust_test_host { - name: "env_logger_host_test_tests_log_tls_dtors", - defaults: ["env_logger_defaults_env_logger"], + name: "env_logger_test_tests_log_tls_dtors", + defaults: ["env_logger_test_defaults"], + host_supported: true, srcs: ["tests/log_tls_dtors.rs"], test_options: { unit_test: true, @@ -115,30 +101,21 @@ rust_test_host { } rust_test { - name: "env_logger_device_test_tests_log_tls_dtors", - defaults: ["env_logger_defaults_env_logger"], - srcs: ["tests/log_tls_dtors.rs"], -} - -rust_test_host { - name: "env_logger_host_test_tests_regexp_filter", - defaults: ["env_logger_defaults_env_logger"], + name: "env_logger_test_tests_regexp_filter", + defaults: ["env_logger_test_defaults"], + host_supported: true, srcs: ["tests/regexp_filter.rs"], test_options: { unit_test: true, }, } -rust_test { - name: "env_logger_device_test_tests_regexp_filter", - defaults: ["env_logger_defaults_env_logger"], - srcs: ["tests/regexp_filter.rs"], -} - rust_library { name: "libenv_logger", host_supported: true, crate_name: "env_logger", + cargo_env_compat: true, + cargo_pkg_version: "0.9.0", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -146,12 +123,12 @@ rust_library { ], apex_available: [ "//apex_available:platform", + "com.android.bluetooth", + "com.android.compos", "com.android.resolv", + "com.android.uwb", "com.android.virt", ], + vendor_available: true, min_sdk_version: "29", } - -// dependent_library ["feature_list"] -// cfg-if-1.0.0 -// log-0.4.14 "std" @@ -13,7 +13,7 @@ [package] edition = "2018" name = "env_logger" -version = "0.8.3" +version = "0.9.0" authors = ["The Rust Project Developers"] include = ["src/**/*", "tests", "LICENSE-*", "README.md", "CHANGELOG.md"] description = "A logging implementation for `log` which is configured via an environment\nvariable.\n" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 42fa5ae..29c77b3 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,7 +1,7 @@ [package] name = "env_logger" edition = "2018" -version = "0.8.3" +version = "0.9.0" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/env_logger/env_logger-0.8.3.crate" + value: "https://static.crates.io/crates/env_logger/env_logger-0.9.0.crate" } - version: "0.8.3" + version: "0.9.0" license_type: NOTICE last_upgrade_date { year: 2021 - month: 2 - day: 11 + month: 8 + day: 9 } } @@ -21,7 +21,7 @@ It must be added along with `log` to the project dependencies: ```toml [dependencies] log = "0.4.0" -env_logger = "0.8.3" +env_logger = "0.8.4" ``` `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. @@ -88,7 +88,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.8.3" +env_logger = "0.8.4" ``` ```rust diff --git a/TEST_MAPPING b/TEST_MAPPING index 15e6776..0553d14 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,32 +1,132 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { + "imports": [ + { + "path": "external/rust/crates/android_logger" + }, + { + "path": "external/rust/crates/cast" + }, + { + "path": "external/rust/crates/crc32fast" + }, + { + "path": "external/rust/crates/libsqlite3-sys" + }, + { + "path": "external/rust/crates/mio" + }, + { + "path": "external/rust/crates/quickcheck" + }, + { + "path": "external/rust/crates/regex" + } + ], "presubmit": [ { + "name": "authfs_device_test_src_lib" + }, + { + "name": "doh_unit_test" + }, + { + "name": "env_logger_test_src_lib" + }, + { + "name": "env_logger_test_tests_init-twice-retains-filter" + }, + { + "name": "env_logger_test_tests_log-in-log" + }, + { + "name": "env_logger_test_tests_log_tls_dtors" + }, + { + "name": "env_logger_test_tests_regexp_filter" + }, + { + "name": "keystore2_selinux_concurrency_test" + }, + { + "name": "keystore2_selinux_test" + }, + { "name": "keystore2_test" }, { - "name": "env_logger_device_test_tests_regexp_filter" + "name": "legacykeystore_test" }, { - "name": "env_logger_device_test_src_lib" + "name": "logger_device_unit_tests" }, { - "name": "env_logger_device_test_tests_log-in-log" + "name": "logger_test_config_log_level" }, { - "name": "env_logger_device_test_tests_init-twice-retains-filter" + "name": "logger_test_default_init" }, { - "name": "libsqlite3-sys_device_test_src_lib" + "name": "logger_test_env_log_level" }, { - "name": "android_logger_device_test_src_lib" + "name": "logger_test_multiple_init" + }, + { + "name": "virtualizationservice_device_test" + } + ], + "presubmit-rust": [ + { + "name": "authfs_device_test_src_lib" + }, + { + "name": "doh_unit_test" + }, + { + "name": "env_logger_test_src_lib" + }, + { + "name": "env_logger_test_tests_init-twice-retains-filter" + }, + { + "name": "env_logger_test_tests_log-in-log" + }, + { + "name": "env_logger_test_tests_log_tls_dtors" + }, + { + "name": "env_logger_test_tests_regexp_filter" + }, + { + "name": "keystore2_selinux_concurrency_test" }, { "name": "keystore2_selinux_test" }, { - "name": "env_logger_device_test_tests_log_tls_dtors" + "name": "keystore2_test" + }, + { + "name": "legacykeystore_test" + }, + { + "name": "logger_device_unit_tests" + }, + { + "name": "logger_test_config_log_level" + }, + { + "name": "logger_test_default_init" + }, + { + "name": "logger_test_env_log_level" + }, + { + "name": "logger_test_multiple_init" + }, + { + "name": "virtualizationservice_device_test" } ] } diff --git a/cargo2android.json b/cargo2android.json index c10a6d8..98d8431 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -1,13 +1,17 @@ { "apex-available": [ "//apex_available:platform", + "com.android.bluetooth", + "com.android.compos", "com.android.resolv", + "com.android.uwb", "com.android.virt" ], - "min_sdk_version": "29", "dependencies": true, "device": true, "features": "", + "min-sdk-version": "29", "run": true, - "tests": true + "tests": true, + "vendor-available": true }
\ No newline at end of file diff --git a/src/filter/mod.rs b/src/filter/mod.rs index 8f7787f..9ebeab0 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -59,6 +59,7 @@ //! [`Filter::matches`]: struct.Filter.html#method.matches use log::{Level, LevelFilter, Metadata, Record}; +use std::collections::HashMap; use std::env; use std::fmt; use std::mem; @@ -107,7 +108,7 @@ pub struct Filter { /// /// [`Filter`]: struct.Filter.html pub struct Builder { - directives: Vec<Directive>, + directives: HashMap<Option<String>, LevelFilter>, filter: Option<inner::Filter>, built: bool, } @@ -171,7 +172,7 @@ impl Builder { /// Initializes the filter builder with defaults. pub fn new() -> Builder { Builder { - directives: Vec::new(), + directives: HashMap::new(), filter: None, built: false, } @@ -203,10 +204,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 { - self.directives.push(Directive { - name: module.map(|s| s.to_string()), - level, - }); + self.directives.insert(module.map(|s| s.to_string()), level); self } @@ -221,7 +219,7 @@ impl Builder { self.filter = filter; for directive in directives { - self.directives.push(directive); + self.directives.insert(directive.name, directive.level); } self } @@ -231,16 +229,23 @@ impl Builder { assert!(!self.built, "attempt to re-use consumed builder"); self.built = true; + let mut directives = Vec::new(); if self.directives.is_empty() { // Adds the default filter if none exist - self.directives.push(Directive { + directives.push(Directive { name: None, level: LevelFilter::Error, }); } else { + // Consume map of directives. + let directives_map = mem::take(&mut self.directives); + directives = directives_map + .into_iter() + .map(|(name, level)| Directive { name, level }) + .collect(); // Sort the directives by length of their name, this allows a // little more efficient lookup at runtime. - self.directives.sort_by(|a, b| { + directives.sort_by(|a, b| { let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0); let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0); alen.cmp(&blen) @@ -248,7 +253,7 @@ impl Builder { } Filter { - directives: mem::replace(&mut self.directives, Vec::new()), + directives: mem::take(&mut directives), filter: mem::replace(&mut self.filter, None), } } diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 3c4fee0..21e0957 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -141,6 +141,7 @@ pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> pub(crate) struct Builder { pub format_timestamp: Option<TimestampPrecision>, pub format_module_path: bool, + pub format_target: bool, pub format_level: bool, pub format_indent: Option<usize>, pub custom_format: Option<FormatFn>, @@ -152,7 +153,8 @@ impl Default for Builder { fn default() -> Self { Builder { format_timestamp: Some(Default::default()), - format_module_path: true, + format_module_path: false, + format_target: true, format_level: true, format_indent: Some(4), custom_format: None, @@ -186,6 +188,7 @@ impl Builder { let fmt = DefaultFormat { timestamp: built.format_timestamp, module_path: built.format_module_path, + target: built.format_target, level: built.format_level, written_header_value: false, indent: built.format_indent, @@ -210,6 +213,7 @@ type SubtleStyle = &'static str; struct DefaultFormat<'a> { timestamp: Option<TimestampPrecision>, module_path: bool, + target: bool, level: bool, written_header_value: bool, indent: Option<usize>, @@ -222,6 +226,7 @@ impl<'a> DefaultFormat<'a> { self.write_timestamp()?; self.write_level(record)?; self.write_module_path(record)?; + self.write_target(record)?; self.finish_header()?; self.write_args(record) @@ -311,6 +316,17 @@ impl<'a> DefaultFormat<'a> { } } + fn write_target(&mut self, record: &Record) -> io::Result<()> { + if !self.target { + return Ok(()); + } + + match record.target() { + "" => Ok(()), + target => self.write_header_value(target), + } + } + fn finish_header(&mut self) -> io::Result<()> { if self.written_header_value { let close_brace = self.subtle_style("]"); @@ -381,23 +397,33 @@ mod tests { use log::{Level, Record}; - fn write(fmt: DefaultFormat) -> String { + fn write_record(record: Record, fmt: DefaultFormat) -> String { let buf = fmt.buf.buf.clone(); - let record = Record::builder() - .args(format_args!("log\nmessage")) - .level(Level::Info) - .file(Some("test.rs")) - .line(Some(144)) - .module_path(Some("test::path")) - .build(); - fmt.write(&record).expect("failed to write record"); let buf = buf.borrow(); String::from_utf8(buf.bytes().to_vec()).expect("failed to read record") } + fn write_target<'a>(target: &'a str, fmt: DefaultFormat) -> String { + write_record( + Record::builder() + .args(format_args!("log\nmessage")) + .level(Level::Info) + .file(Some("test.rs")) + .line(Some(144)) + .module_path(Some("test::path")) + .target(target) + .build(), + fmt, + ) + } + + fn write(fmt: DefaultFormat) -> String { + write_target("", fmt) + } + #[test] fn format_with_header() { let writer = writer::Builder::new() @@ -409,6 +435,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: true, + target: false, level: true, written_header_value: false, indent: None, @@ -430,6 +457,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: false, + target: false, level: false, written_header_value: false, indent: None, @@ -451,6 +479,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: true, + target: false, level: true, written_header_value: false, indent: Some(4), @@ -472,6 +501,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: true, + target: false, level: true, written_header_value: false, indent: Some(0), @@ -493,6 +523,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: false, + target: false, level: false, written_header_value: false, indent: Some(4), @@ -514,6 +545,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: false, + target: false, level: false, written_header_value: false, indent: None, @@ -535,6 +567,7 @@ mod tests { let written = write(DefaultFormat { timestamp: None, module_path: false, + target: false, level: false, written_header_value: false, indent: Some(4), @@ -544,4 +577,76 @@ mod tests { assert_eq!("log\n\n message\n\n", written); } + + #[test] + fn format_target() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); + + let mut f = Formatter::new(&writer); + + let written = write_target( + "target", + DefaultFormat { + timestamp: None, + module_path: true, + target: true, + level: true, + written_header_value: false, + indent: None, + suffix: "\n", + buf: &mut f, + }, + ); + + assert_eq!("[INFO test::path target] log\nmessage\n", written); + } + + #[test] + fn format_empty_target() { + 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, + target: true, + level: true, + written_header_value: false, + indent: None, + suffix: "\n", + buf: &mut f, + }); + + assert_eq!("[INFO test::path] log\nmessage\n", written); + } + + #[test] + fn format_no_target() { + let writer = writer::Builder::new() + .write_style(WriteStyle::Never) + .build(); + + let mut f = Formatter::new(&writer); + + let written = write_target( + "target", + DefaultFormat { + timestamp: None, + module_path: true, + target: false, + level: true, + written_header_value: false, + indent: None, + suffix: "\n", + buf: &mut f, + }, + ); + + assert_eq!("[INFO test::path] log\nmessage\n", written); + } } diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 6ee63a3..5bb5353 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -3,22 +3,24 @@ mod termcolor; use self::atty::{is_stderr, is_stdout}; use self::termcolor::BufferWriter; -use std::{fmt, io}; +use std::{fmt, io, mem, sync::Mutex}; -pub(in crate::fmt) mod glob { +pub(super) mod glob { pub use super::termcolor::glob::*; pub use super::*; } -pub(in crate::fmt) use self::termcolor::Buffer; +pub(super) use self::termcolor::Buffer; -/// Log target, either `stdout` or `stderr`. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +/// Log target, either `stdout`, `stderr` or a custom pipe. +#[non_exhaustive] pub enum Target { /// Logs will be sent to standard output. Stdout, /// Logs will be sent to standard error. Stderr, + /// Logs will be sent to a custom pipe. + Pipe(Box<dyn io::Write + Send + 'static>), } impl Default for Target { @@ -27,6 +29,61 @@ impl Default for Target { } } +impl fmt::Debug for Target { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Stdout => "stdout", + Self::Stderr => "stderr", + Self::Pipe(_) => "pipe", + } + ) + } +} + +/// Log target, either `stdout`, `stderr` or a custom pipe. +/// +/// Same as `Target`, except the pipe is wrapped in a mutex for interior mutability. +pub(super) enum WritableTarget { + /// Logs will be sent to standard output. + Stdout, + /// Logs will be sent to standard error. + Stderr, + /// Logs will be sent to a custom pipe. + Pipe(Box<Mutex<dyn io::Write + Send + 'static>>), +} + +impl From<Target> for WritableTarget { + fn from(target: Target) -> Self { + match target { + Target::Stdout => Self::Stdout, + Target::Stderr => Self::Stderr, + Target::Pipe(pipe) => Self::Pipe(Box::new(Mutex::new(pipe))), + } + } +} + +impl Default for WritableTarget { + fn default() -> Self { + Self::from(Target::default()) + } +} + +impl fmt::Debug for WritableTarget { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Stdout => "stdout", + Self::Stderr => "stderr", + Self::Pipe(_) => "pipe", + } + ) + } +} /// Whether or not to print styles to the target. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum WriteStyle { @@ -55,11 +112,11 @@ impl Writer { self.write_style } - pub(in crate::fmt) fn buffer(&self) -> Buffer { + pub(super) fn buffer(&self) -> Buffer { self.inner.buffer() } - pub(in crate::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> { + pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> { self.inner.print(buf) } } @@ -67,8 +124,9 @@ impl Writer { /// A builder for a terminal writer. /// /// The target and style choice can be configured before building. +#[derive(Debug)] pub(crate) struct Builder { - target: Target, + target: WritableTarget, write_style: WriteStyle, is_test: bool, built: bool, @@ -87,7 +145,7 @@ impl Builder { /// Set the target to write to. pub(crate) fn target(&mut self, target: Target) -> &mut Self { - self.target = target; + self.target = target.into(); self } @@ -119,9 +177,10 @@ impl Builder { let color_choice = match self.write_style { WriteStyle::Auto => { - if match self.target { - Target::Stderr => is_stderr(), - Target::Stdout => is_stdout(), + if match &self.target { + WritableTarget::Stderr => is_stderr(), + WritableTarget::Stdout => is_stdout(), + WritableTarget::Pipe(_) => false, } { WriteStyle::Auto } else { @@ -131,9 +190,10 @@ impl Builder { color_choice => color_choice, }; - let writer = match self.target { - Target::Stderr => BufferWriter::stderr(self.is_test, color_choice), - Target::Stdout => BufferWriter::stdout(self.is_test, color_choice), + let writer = match mem::take(&mut self.target) { + WritableTarget::Stderr => BufferWriter::stderr(self.is_test, color_choice), + WritableTarget::Stdout => BufferWriter::stdout(self.is_test, color_choice), + WritableTarget::Pipe(pipe) => BufferWriter::pipe(self.is_test, color_choice, pipe), }; Writer { @@ -149,15 +209,6 @@ impl Default for Builder { } } -impl fmt::Debug for Builder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Logger") - .field("target", &self.target) - .field("write_style", &self.write_style) - .finish() - } -} - impl fmt::Debug for Writer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Writer").finish() diff --git a/src/fmt/writer/termcolor/extern_impl.rs b/src/fmt/writer/termcolor/extern_impl.rs index 4324a45..11012fb 100644 --- a/src/fmt/writer/termcolor/extern_impl.rs +++ b/src/fmt/writer/termcolor/extern_impl.rs @@ -3,11 +3,12 @@ use std::cell::RefCell; use std::fmt; use std::io::{self, Write}; use std::rc::Rc; +use std::sync::Mutex; use log::Level; use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; -use crate::fmt::{Formatter, Target, WriteStyle}; +use crate::fmt::{Formatter, WritableTarget, WriteStyle}; pub(in crate::fmt::writer) mod glob { pub use super::*; @@ -70,46 +71,71 @@ impl Formatter { pub(in crate::fmt::writer) struct BufferWriter { inner: termcolor::BufferWriter, - test_target: Option<Target>, + test_target: Option<WritableTarget>, } pub(in crate::fmt) struct Buffer { inner: termcolor::Buffer, - test_target: Option<Target>, + has_test_target: bool, } impl BufferWriter { pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self { BufferWriter { inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), - test_target: if is_test { Some(Target::Stderr) } else { None }, + test_target: if is_test { + Some(WritableTarget::Stderr) + } else { + None + }, } } pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self { BufferWriter { inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()), - test_target: if is_test { Some(Target::Stdout) } else { None }, + test_target: if is_test { + Some(WritableTarget::Stdout) + } else { + None + }, + } + } + + pub(in crate::fmt::writer) fn pipe( + is_test: bool, + write_style: WriteStyle, + pipe: Box<Mutex<dyn io::Write + Send + 'static>>, + ) -> Self { + BufferWriter { + // The inner Buffer is never printed from, but it is still needed to handle coloring and other formating + inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), + test_target: if is_test { + Some(WritableTarget::Pipe(pipe)) + } else { + None + }, } } pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { Buffer { inner: self.inner.buffer(), - test_target: self.test_target, + has_test_target: self.test_target.is_some(), } } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { - if let Some(target) = self.test_target { + 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), + WritableTarget::Stderr => eprint!("{}", log), + WritableTarget::Stdout => print!("{}", log), + WritableTarget::Pipe(pipe) => write!(pipe.lock().unwrap(), "{}", log)?, } Ok(()) @@ -138,7 +164,7 @@ impl Buffer { fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { // Ignore styles for test captured logs because they can't be printed - if self.test_target.is_none() { + if !self.has_test_target { self.inner.set_color(spec) } else { Ok(()) @@ -147,7 +173,7 @@ impl Buffer { fn reset(&mut self) -> io::Result<()> { // Ignore styles for test captured logs because they can't be printed - if self.test_target.is_none() { + if !self.has_test_target { self.inner.reset() } else { Ok(()) @@ -255,7 +281,7 @@ impl Style { /// }); /// ``` pub fn set_color(&mut self, color: Color) -> &mut Style { - self.spec.set_fg(color.into_termcolor()); + self.spec.set_fg(Some(color.into_termcolor())); self } @@ -334,7 +360,7 @@ impl Style { /// }); /// ``` pub fn set_bg(&mut self, color: Color) -> &mut Style { - self.spec.set_bg(color.into_termcolor()); + self.spec.set_bg(Some(color.into_termcolor())); self } @@ -467,18 +493,18 @@ pub enum Color { } impl Color { - fn into_termcolor(self) -> Option<termcolor::Color> { + fn into_termcolor(self) -> termcolor::Color { match self { - Color::Black => Some(termcolor::Color::Black), - Color::Blue => Some(termcolor::Color::Blue), - Color::Green => Some(termcolor::Color::Green), - Color::Red => Some(termcolor::Color::Red), - Color::Cyan => Some(termcolor::Color::Cyan), - Color::Magenta => Some(termcolor::Color::Magenta), - Color::Yellow => Some(termcolor::Color::Yellow), - Color::White => Some(termcolor::Color::White), - Color::Ansi256(value) => Some(termcolor::Color::Ansi256(value)), - Color::Rgb(r, g, b) => Some(termcolor::Color::Rgb(r, g, b)), + Color::Black => termcolor::Color::Black, + Color::Blue => termcolor::Color::Blue, + Color::Green => termcolor::Color::Green, + Color::Red => termcolor::Color::Red, + Color::Cyan => termcolor::Color::Cyan, + Color::Magenta => termcolor::Color::Magenta, + Color::Yellow => termcolor::Color::Yellow, + Color::White => termcolor::Color::White, + Color::Ansi256(value) => termcolor::Color::Ansi256(value), + Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b), } } } diff --git a/src/fmt/writer/termcolor/shim_impl.rs b/src/fmt/writer/termcolor/shim_impl.rs index 563f8ad..bfc31d0 100644 --- a/src/fmt/writer/termcolor/shim_impl.rs +++ b/src/fmt/writer/termcolor/shim_impl.rs @@ -1,11 +1,11 @@ -use std::io; +use std::{io, sync::Mutex}; -use crate::fmt::{Target, WriteStyle}; +use crate::fmt::{WritableTarget, WriteStyle}; pub(in crate::fmt::writer) mod glob {} pub(in crate::fmt::writer) struct BufferWriter { - target: Target, + target: WritableTarget, } pub(in crate::fmt) struct Buffer(Vec<u8>); @@ -13,13 +13,23 @@ pub(in crate::fmt) struct Buffer(Vec<u8>); impl BufferWriter { pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { - target: Target::Stderr, + target: WritableTarget::Stderr, } } pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { - target: Target::Stdout, + target: WritableTarget::Stdout, + } + } + + pub(in crate::fmt::writer) fn pipe( + _is_test: bool, + _write_style: WriteStyle, + pipe: Box<Mutex<dyn io::Write + Send + 'static>>, + ) -> Self { + BufferWriter { + target: WritableTarget::Pipe(pipe), } } @@ -30,12 +40,12 @@ impl BufferWriter { pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { // This impl uses the `eprint` and `print` macros // instead of using the streams directly. - // This is so their output can be captured by `cargo test` - let log = String::from_utf8_lossy(&buf.0); - - match self.target { - Target::Stderr => eprint!("{}", log), - Target::Stdout => print!("{}", log), + // This is so their output can be captured by `cargo test`. + match &self.target { + // Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty. + WritableTarget::Pipe(pipe) => pipe.lock().unwrap().write_all(&buf.0)?, + WritableTarget::Stdout => print!("{}", String::from_utf8_lossy(&buf.0)), + WritableTarget::Stderr => eprint!("{}", String::from_utf8_lossy(&buf.0)), } Ok(()) @@ -97,28 +97,38 @@ //! directives*. A logging directive is of the form: //! //! ```text -//! path::to::module=level +//! example::log::target=level //! ``` //! -//! The path to the module is rooted in the name of the crate it was compiled -//! for, so if your program is contained in a file `hello.rs`, for example, to -//! turn on logging for this file you would use a value of `RUST_LOG=hello`. -//! Furthermore, this path is a prefix-search, so all modules nested in the -//! specified module will also have logging enabled. +//! The log target is typically equal to the path of the module the message +//! in question originated from, though it can be overriden. +//! +//! The path is rooted in the name of the crate it was compiled for, so if +//! your program is in a file called, for example, `hello.rs`, the path would +//! simply be be `hello`. +//! +//! Furthermore, the the log can be filtered using prefix-search based on the +//! specified log target. A value of, for example, `RUST_LOG=example`, would +//! match all of the messages with targets: +//! +//! * `example` +//! * `example::test` +//! * `example::test::module::submodule` +//! * `examples::and_more_examples` //! //! When providing the crate name or a module path, explicitly specifying the -//! log level is optional. If omitted, all logging for the item (and its -//! children) will be enabled. +//! log level is optional. If omitted, all logging for the item will be +//! enabled. //! //! The names of the log levels that may be specified correspond to the //! variations of the [`log::Level`][level-enum] enum from the `log` //! crate. They are: //! -//! * `error` -//! * `warn` -//! * `info` -//! * `debug` -//! * `trace` +//! * `error` +//! * `warn` +//! * `info` +//! * `debug` +//! * `trace` //! //! There is also a pseudo logging level, `off`, which may be specified to //! disable all logging for a given module or for the entire application. As @@ -260,7 +270,7 @@ //! env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init(); //! ``` //! -//! [gh-repo-examples]: https://github.com/env-logger-rs/env_logger/tree/master/examples +//! [gh-repo-examples]: https://github.com/env-logger-rs/env_logger/tree/main/examples //! [level-enum]: https://docs.rs/log/latest/log/enum.Level.html //! [log-crate-url]: https://docs.rs/log/ //! [`Builder`]: struct.Builder.html @@ -598,6 +608,12 @@ impl Builder { self } + /// Whether or not to write the target in the default format. + pub fn format_target(&mut self, write: bool) -> &mut Self { + self.format.format_target = write; + self + } + /// 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<usize>) -> &mut Self { @@ -708,7 +724,10 @@ impl Builder { /// Sets the target for the log output. /// - /// Env logger can log to either stdout or stderr. The default is stderr. + /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr. + /// + /// The custom pipe can be used to send the log messages to a custom sink (for example a file). + /// Do note that direct writes to a file can become a bottleneck due to IO operation times. /// /// # Examples /// @@ -1277,4 +1296,20 @@ mod tests { assert_eq!(Some("from default".to_owned()), env.get_write_style()); } + + #[test] + fn builder_parse_env_overrides_existing_filters() { + env::set_var( + "builder_parse_default_env_overrides_existing_filters", + "debug", + ); + let env = Env::new().filter("builder_parse_default_env_overrides_existing_filters"); + + let mut builder = Builder::new(); + builder.filter_level(LevelFilter::Trace); + // Overrides global level to debug + builder.parse_env(env); + + assert_eq!(builder.filter.build().filter(), LevelFilter::Debug); + } } |