aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:00:14 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:00:14 +0000
commite79c3a39720482d94bd5e906fd018835ca675f14 (patch)
treedfc3b3908fa097798973ffea9e1bb600e9646bdb
parentbecfaa56a65edcb8a11a4e86e9931fd15f1dd9b1 (diff)
parent48a125a2ebdd03b00350bb00d56e6c2c660c56c0 (diff)
downloadenv_logger-e79c3a39720482d94bd5e906fd018835ca675f14.tar.gz
Change-Id: I2e64cfe30f21490ef09ffdffa5ef7e342c985d29
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp83
-rw-r--r--Cargo.toml2
-rw-r--r--Cargo.toml.orig2
-rw-r--r--METADATA8
-rw-r--r--README.md4
-rw-r--r--TEST_MAPPING114
-rw-r--r--cargo2android.json8
-rw-r--r--src/filter/mod.rs25
-rw-r--r--src/fmt/mod.rs125
-rw-r--r--src/fmt/writer/mod.rs99
-rw-r--r--src/fmt/writer/termcolor/extern_impl.rs74
-rw-r--r--src/fmt/writer/termcolor/shim_impl.rs32
-rw-r--r--src/lib.rs65
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"
}
}
diff --git a/Android.bp b/Android.bp
index fe94641..b08c70d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index bd952f8..b91eddb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/METADATA b/METADATA
index b1e7cb0..07f72fc 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/README.md b/README.md
index 329d1be..4e84e8f 100644
--- a/README.md
+++ b/README.md
@@ -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(())
diff --git a/src/lib.rs b/src/lib.rs
index 31ea7c3..8504108 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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);
+ }
}