diff options
author | ThiƩbaud Weksteen <tweek@google.com> | 2021-03-04 08:17:29 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-03-04 08:17:29 +0000 |
commit | 85bd36e3462182ff95e9bbcfe85f1b89bd6085e0 (patch) | |
tree | 445137be149a627423611e1bc736fe55a624e276 | |
parent | 168e17506cce243aed6dc55c185870f7130b48f9 (diff) | |
parent | fec76ba12bc74bee46acebf2ec6ff242690f9fe0 (diff) | |
download | env_logger-85bd36e3462182ff95e9bbcfe85f1b89bd6085e0.tar.gz |
Merge "Upgrade rust/crates/env_logger to 0.8.3"
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 114 | ||||
-rw-r--r-- | .github/workflows/docs.yml | 40 | ||||
-rw-r--r-- | Android.bp | 4 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | Cargo.toml.orig | 3 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | README.md | 37 | ||||
-rw-r--r-- | TEST_MAPPING | 20 | ||||
-rw-r--r-- | examples/custom_default_format.rs | 40 | ||||
-rw-r--r-- | examples/custom_format.rs | 54 | ||||
-rw-r--r-- | examples/custom_logger.rs | 62 | ||||
-rw-r--r-- | examples/default.rs | 38 | ||||
-rw-r--r-- | examples/direct_logger.rs | 39 | ||||
-rw-r--r-- | examples/filters_from_code.rs | 20 | ||||
-rw-r--r-- | examples/in_tests.rs | 54 | ||||
-rw-r--r-- | examples/syslog_friendly_format.rs | 24 | ||||
-rw-r--r-- | rust-toolchain | 1 | ||||
-rw-r--r-- | src/filter/mod.rs | 261 | ||||
-rw-r--r-- | src/fmt/mod.rs | 63 | ||||
-rw-r--r-- | src/lib.rs | 77 |
21 files changed, 435 insertions, 531 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 7e09207..8b186b2 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "0f53c8dd5bf65b80d1fdb8ea0befd848f120c631" + "sha1": "67adcba945148ef3bc1a867832f1422779d626cc" } } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index d2e8979..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Continuous Integration
-
-on:
- pull_request:
- paths:
- - "**.rs"
- - "Cargo.toml"
- - "Cargo.lock"
-
-jobs:
- fmt:
- name: Source formatting check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout sources
- uses: actions/checkout@v2
-
- - name: Install Rust toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - name: Check formatting
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- args: -- --check
-
- check:
- name: Compilation check
- runs-on: ubuntu-latest
- strategy:
- fail-fast: true
- matrix:
- rust:
- - stable
- - beta
- - nightly
- - 1.41.0
- steps:
- - name: Checkout sources
- uses: actions/checkout@v2
-
- - name: Install Rust toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: ${{ matrix.rust }}
- override: true
-
- - name: Run cargo check
- uses: actions-rs/cargo@v1
- with:
- command: check
-
- clippy:
- name: Lint check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout sources
- uses: actions/checkout@v2
-
- - name: Install Rust toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- override: true
- components: clippy
-
- - name: Run lints
- uses: actions-rs/cargo@v1
- with:
- command: clippy
- args: -- -D warnings
-
- ci-crate:
- name: CI crate check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout sources
- uses: actions/checkout@v2
-
- - name: Install Rust toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
-
- - name: Run ci crate
- uses: actions-rs/cargo@v1
- with:
- command: run
- args: -p ci
-
- crate-example:
- name: Crate example check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout sources
- uses: actions/checkout@v2
-
- - name: Install Rust toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
-
- - name: Run crate example
- uses: actions-rs/cargo@v1
- with:
- command: run
- args: --example default
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 4b6110b..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Continuous Integration - Docs - -on: - push: - branches: - - master - paths: - - "**.rs" - - "Cargo.toml" - - "Cargo.lock" - workflow_dispatch: - -jobs: - docs: - name: Generate crate documentation - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - - - name: Generate documentation - uses: actions-rs/cargo@v1 - env: - RUSTDOCFLAGS: "--enable-index-page -Zunstable-options" - with: - command: doc - args: --no-deps - - - name: Deploy documentation - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./target/doc @@ -152,5 +152,5 @@ rust_library { } // dependent_library ["feature_list"] -// cfg-if-0.1.10 -// log-0.4.13 "std" +// cfg-if-1.0.0 +// log-0.4.14 "std" @@ -13,8 +13,9 @@ [package] edition = "2018" name = "env_logger" -version = "0.8.2" +version = "0.8.3" 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" documentation = "https://docs.rs/env_logger" readme = "README.md" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 449deaa..42fa5ae 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,7 +1,7 @@ [package] name = "env_logger" edition = "2018" -version = "0.8.2" # remember to update html_root_url +version = "0.8.3" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -13,6 +13,7 @@ variable. """ categories = ["development-tools::debugging"] keywords = ["logging", "log", "logger"] +include = ["src/**/*", "tests", "LICENSE-*", "README.md", "CHANGELOG.md"] [workspace] members = [ @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/env_logger/env_logger-0.8.2.crate" + value: "https://static.crates.io/crates/env_logger/env_logger-0.8.3.crate" } - version: "0.8.2" + version: "0.8.3" license_type: NOTICE last_upgrade_date { - year: 2020 - month: 11 - day: 18 + year: 2021 + month: 2 + day: 11 } } @@ -1,6 +1,5 @@ # env_logger -[![Build Status](https://travis-ci.org/env-logger-rs/env_logger.svg?branch=master)](https://travis-ci.org/env-logger-rs/env_logger) [![Maintenance](https://img.shields.io/badge/maintenance-actively%20maintained-brightgreen.svg)](https://github.com/env-logger-rs/env_logger) [![crates.io](https://img.shields.io/crates/v/env_logger.svg)](https://crates.io/crates/env_logger) [![Documentation](https://docs.rs/env_logger/badge.svg)](https://docs.rs/env_logger) @@ -22,7 +21,7 @@ It must be added along with `log` to the project dependencies: ```toml [dependencies] log = "0.4.0" -env_logger = "0.8.2" +env_logger = "0.8.3" ``` `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. @@ -40,7 +39,7 @@ fn main() { } ``` -Then when running the executable, specify a value for the `RUST_LOG` +Then when running the executable, specify a value for the **`RUST_LOG`** environment variable that corresponds with the log messages you want to show. ```bash @@ -48,6 +47,36 @@ $ RUST_LOG=info ./main [2018-11-03T06:09:06Z INFO default] starting up ``` +The letter case is not significant for the logging level names; e.g., `debug`, +`DEBUG`, and `dEbuG` all represent the same logging level. Therefore, the +previous example could also have been written this way, specifying the log +level as `INFO` rather than as `info`: + +```bash +$ RUST_LOG=INFO ./main +[2018-11-03T06:09:06Z INFO default] starting up +``` + +So which form should you use? For consistency, our convention is to use lower +case names. Where our docs do use other forms, they do so in the context of +specific examples, so you won't be surprised if you see similar usage in the +wild. + +The log levels that may be specified correspond to the [`log::Level`][level-enum] +enum from the `log` crate. They are: + + * `error` + * `warn` + * `info` + * `debug` + * `trace` + +[level-enum]: https://docs.rs/log/latest/log/enum.Level.html "log::Level (docs.rs)" + +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 with the +logging levels, the letter case is not significant. + `env_logger` can be configured in other ways besides an environment variable. See [the examples](https://github.com/env-logger-rs/env_logger/tree/master/examples) for more approaches. ### In tests @@ -59,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.2" +env_logger = "0.8.3" ``` ```rust diff --git a/TEST_MAPPING b/TEST_MAPPING index 5ab713d..15e6776 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,32 +1,32 @@ -// Generated by cargo2android.py for tests that depend on this crate. +// Generated by update_crate_tests.py for tests that depend on this crate. { "presubmit": [ { - "name": "env_logger_device_test_tests_init-twice-retains-filter" + "name": "keystore2_test" }, { - "name": "env_logger_device_test_tests_log-in-log" + "name": "env_logger_device_test_tests_regexp_filter" }, { - "name": "env_logger_device_test_tests_regexp_filter" + "name": "env_logger_device_test_src_lib" }, { - "name": "android_logger_device_test_src_lib" + "name": "env_logger_device_test_tests_log-in-log" }, { - "name": "keystore2_selinux_test" + "name": "env_logger_device_test_tests_init-twice-retains-filter" }, { - "name": "env_logger_device_test_tests_log_tls_dtors" + "name": "libsqlite3-sys_device_test_src_lib" }, { - "name": "keystore2_test" + "name": "android_logger_device_test_src_lib" }, { - "name": "libsqlite3-sys_device_test_src_lib" + "name": "keystore2_selinux_test" }, { - "name": "env_logger_device_test_src_lib" + "name": "env_logger_device_test_tests_log_tls_dtors" } ] } diff --git a/examples/custom_default_format.rs b/examples/custom_default_format.rs deleted file mode 100644 index b04eb60..0000000 --- a/examples/custom_default_format.rs +++ /dev/null @@ -1,40 +0,0 @@ -/*! -Disabling parts of the default format. - -Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`: - -```no_run,shell -$ export MY_LOG_LEVEL='info' -``` - -Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors -or `auto` to enable them: - -```no_run,shell -$ export MY_LOG_STYLE=never -``` - -If you want to control the logging output completely, see the `custom_logger` example. -*/ - -#[macro_use] -extern crate log; - -use env_logger::{Builder, Env}; - -fn init_logger() { - let env = Env::default() - .filter("MY_LOG_LEVEL") - .write_style("MY_LOG_STYLE"); - - Builder::from_env(env) - .format_level(false) - .format_timestamp_nanos() - .init(); -} - -fn main() { - init_logger(); - - info!("a log from `MyLogger`"); -} diff --git a/examples/custom_format.rs b/examples/custom_format.rs deleted file mode 100644 index d8585a5..0000000 --- a/examples/custom_format.rs +++ /dev/null @@ -1,54 +0,0 @@ -/*! -Changing the default logging format. - -Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`: - -```no_run,shell -$ export MY_LOG_LEVEL='info' -``` - -Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors -or `auto` to enable them: - -```no_run,shell -$ export MY_LOG_STYLE=never -``` - -If you want to control the logging output completely, see the `custom_logger` example. -*/ - -#[cfg(all(feature = "termcolor", feature = "humantime"))] -fn main() { - use env_logger::{fmt::Color, 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(Color::Yellow).set_bold(true); - - let timestamp = buf.timestamp(); - - writeln!( - buf, - "My formatted log ({}): {}", - timestamp, - style.value(record.args()) - ) - }) - .init(); - } - - init_logger(); - - 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 deleted file mode 100644 index 3d4dc5b..0000000 --- a/examples/custom_logger.rs +++ /dev/null @@ -1,62 +0,0 @@ -/*! -Using `env_logger` to drive a custom logger. - -Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`: - -```no_run,shell -$ export MY_LOG_LEVEL='info' -``` - -If you only want to change the way logs are formatted, look at the `custom_format` example. -*/ - -#[macro_use] -extern crate log; - -use env_logger::filter::{Builder, Filter}; - -use log::{Log, Metadata, Record, SetLoggerError}; - -const FILTER_ENV: &'static str = "MY_LOG_LEVEL"; - -struct MyLogger { - inner: Filter, -} - -impl MyLogger { - fn new() -> MyLogger { - let mut builder = Builder::from_env(FILTER_ENV); - - MyLogger { - inner: builder.build(), - } - } - - fn init() -> Result<(), SetLoggerError> { - let logger = Self::new(); - - log::set_max_level(logger.inner.filter()); - log::set_boxed_logger(Box::new(logger)) - } -} - -impl Log for MyLogger { - fn enabled(&self, metadata: &Metadata) -> bool { - self.inner.enabled(metadata) - } - - fn log(&self, record: &Record) { - // Check if the record is matched by the logger before logging - if self.inner.matches(record) { - println!("{} - {}", record.level(), record.args()); - } - } - - fn flush(&self) {} -} - -fn main() { - MyLogger::init().unwrap(); - - info!("a log from `MyLogger`"); -} diff --git a/examples/default.rs b/examples/default.rs deleted file mode 100644 index 67bb030..0000000 --- a/examples/default.rs +++ /dev/null @@ -1,38 +0,0 @@ -/*! -Using `env_logger`. - -Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`: - -```no_run,shell -$ export MY_LOG_LEVEL='info' -``` - -Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors -or `auto` to enable them: - -```no_run,shell -$ export MY_LOG_STYLE=never -``` -*/ - -#[macro_use] -extern crate log; - -use env_logger::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"); - - env_logger::init_from_env(env); - - trace!("some trace log"); - debug!("some debug log"); - info!("some information log"); - warn!("some warning log"); - error!("some error log"); -} diff --git a/examples/direct_logger.rs b/examples/direct_logger.rs deleted file mode 100644 index 4d7f39d..0000000 --- a/examples/direct_logger.rs +++ /dev/null @@ -1,39 +0,0 @@ -/*! -Using `env_logger::Logger` and the `log::Log` trait directly. - -This example doesn't rely on environment variables, or having a static logger installed. -*/ - -use env_logger::{Builder, WriteStyle}; - -use log::{Level, LevelFilter, Log, MetadataBuilder, Record}; - -fn record() -> Record<'static> { - let error_metadata = MetadataBuilder::new() - .target("myApp") - .level(Level::Error) - .build(); - - Record::builder() - .metadata(error_metadata) - .args(format_args!("Error!")) - .line(Some(433)) - .file(Some("app.rs")) - .module_path(Some("server")) - .build() -} - -fn main() { - let stylish_logger = Builder::new() - .filter(None, LevelFilter::Error) - .write_style(WriteStyle::Always) - .build(); - - let unstylish_logger = Builder::new() - .filter(None, LevelFilter::Error) - .write_style(WriteStyle::Never) - .build(); - - stylish_logger.log(&record()); - unstylish_logger.log(&record()); -} diff --git a/examples/filters_from_code.rs b/examples/filters_from_code.rs deleted file mode 100644 index c877a44..0000000 --- a/examples/filters_from_code.rs +++ /dev/null @@ -1,20 +0,0 @@ -/*! -Specify logging filters in code instead of using an environment variable. -*/ - -#[macro_use] -extern crate log; - -use env_logger::Builder; - -use log::LevelFilter; - -fn main() { - Builder::new().filter_level(LevelFilter::max()).init(); - - trace!("some trace log"); - debug!("some debug log"); - info!("some information log"); - warn!("some warning log"); - error!("some error log"); -} diff --git a/examples/in_tests.rs b/examples/in_tests.rs deleted file mode 100644 index c4487cc..0000000 --- a/examples/in_tests.rs +++ /dev/null @@ -1,54 +0,0 @@ -/*! -Using `env_logger` in tests. - -Log events will be captured by `cargo` and only printed if the test fails. -You can run this example by calling: - -```text -cargo test --example in_tests -``` - -You should see the `it_does_not_work` test fail and include its log output. -*/ - -#[cfg_attr(test, macro_use)] -extern crate log; - -fn main() {} - -#[cfg(test)] -mod tests { - fn init_logger() { - let _ = env_logger::builder() - // Include all events in tests - .filter_level(log::LevelFilter::max()) - // Ensure events are captured by `cargo test` - .is_test(true) - // Ignore errors initializing the logger if tests race to configure it - .try_init(); - } - - #[test] - fn it_works() { - init_logger(); - - let a = 1; - let b = 2; - - debug!("checking whether {} + {} = 3", a, b); - - assert_eq!(3, a + b); - } - - #[test] - fn it_does_not_work() { - init_logger(); - - let a = 1; - let b = 2; - - debug!("checking whether {} + {} = 6", a, b); - - assert_eq!(6, a + b); - } -} diff --git a/examples/syslog_friendly_format.rs b/examples/syslog_friendly_format.rs deleted file mode 100644 index 9809ab3..0000000 --- a/examples/syslog_friendly_format.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::io::Write;
-
-fn main() {
- match std::env::var("RUST_LOG_STYLE") {
- Ok(s) if s == "SYSTEMD" => env_logger::builder()
- .format(|buf, record| {
- writeln!(
- buf,
- "<{}>{}: {}",
- match record.level() {
- log::Level::Error => 3,
- log::Level::Warn => 4,
- log::Level::Info => 6,
- log::Level::Debug => 7,
- log::Level::Trace => 7,
- },
- record.target(),
- record.args()
- )
- })
- .init(),
- _ => env_logger::init(),
- };
-}
diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 033080c..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.41.0
diff --git a/src/filter/mod.rs b/src/filter/mod.rs index ba81302..8f7787f 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -299,7 +299,7 @@ fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) { return (dirs, None); } if let Some(m) = mods { - for s in m.split(',') { + for s in m.split(',').map(|ss| ss.trim()) { if s.is_empty() { continue; } @@ -395,6 +395,26 @@ mod tests { assert!(!enabled(&logger.directives, Level::Debug, "crate2")); } + // Some of our tests are only correct or complete when they cover the full + // universe of variants for log::Level. In the unlikely event that a new + // variant is added in the future, this test will detect the scenario and + // alert us to the need to review and update the tests. In such a + // situation, this test will fail to compile, and the error message will + // look something like this: + // + // error[E0004]: non-exhaustive patterns: `NewVariant` not covered + // --> src/filter/mod.rs:413:15 + // | + // 413 | match level_universe { + // | ^^^^^^^^^^^^^^ pattern `NewVariant` not covered + #[test] + fn ensure_tests_cover_level_universe() { + let level_universe: Level = Level::Trace; // use of trace variant is arbitrary + match level_universe { + Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (), + } + } + #[test] fn parse_default() { let logger = Builder::new().parse("info,crate1::mod1=warn").build(); @@ -403,6 +423,166 @@ mod tests { } #[test] + fn parse_default_bare_level_off_lc() { + let logger = Builder::new().parse("off").build(); + assert!(!enabled(&logger.directives, Level::Error, "")); + assert!(!enabled(&logger.directives, Level::Warn, "")); + assert!(!enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_off_uc() { + let logger = Builder::new().parse("OFF").build(); + assert!(!enabled(&logger.directives, Level::Error, "")); + assert!(!enabled(&logger.directives, Level::Warn, "")); + assert!(!enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_error_lc() { + let logger = Builder::new().parse("error").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(!enabled(&logger.directives, Level::Warn, "")); + assert!(!enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_error_uc() { + let logger = Builder::new().parse("ERROR").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(!enabled(&logger.directives, Level::Warn, "")); + assert!(!enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_warn_lc() { + let logger = Builder::new().parse("warn").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(!enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_warn_uc() { + let logger = Builder::new().parse("WARN").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(!enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_info_lc() { + let logger = Builder::new().parse("info").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_info_uc() { + let logger = Builder::new().parse("INFO").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(!enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_debug_lc() { + let logger = Builder::new().parse("debug").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_debug_uc() { + let logger = Builder::new().parse("DEBUG").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_trace_lc() { + let logger = Builder::new().parse("trace").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(enabled(&logger.directives, Level::Trace, "")); + } + + #[test] + fn parse_default_bare_level_trace_uc() { + let logger = Builder::new().parse("TRACE").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(enabled(&logger.directives, Level::Trace, "")); + } + + // In practice, the desired log level is typically specified by a token + // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g, + // 'TRACE'), but this tests serves as a reminder that + // log::Level::from_str() ignores all case variants. + #[test] + fn parse_default_bare_level_debug_mixed() { + { + let logger = Builder::new().parse("Debug").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + { + let logger = Builder::new().parse("debuG").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + { + let logger = Builder::new().parse("deBug").build(); + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + { + let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor! + assert!(enabled(&logger.directives, Level::Error, "")); + assert!(enabled(&logger.directives, Level::Warn, "")); + assert!(enabled(&logger.directives, Level::Info, "")); + assert!(enabled(&logger.directives, Level::Debug, "")); + assert!(!enabled(&logger.directives, Level::Trace, "")); + } + } + + #[test] fn match_full_path() { let logger = make_logger_filter(vec![ Directive { @@ -558,6 +738,55 @@ mod tests { } #[test] + fn parse_spec_empty_level_isolated() { + // test parse_spec with "" as log level (and the entire spec str) + let (dirs, filter) = parse_spec(""); // should be ignored + assert_eq!(dirs.len(), 0); + assert!(filter.is_none()); + } + + #[test] + fn parse_spec_blank_level_isolated() { + // test parse_spec with a white-space-only string specified as the log + // level (and the entire spec str) + let (dirs, filter) = parse_spec(" "); // should be ignored + assert_eq!(dirs.len(), 0); + assert!(filter.is_none()); + } + + #[test] + fn parse_spec_blank_level_isolated_comma_only() { + // The spec should contain zero or more comma-separated string slices, + // so a comma-only string should be interpretted as two empty strings + // (which should both be treated as invalid, so ignored). + let (dirs, filter) = parse_spec(","); // should be ignored + assert_eq!(dirs.len(), 0); + assert!(filter.is_none()); + } + + #[test] + fn parse_spec_blank_level_isolated_comma_blank() { + // The spec should contain zero or more comma-separated string slices, + // so this bogus spec should be interpretted as containing one empty + // string and one blank string. Both should both be treated as + // invalid, so ignored. + let (dirs, filter) = parse_spec(", "); // should be ignored + assert_eq!(dirs.len(), 0); + assert!(filter.is_none()); + } + + #[test] + fn parse_spec_blank_level_isolated_blank_comma() { + // The spec should contain zero or more comma-separated string slices, + // so this bogus spec should be interpretted as containing one blank + // string and one empty string. Both should both be treated as + // invalid, so ignored. + let (dirs, filter) = parse_spec(" ,"); // should be ignored + assert_eq!(dirs.len(), 0); + assert!(filter.is_none()); + } + + #[test] fn parse_spec_global() { // test parse_spec with no crate let (dirs, filter) = parse_spec("warn,crate2=debug"); @@ -570,6 +799,36 @@ mod tests { } #[test] + fn parse_spec_global_bare_warn_lc() { + // test parse_spec with no crate, in isolation, all lowercase + let (dirs, filter) = parse_spec("warn"); + assert_eq!(dirs.len(), 1); + assert_eq!(dirs[0].name, None); + assert_eq!(dirs[0].level, LevelFilter::Warn); + assert!(filter.is_none()); + } + + #[test] + fn parse_spec_global_bare_warn_uc() { + // test parse_spec with no crate, in isolation, all uppercase + let (dirs, filter) = parse_spec("WARN"); + assert_eq!(dirs.len(), 1); + assert_eq!(dirs[0].name, None); + assert_eq!(dirs[0].level, LevelFilter::Warn); + assert!(filter.is_none()); + } + + #[test] + fn parse_spec_global_bare_warn_mixed() { + // test parse_spec with no crate, in isolation, mixed case + let (dirs, filter) = parse_spec("wArN"); + assert_eq!(dirs.len(), 1); + assert_eq!(dirs[0].name, None); + assert_eq!(dirs[0].level, LevelFilter::Warn); + assert!(filter.is_none()); + } + + #[test] fn parse_spec_valid_filter() { let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc"); assert_eq!(dirs.len(), 3); diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 558c4d9..3c4fee0 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -144,6 +144,7 @@ pub(crate) struct Builder { pub format_level: bool, pub format_indent: Option<usize>, pub custom_format: Option<FormatFn>, + pub format_suffix: &'static str, built: bool, } @@ -155,6 +156,7 @@ impl Default for Builder { format_level: true, format_indent: Some(4), custom_format: None, + format_suffix: "\n", built: false, } } @@ -187,6 +189,7 @@ impl Builder { level: built.format_level, written_header_value: false, indent: built.format_indent, + suffix: built.format_suffix, buf, }; @@ -211,6 +214,7 @@ struct DefaultFormat<'a> { written_header_value: bool, indent: Option<usize>, buf: &'a mut Formatter, + suffix: &'a str, } impl<'a> DefaultFormat<'a> { @@ -319,7 +323,7 @@ impl<'a> DefaultFormat<'a> { fn write_args(&mut self, record: &Record) -> io::Result<()> { match self.indent { // Fast path for no indentation - None => writeln!(self.buf, "{}", record.args()), + None => write!(self.buf, "{}{}", record.args(), self.suffix), Some(indent_count) => { // Create a wrapper around the buffer only if we have to actually indent the message @@ -334,7 +338,13 @@ impl<'a> DefaultFormat<'a> { 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)?; + write!( + self.fmt.buf, + "{}{:width$}", + self.fmt.suffix, + "", + width = self.indent_count + )?; } self.fmt.buf.write_all(chunk)?; first = false; @@ -357,7 +367,7 @@ impl<'a> DefaultFormat<'a> { write!(wrapper, "{}", record.args())?; } - writeln!(self.buf)?; + write!(self.buf, "{}", self.suffix)?; Ok(()) } @@ -402,6 +412,7 @@ mod tests { level: true, written_header_value: false, indent: None, + suffix: "\n", buf: &mut f, }); @@ -422,6 +433,7 @@ mod tests { level: false, written_header_value: false, indent: None, + suffix: "\n", buf: &mut f, }); @@ -442,6 +454,7 @@ mod tests { level: true, written_header_value: false, indent: Some(4), + suffix: "\n", buf: &mut f, }); @@ -462,6 +475,7 @@ mod tests { level: true, written_header_value: false, indent: Some(0), + suffix: "\n", buf: &mut f, }); @@ -482,9 +496,52 @@ mod tests { level: false, written_header_value: false, indent: Some(4), + suffix: "\n", buf: &mut f, }); assert_eq!("log\n message\n", written); } + + #[test] + fn format_suffix() { + 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: None, + suffix: "\n\n", + buf: &mut f, + }); + + assert_eq!("log\nmessage\n\n", written); + } + + #[test] + fn format_suffix_with_indent() { + 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), + suffix: "\n\n", + buf: &mut f, + }); + + assert_eq!("log\n\n message\n\n", written); + } } @@ -8,9 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A simple logger configured via environment variables which writes -//! to stdout or stderr, for use with the logging facade exposed by the -//! [`log` crate][log-crate-url]. +//! A simple logger that can be configured via environment variables, for use +//! with the logging facade exposed by the [`log` crate][log-crate-url]. +//! +//! Despite having "env" in its name, **`env_logger`** can also be configured by +//! other means besides environment variables. See [the examples][gh-repo-examples] +//! in the source repository for more approaches. +//! +//! By default, `env_logger` writes logs to `stderr`, but can be configured to +//! instead write them to `stdout`. //! //! ## Example //! @@ -83,11 +89,12 @@ //! //! ## Enabling logging //! -//! Log levels are controlled on a per-module basis, and by default all logging -//! is disabled except for `error!`. Logging is controlled via the `RUST_LOG` -//! environment variable. The value of this environment variable is a -//! comma-separated list of logging directives. A logging directive is of the -//! form: +//! Log levels are controlled on a per-module basis, and **by default all +//! logging is disabled except for the `error` level**. +//! +//! Logging is controlled via the **`RUST_LOG`** environment variable. The +//! value of this environment variable is a comma-separated list of *logging +//! directives*. A logging directive is of the form: //! //! ```text //! path::to::module=level @@ -99,21 +106,51 @@ //! Furthermore, this path is a prefix-search, so all modules nested in the //! specified module will also have logging enabled. //! -//! The actual `level` is optional to specify. If omitted, all logging will -//! be enabled. If specified, it must be one of the strings `debug`, `error`, -//! `info`, `warn`, or `trace`. +//! 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. +//! +//! 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` +//! +//! 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 +//! with the logging levels, the letter case is not significant[^fn-off]. +//! +//! [^fn-off]: Similar to the universe of log level names, the `off` pseudo +//! log level feature is also provided by the underlying `log` crate. +//! +//! The letter case is not significant for the logging level names; e.g., +//! `debug`, `DEBUG`, and `dEbuG` all represent the same logging level. For +//! consistency, our convention is to use the lower case names. Where our docs +//! do use other forms, they do so in the context of specific examples, so you +//! won't be surprised if you see similar usage in the wild. //! //! As the log level for a module is optional, the module to enable logging for -//! is also optional. If only a `level` is provided, then the global log -//! level for all modules is set to this value. +//! is also optional. **If only a level is provided, then the global log +//! level for all modules is set to this value.** //! //! Some examples of valid values of `RUST_LOG` are: //! //! * `hello` turns on all logging for the 'hello' module +//! * `trace` turns on all logging for the application, regardless of its name +//! * `TRACE` turns on all logging for the application, regardless of its name (same as previous) //! * `info` turns on all info logging +//! * `INFO` turns on all info logging (same as previous) //! * `hello=debug` turns on debug logging for 'hello' +//! * `hello=DEBUG` turns on debug logging for 'hello' (same as previous) //! * `hello,std::option` turns on hello, and std's option logging //! * `error,hello=warn` turn on global error logging and also warn for hello +//! * `error,hello=off` turn on global error logging, but turn off logging for hello +//! * `off` turns off all logging for the application +//! * `OFF` turns off all logging for the application (same as previous) //! //! ## Filtering results //! @@ -223,6 +260,8 @@ //! 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 +//! [level-enum]: https://docs.rs/log/latest/log/enum.Level.html //! [log-crate-url]: https://docs.rs/log/ //! [`Builder`]: struct.Builder.html //! [`Builder::is_test`]: struct.Builder.html#method.is_test @@ -231,15 +270,13 @@ #![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.8.2" + html_favicon_url = "https://www.rust-lang.org/static/images/favicon.ico" )] -#![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)] +#![deny(missing_debug_implementations, missing_docs)] use std::{borrow::Cow, cell::RefCell, env, io}; @@ -594,6 +631,12 @@ impl Builder { self.format_timestamp(Some(fmt::TimestampPrecision::Nanos)) } + /// Configures the end of line suffix. + pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self { + self.format.format_suffix = suffix; + self + } + /// Adds a directive to the filter for a specific module. /// /// # Examples |