env_logger-0.10.0/.cargo_vcs_info.json0000644000000001360000000000100132410ustar { "git": { "sha1": "ff029fa8b6258ed926134f51d38ee85ebd553c35" }, "path_in_vcs": "" }env_logger-0.10.0/Cargo.lock0000644000000154370000000000100112260ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "env_logger" version = "0.10.0" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ "errno-dragonfly", "libc", "winapi", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "io-lifetimes" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7d367024b3f3414d8e01f437f704f41a9f64ab36f9067fa73e526ad4c763c87" dependencies = [ "libc", "windows-sys", ] [[package]] name = "is-terminal" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d" dependencies = [ "hermit-abi", "io-lifetimes", "rustix", "windows-sys", ] [[package]] name = "libc" version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "linux-raw-sys" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "regex" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustix" version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "termcolor" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] name = "windows_aarch64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" env_logger-0.10.0/Cargo.toml0000644000000050710000000000100112420ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.60.0" name = "env_logger" version = "0.10.0" include = [ "build.rs", "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "tests/**/*", "benches/**/*", "examples/**/*", ] description = """ A logging implementation for `log` which is configured via an environment variable. """ readme = "README.md" keywords = [ "logging", "log", "logger", ] categories = ["development-tools::debugging"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-cli/env_logger/" [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "Unreleased" replace = "{{version}}" min = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '\.\.\.HEAD' replace = "...{{tag_name}}" exactly = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "ReleaseDate" replace = "{{date}}" min = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "" replace = """ ## [Unreleased] - ReleaseDate """ exactly = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "" replace = """ [Unreleased]: https://github.com/rust-cli/env_logger/compare/{{tag_name}}...HEAD""" exactly = 1 [[test]] name = "regexp_filter" harness = false [[test]] name = "log-in-log" harness = false [[test]] name = "log_tls_dtors" harness = false [[test]] name = "init-twice-retains-filter" harness = false [dependencies.humantime] version = "2.0.0" optional = true [dependencies.is-terminal] version = "0.4.0" optional = true [dependencies.log] version = "0.4.8" features = ["std"] [dependencies.regex] version = "1.0.3" features = [ "std", "perf", ] optional = true default-features = false [dependencies.termcolor] version = "1.1.1" optional = true [features] auto-color = [ "dep:is-terminal", "color", ] color = ["dep:termcolor"] default = [ "auto-color", "humantime", "regex", ] humantime = ["dep:humantime"] regex = ["dep:regex"] env_logger-0.10.0/Cargo.toml.orig000064400000000000000000000034751046102023000147310ustar 00000000000000[workspace] members = [ "ci" ] [package] name = "env_logger" version = "0.10.0" description = """ A logging implementation for `log` which is configured via an environment variable. """ license = "MIT OR Apache-2.0" repository = "https://github.com/rust-cli/env_logger/" categories = ["development-tools::debugging"] keywords = ["logging", "log", "logger"] edition = "2021" rust-version = "1.60.0" # MSRV include = [ "build.rs", "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "tests/**/*", "benches/**/*", "examples/**/*" ] [package.metadata.release] pre-release-replacements = [ {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/env_logger/compare/{{tag_name}}...HEAD", exactly=1}, ] [features] default = ["auto-color", "humantime", "regex"] color = ["dep:termcolor"] auto-color = ["dep:is-terminal", "color"] humantime = ["dep:humantime"] regex = ["dep:regex"] [dependencies] log = { version = "0.4.8", features = ["std"] } regex = { version = "1.0.3", optional = true, default-features=false, features=["std", "perf"] } termcolor = { version = "1.1.1", optional = true } humantime = { version = "2.0.0", optional = true } is-terminal = { version = "0.4.0", optional = true } [[test]] name = "regexp_filter" harness = false [[test]] name = "log-in-log" harness = false [[test]] name = "log_tls_dtors" harness = false [[test]] name = "init-twice-retains-filter" harness = false env_logger-0.10.0/LICENSE-APACHE000064400000000000000000000251371046102023000137650ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. env_logger-0.10.0/LICENSE-MIT000064400000000000000000000017771046102023000135010ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. env_logger-0.10.0/README.md000064400000000000000000000120231046102023000133060ustar 00000000000000# 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) [![Documentation](https://img.shields.io/badge/docs-main-blue.svg)](https://env-logger-rs.github.io/env_logger/env_logger/index.html) Implements a logger that can be configured via environment variables. ## Usage ### In libraries `env_logger` makes sense when used in executables (binary projects). Libraries should use the [`log`](https://docs.rs/log) crate instead. ### In executables It must be added along with `log` to the project dependencies: ```toml [dependencies] log = "0.4.0" env_logger = "0.9.0" ``` `env_logger` must be initialized as early as possible in the project. After it's initialized, you can use the `log` macros to do actual logging. ```rust #[macro_use] extern crate log; fn main() { env_logger::init(); info!("starting up"); // ... } ``` 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 $ 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/main/examples) for more approaches. ### In tests Tests can use the `env_logger` crate to see log messages generated during that test: ```toml [dependencies] log = "0.4.0" [dev-dependencies] env_logger = "0.9.0" ``` ```rust #[macro_use] extern crate log; fn add_one(num: i32) -> i32 { info!("add_one called with {}", num); num + 1 } #[cfg(test)] mod tests { use super::*; fn init() { let _ = env_logger::builder().is_test(true).try_init(); } #[test] fn it_adds_one() { init(); info!("can log from the test too"); assert_eq!(3, add_one(2)); } #[test] fn it_handles_negative_numbers() { init(); info!("logging from another test"); assert_eq!(-7, add_one(-8)); } } ``` Assuming the module under test is called `my_lib`, running the tests with the `RUST_LOG` filtering to info messages from this module looks like: ```bash $ RUST_LOG=my_lib=info cargo test Running target/debug/my_lib-... running 2 tests [INFO my_lib::tests] logging from another test [INFO my_lib] add_one called with -8 test tests::it_handles_negative_numbers ... ok [INFO my_lib::tests] can log from the test too [INFO my_lib] add_one called with 2 test tests::it_adds_one ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured ``` Note that `env_logger::try_init()` needs to be called in each test in which you want to enable logging. Additionally, the default behavior of tests to run in parallel means that logging output may be interleaved with test output. Either run tests in a single thread by specifying `RUST_TEST_THREADS=1` or by running one test by specifying its name as an argument to the test binaries as directed by the `cargo test` help docs: ```bash $ RUST_LOG=my_lib=info cargo test it_adds_one Running target/debug/my_lib-... running 1 test [INFO my_lib::tests] can log from the test too [INFO my_lib] add_one called with 2 test tests::it_adds_one ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured ``` ## Configuring log target By default, `env_logger` logs to stderr. If you want to log to stdout instead, you can use the `Builder` to change the log target: ```rust use std::env; use env_logger::{Builder, Target}; let mut builder = Builder::from_default_env(); builder.target(Target::Stdout); builder.init(); ``` ## Stability of the default format The default format won't optimise for long-term stability, and explicitly makes no guarantees about the stability of its output across major, minor or patch version bumps during `0.x`. If you want to capture or interpret the output of `env_logger` programmatically then you should use a custom format. env_logger-0.10.0/examples/custom_default_format.rs000064400000000000000000000014441046102023000206060ustar 00000000000000/*! 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`"); } env_logger-0.10.0/examples/custom_format.rs000064400000000000000000000024371046102023000171050ustar 00000000000000/*! 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 = "color", 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 = "color", feature = "humantime")))] fn main() {} env_logger-0.10.0/examples/custom_logger.rs000064400000000000000000000024461046102023000170740ustar 00000000000000/*! 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: &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`"); } env_logger-0.10.0/examples/default.rs000064400000000000000000000015301046102023000156400ustar 00000000000000/*! 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"); } env_logger-0.10.0/examples/direct_logger.rs000064400000000000000000000017411046102023000170310ustar 00000000000000/*! 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()); } env_logger-0.10.0/examples/filters_from_code.rs000064400000000000000000000006121046102023000177010ustar 00000000000000/*! 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"); } env_logger-0.10.0/examples/in_tests.rs000064400000000000000000000021071046102023000160450ustar 00000000000000/*! 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); } } env_logger-0.10.0/examples/syslog_friendly_format.rs000064400000000000000000000013561046102023000210060ustar 00000000000000use 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(), }; } env_logger-0.10.0/src/filter/mod.rs000064400000000000000000000742171046102023000152450ustar 00000000000000//! Filtering for log records. //! //! This module contains the log filtering used by `env_logger` to match records. //! You can use the `Filter` type in your own logger implementation to use the same //! filter parsing and matching as `env_logger`. For more details about the format //! for directive strings see [Enabling Logging]. //! //! ## Using `env_logger` in your own logger //! //! You can use `env_logger`'s filtering functionality with your own logger. //! Call [`Builder::parse`] to parse directives from a string when constructing //! your logger. Call [`Filter::matches`] to check whether a record should be //! logged based on the parsed filters when log records are received. //! //! ``` //! extern crate log; //! extern crate env_logger; //! use env_logger::filter::Filter; //! use log::{Log, Metadata, Record}; //! //! struct MyLogger { //! filter: Filter //! } //! //! impl MyLogger { //! fn new() -> MyLogger { //! use env_logger::filter::Builder; //! let mut builder = Builder::new(); //! //! // Parse a directives string from an environment variable //! if let Ok(ref filter) = std::env::var("MY_LOG_LEVEL") { //! builder.parse(filter); //! } //! //! MyLogger { //! filter: builder.build() //! } //! } //! } //! //! impl Log for MyLogger { //! fn enabled(&self, metadata: &Metadata) -> bool { //! self.filter.enabled(metadata) //! } //! //! fn log(&self, record: &Record) { //! // Check if the record is matched by the filter //! if self.filter.matches(record) { //! println!("{:?}", record); //! } //! } //! //! fn flush(&self) {} //! } //! ``` //! //! [Enabling Logging]: ../index.html#enabling-logging //! [`Builder::parse`]: struct.Builder.html#method.parse //! [`Filter::matches`]: struct.Filter.html#method.matches use log::{Level, LevelFilter, Metadata, Record}; use std::collections::HashMap; use std::env; use std::fmt; use std::mem; #[cfg(feature = "regex")] #[path = "regex.rs"] mod inner; #[cfg(not(feature = "regex"))] #[path = "string.rs"] mod inner; /// A log filter. /// /// This struct can be used to determine whether or not a log record /// should be written to the output. /// Use the [`Builder`] type to parse and construct a `Filter`. /// /// [`Builder`]: struct.Builder.html pub struct Filter { directives: Vec, filter: Option, } /// A builder for a log filter. /// /// It can be used to parse a set of directives from a string before building /// a [`Filter`] instance. /// /// ## Example /// /// ``` /// # #[macro_use] extern crate log; /// # use std::env; /// use env_logger::filter::Builder; /// /// let mut builder = Builder::new(); /// /// // Parse a logging filter from an environment variable. /// if let Ok(rust_log) = env::var("RUST_LOG") { /// builder.parse(&rust_log); /// } /// /// let filter = builder.build(); /// ``` /// /// [`Filter`]: struct.Filter.html pub struct Builder { directives: HashMap, LevelFilter>, filter: Option, built: bool, } #[derive(Debug)] struct Directive { name: Option, level: LevelFilter, } impl Filter { /// Returns the maximum `LevelFilter` that this filter instance is /// configured to output. /// /// # Example /// /// ```rust /// use log::LevelFilter; /// use env_logger::filter::Builder; /// /// let mut builder = Builder::new(); /// builder.filter(Some("module1"), LevelFilter::Info); /// builder.filter(Some("module2"), LevelFilter::Error); /// /// let filter = builder.build(); /// assert_eq!(filter.filter(), LevelFilter::Info); /// ``` pub fn filter(&self) -> LevelFilter { self.directives .iter() .map(|d| d.level) .max() .unwrap_or(LevelFilter::Off) } /// Checks if this record matches the configured filter. pub fn matches(&self, record: &Record) -> bool { if !self.enabled(record.metadata()) { return false; } if let Some(filter) = self.filter.as_ref() { if !filter.is_match(&record.args().to_string()) { return false; } } true } /// Determines if a log message with the specified metadata would be logged. pub fn enabled(&self, metadata: &Metadata) -> bool { let level = metadata.level(); let target = metadata.target(); enabled(&self.directives, level, target) } } impl Builder { /// Initializes the filter builder with defaults. pub fn new() -> Builder { Builder { directives: HashMap::new(), filter: None, built: false, } } /// Initializes the filter builder from an environment. pub fn from_env(env: &str) -> Builder { let mut builder = Builder::new(); if let Ok(s) = env::var(env) { builder.parse(&s); } builder } /// Adds a directive to the filter for a specific module. pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self { self.filter(Some(module), level) } /// Adds a directive to the filter for all modules. pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self { self.filter(None, level) } /// Adds a directive to the filter. /// /// 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.insert(module.map(|s| s.to_string()), level); self } /// Parses the directives string. /// /// See the [Enabling Logging] section for more details. /// /// [Enabling Logging]: ../index.html#enabling-logging pub fn parse(&mut self, filters: &str) -> &mut Self { let (directives, filter) = parse_spec(filters); self.filter = filter; for directive in directives { self.directives.insert(directive.name, directive.level); } self } /// Build a log filter. pub fn build(&mut self) -> Filter { 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 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. 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) }); } Filter { directives: mem::take(&mut directives), filter: mem::replace(&mut self.filter, None), } } } impl Default for Builder { fn default() -> Self { Builder::new() } } impl fmt::Debug for Filter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Filter") .field("filter", &self.filter) .field("directives", &self.directives) .finish() } } impl fmt::Debug for Builder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.built { f.debug_struct("Filter").field("built", &true).finish() } else { f.debug_struct("Filter") .field("filter", &self.filter) .field("directives", &self.directives) .finish() } } } /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo") /// and return a vector with log directives. fn parse_spec(spec: &str) -> (Vec, Option) { let mut dirs = Vec::new(); let mut parts = spec.split('/'); let mods = parts.next(); let filter = parts.next(); if parts.next().is_some() { eprintln!( "warning: invalid logging spec '{}', \ ignoring it (too many '/'s)", spec ); return (dirs, None); } if let Some(m) = mods { for s in m.split(',').map(|ss| ss.trim()) { if s.is_empty() { continue; } let mut parts = s.split('='); let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) { (Some(part0), None, None) => { // if the single argument is a log-level string or number, // treat that as a global fallback match part0.parse() { Ok(num) => (num, None), Err(_) => (LevelFilter::max(), Some(part0)), } } (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)), (Some(part0), Some(part1), None) => match part1.parse() { Ok(num) => (num, Some(part0)), _ => { eprintln!( "warning: invalid logging spec '{}', \ ignoring it", part1 ); continue; } }, _ => { eprintln!( "warning: invalid logging spec '{}', \ ignoring it", s ); continue; } }; dirs.push(Directive { name: name.map(|s| s.to_string()), level: log_level, }); } } let filter = filter.and_then(|filter| match inner::Filter::new(filter) { Ok(re) => Some(re), Err(e) => { eprintln!("warning: invalid regex filter - {}", e); None } }); (dirs, filter) } // Check whether a level and target are enabled by the set of directives. fn enabled(directives: &[Directive], level: Level, target: &str) -> bool { // Search for the longest match, the vector is assumed to be pre-sorted. for directive in directives.iter().rev() { match directive.name { Some(ref name) if !target.starts_with(&**name) => {} Some(..) | None => return level <= directive.level, } } false } #[cfg(test)] mod tests { use log::{Level, LevelFilter}; use super::{enabled, parse_spec, Builder, Directive, Filter}; fn make_logger_filter(dirs: Vec) -> Filter { let mut logger = Builder::new().build(); logger.directives = dirs; logger } #[test] fn filter_info() { let logger = Builder::new().filter(None, LevelFilter::Info).build(); assert!(enabled(&logger.directives, Level::Info, "crate1")); assert!(!enabled(&logger.directives, Level::Debug, "crate1")); } #[test] fn filter_beginning_longest_match() { let logger = Builder::new() .filter(Some("crate2"), LevelFilter::Info) .filter(Some("crate2::mod"), LevelFilter::Debug) .filter(Some("crate1::mod1"), LevelFilter::Warn) .build(); assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1")); 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(); assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1")); assert!(enabled(&logger.directives, Level::Info, "crate2::mod2")); } #[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 { name: Some("crate2".to_string()), level: LevelFilter::Info, }, Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn, }, ]); assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1")); assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1")); assert!(enabled(&logger.directives, Level::Info, "crate2")); assert!(!enabled(&logger.directives, Level::Debug, "crate2")); } #[test] fn no_match() { let logger = make_logger_filter(vec![ Directive { name: Some("crate2".to_string()), level: LevelFilter::Info, }, Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn, }, ]); assert!(!enabled(&logger.directives, Level::Warn, "crate3")); } #[test] fn match_beginning() { let logger = make_logger_filter(vec![ Directive { name: Some("crate2".to_string()), level: LevelFilter::Info, }, Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn, }, ]); assert!(enabled(&logger.directives, Level::Info, "crate2::mod1")); } #[test] fn match_beginning_longest_match() { let logger = make_logger_filter(vec![ Directive { name: Some("crate2".to_string()), level: LevelFilter::Info, }, Directive { name: Some("crate2::mod".to_string()), level: LevelFilter::Debug, }, Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn, }, ]); assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1")); assert!(!enabled(&logger.directives, Level::Debug, "crate2")); } #[test] fn match_default() { let logger = make_logger_filter(vec![ Directive { name: None, level: LevelFilter::Info, }, Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn, }, ]); assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1")); assert!(enabled(&logger.directives, Level::Info, "crate2::mod2")); } #[test] fn zero_level() { let logger = make_logger_filter(vec![ Directive { name: None, level: LevelFilter::Info, }, Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Off, }, ]); assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1")); assert!(enabled(&logger.directives, Level::Info, "crate2::mod2")); } #[test] fn parse_spec_valid() { let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug"); assert_eq!(dirs.len(), 3); assert_eq!(dirs[0].name, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::Error); assert_eq!(dirs[1].name, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::max()); assert_eq!(dirs[2].name, Some("crate2".to_string())); assert_eq!(dirs[2].level, LevelFilter::Debug); assert!(filter.is_none()); } #[test] fn parse_spec_invalid_crate() { // test parse_spec with multiple = in specification let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug"); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::Debug); assert!(filter.is_none()); } #[test] fn parse_spec_invalid_level() { // test parse_spec with 'noNumber' as log level let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug"); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::Debug); assert!(filter.is_none()); } #[test] fn parse_spec_string_level() { // test parse_spec with 'warn' as log level let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn"); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::Warn); assert!(filter.is_none()); } #[test] fn parse_spec_empty_level() { // test parse_spec with '' as log level let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2="); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::max()); assert!(filter.is_none()); } #[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 interpreted 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 interpreted 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 interpreted 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"); assert_eq!(dirs.len(), 2); assert_eq!(dirs[0].name, None); assert_eq!(dirs[0].level, LevelFilter::Warn); assert_eq!(dirs[1].name, Some("crate2".to_string())); assert_eq!(dirs[1].level, LevelFilter::Debug); assert!(filter.is_none()); } #[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); assert_eq!(dirs[0].name, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::Error); assert_eq!(dirs[1].name, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::max()); assert_eq!(dirs[2].name, Some("crate2".to_string())); assert_eq!(dirs[2].level, LevelFilter::Debug); assert!(filter.is_some() && filter.unwrap().to_string() == "abc"); } #[test] fn parse_spec_invalid_crate_filter() { let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c"); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::Debug); assert!(filter.is_some() && filter.unwrap().to_string() == "a.c"); } #[test] fn parse_spec_empty_with_filter() { let (dirs, filter) = parse_spec("crate1/a*c"); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate1".to_string())); assert_eq!(dirs[0].level, LevelFilter::max()); assert!(filter.is_some() && filter.unwrap().to_string() == "a*c"); } } env_logger-0.10.0/src/filter/regex.rs000064400000000000000000000010301046102023000155570ustar 00000000000000extern crate regex; use std::fmt; use self::regex::Regex; #[derive(Debug)] pub struct Filter { inner: Regex, } impl Filter { pub fn new(spec: &str) -> Result { match Regex::new(spec) { Ok(r) => Ok(Filter { inner: r }), Err(e) => Err(e.to_string()), } } pub fn is_match(&self, s: &str) -> bool { self.inner.is_match(s) } } impl fmt::Display for Filter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) } } env_logger-0.10.0/src/filter/string.rs000064400000000000000000000006551046102023000157670ustar 00000000000000use std::fmt; #[derive(Debug)] pub struct Filter { inner: String, } impl Filter { pub fn new(spec: &str) -> Result { Ok(Filter { inner: spec.to_string(), }) } pub fn is_match(&self, s: &str) -> bool { s.contains(&self.inner) } } impl fmt::Display for Filter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) } } env_logger-0.10.0/src/fmt/humantime/extern_impl.rs000064400000000000000000000065501046102023000202770ustar 00000000000000use std::fmt; use std::time::SystemTime; use humantime::{ format_rfc3339_micros, format_rfc3339_millis, format_rfc3339_nanos, format_rfc3339_seconds, }; use crate::fmt::{Formatter, TimestampPrecision}; pub(in crate::fmt) mod glob { pub use super::*; } impl Formatter { /// Get a [`Timestamp`] for the current date and time in UTC. /// /// # Examples /// /// Include the current timestamp with the log record: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let ts = buf.timestamp(); /// /// writeln!(buf, "{}: {}: {}", ts, record.level(), record.args()) /// }); /// ``` /// /// [`Timestamp`]: struct.Timestamp.html pub fn timestamp(&self) -> Timestamp { Timestamp { time: SystemTime::now(), precision: TimestampPrecision::Seconds, } } /// Get a [`Timestamp`] for the current date and time in UTC with full /// second precision. pub fn timestamp_seconds(&self) -> Timestamp { Timestamp { time: SystemTime::now(), precision: TimestampPrecision::Seconds, } } /// Get a [`Timestamp`] for the current date and time in UTC with /// millisecond precision. pub fn timestamp_millis(&self) -> Timestamp { Timestamp { time: SystemTime::now(), precision: TimestampPrecision::Millis, } } /// Get a [`Timestamp`] for the current date and time in UTC with /// microsecond precision. pub fn timestamp_micros(&self) -> Timestamp { Timestamp { time: SystemTime::now(), precision: TimestampPrecision::Micros, } } /// Get a [`Timestamp`] for the current date and time in UTC with /// nanosecond precision. pub fn timestamp_nanos(&self) -> Timestamp { Timestamp { time: SystemTime::now(), precision: TimestampPrecision::Nanos, } } } /// An [RFC3339] formatted timestamp. /// /// The timestamp implements [`Display`] and can be written to a [`Formatter`]. /// /// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt /// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html /// [`Formatter`]: struct.Formatter.html pub struct Timestamp { time: SystemTime, precision: TimestampPrecision, } impl fmt::Debug for Timestamp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation. struct TimestampValue<'a>(&'a Timestamp); impl<'a> fmt::Debug for TimestampValue<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } f.debug_tuple("Timestamp") .field(&TimestampValue(self)) .finish() } } impl fmt::Display for Timestamp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let formatter = match self.precision { TimestampPrecision::Seconds => format_rfc3339_seconds, TimestampPrecision::Millis => format_rfc3339_millis, TimestampPrecision::Micros => format_rfc3339_micros, TimestampPrecision::Nanos => format_rfc3339_nanos, }; formatter(self.time).fmt(f) } } env_logger-0.10.0/src/fmt/humantime/mod.rs000064400000000000000000000004631046102023000165250ustar 00000000000000/* This internal module contains the timestamp implementation. Its public API is available when the `humantime` crate is available. */ #[cfg_attr(feature = "humantime", path = "extern_impl.rs")] #[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")] mod imp; pub(in crate::fmt) use self::imp::*; env_logger-0.10.0/src/fmt/humantime/shim_impl.rs000064400000000000000000000001571046102023000177270ustar 00000000000000/* Timestamps aren't available when we don't have a `humantime` dependency. */ pub(in crate::fmt) mod glob {} env_logger-0.10.0/src/fmt/mod.rs000064400000000000000000000430661046102023000145440ustar 00000000000000//! Formatting for log records. //! //! This module contains a [`Formatter`] that can be used to format log records //! into without needing temporary allocations. Usually you won't need to worry //! about the contents of this module and can use the `Formatter` like an ordinary //! [`Write`]. //! //! # Formatting log records //! //! The format used to print log records can be customised using the [`Builder::format`] //! method. //! Custom formats can apply different color and weight to printed values using //! [`Style`] builders. //! //! ``` //! use std::io::Write; //! //! let mut builder = env_logger::Builder::new(); //! //! builder.format(|buf, record| { //! writeln!(buf, "{}: {}", //! record.level(), //! record.args()) //! }); //! ``` //! //! [`Formatter`]: struct.Formatter.html //! [`Style`]: struct.Style.html //! [`Builder::format`]: ../struct.Builder.html#method.format //! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html use std::cell::RefCell; use std::fmt::Display; use std::io::prelude::*; use std::rc::Rc; use std::{fmt, io, mem}; use log::Record; mod humantime; pub(crate) mod writer; pub use self::humantime::glob::*; pub use self::writer::glob::*; use self::writer::{Buffer, Writer}; pub(crate) mod glob { pub use super::{Target, TimestampPrecision, WriteStyle}; } /// Formatting precision of timestamps. /// /// Seconds give precision of full seconds, milliseconds give thousands of a /// second (3 decimal digits), microseconds are millionth of a second (6 decimal /// digits) and nanoseconds are billionth of a second (9 decimal digits). #[derive(Copy, Clone, Debug)] pub enum TimestampPrecision { /// Full second precision (0 decimal digits) Seconds, /// Millisecond precision (3 decimal digits) Millis, /// Microsecond precision (6 decimal digits) Micros, /// Nanosecond precision (9 decimal digits) Nanos, } /// The default timestamp precision is seconds. impl Default for TimestampPrecision { fn default() -> Self { TimestampPrecision::Seconds } } /// A formatter to write logs into. /// /// `Formatter` implements the standard [`Write`] trait for writing log records. /// It also supports terminal colors, through the [`style`] method. /// /// # Examples /// /// Use the [`writeln`] macro to format a log record. /// An instance of a `Formatter` is passed to an `env_logger` format as `buf`: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())); /// ``` /// /// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html /// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html /// [`style`]: #method.style pub struct Formatter { buf: Rc>, write_style: WriteStyle, } impl Formatter { pub(crate) fn new(writer: &Writer) -> Self { Formatter { buf: Rc::new(RefCell::new(writer.buffer())), write_style: writer.write_style(), } } pub(crate) fn write_style(&self) -> WriteStyle { self.write_style } pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { writer.print(&self.buf.borrow()) } pub(crate) fn clear(&mut self) { self.buf.borrow_mut().clear() } } impl Write for Formatter { fn write(&mut self, buf: &[u8]) -> io::Result { self.buf.borrow_mut().write(buf) } fn flush(&mut self) -> io::Result<()> { self.buf.borrow_mut().flush() } } impl fmt::Debug for Formatter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Formatter").finish() } } pub(crate) type FormatFn = Box io::Result<()> + Sync + Send>; pub(crate) struct Builder { pub format_timestamp: Option, pub format_module_path: bool, pub format_target: bool, pub format_level: bool, pub format_indent: Option, pub custom_format: Option, pub format_suffix: &'static str, built: bool, } impl Default for Builder { fn default() -> Self { Builder { format_timestamp: Some(Default::default()), format_module_path: false, format_target: true, format_level: true, format_indent: Some(4), custom_format: None, format_suffix: "\n", built: false, } } } impl Builder { /// Convert the format into a callable function. /// /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. /// If the `custom_format` is `None`, then a default format is returned. /// Any `default_format` switches set to `false` won't be written by the format. pub fn build(&mut self) -> FormatFn { assert!(!self.built, "attempt to re-use consumed builder"); let built = mem::replace( self, Builder { built: true, ..Default::default() }, ); if let Some(fmt) = built.custom_format { fmt } else { Box::new(move |buf, record| { let fmt = DefaultFormat { timestamp: 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, suffix: built.format_suffix, buf, }; fmt.write(record) }) } } } #[cfg(feature = "color")] type SubtleStyle = StyledValue<'static, &'static str>; #[cfg(not(feature = "color"))] type SubtleStyle = &'static str; /// The default format. /// /// This format needs to work with any combination of crate features. struct DefaultFormat<'a> { timestamp: Option, module_path: bool, target: bool, level: bool, written_header_value: bool, indent: Option, buf: &'a mut Formatter, suffix: &'a str, } impl<'a> DefaultFormat<'a> { fn write(mut self, record: &Record) -> io::Result<()> { self.write_timestamp()?; self.write_level(record)?; self.write_module_path(record)?; self.write_target(record)?; self.finish_header()?; self.write_args(record) } fn subtle_style(&self, text: &'static str) -> SubtleStyle { #[cfg(feature = "color")] { self.buf .style() .set_color(Color::Black) .set_intense(true) .clone() .into_value(text) } #[cfg(not(feature = "color"))] { text } } fn write_header_value(&mut self, value: T) -> io::Result<()> where T: Display, { if !self.written_header_value { self.written_header_value = true; let open_brace = self.subtle_style("["); write!(self.buf, "{}{}", open_brace, value) } else { write!(self.buf, " {}", value) } } fn write_level(&mut self, record: &Record) -> io::Result<()> { if !self.level { return Ok(()); } let level = { #[cfg(feature = "color")] { self.buf.default_styled_level(record.level()) } #[cfg(not(feature = "color"))] { record.level() } }; self.write_header_value(format_args!("{:<5}", level)) } fn write_timestamp(&mut self) -> io::Result<()> { #[cfg(feature = "humantime")] { use self::TimestampPrecision::*; let ts = match self.timestamp { None => return Ok(()), Some(Seconds) => self.buf.timestamp_seconds(), Some(Millis) => self.buf.timestamp_millis(), Some(Micros) => self.buf.timestamp_micros(), Some(Nanos) => self.buf.timestamp_nanos(), }; self.write_header_value(ts) } #[cfg(not(feature = "humantime"))] { // Trick the compiler to think we have used self.timestamp // Workaround for "field is never used: `timestamp`" compiler nag. let _ = self.timestamp; Ok(()) } } fn write_module_path(&mut self, record: &Record) -> io::Result<()> { if !self.module_path { return Ok(()); } if let Some(module_path) = record.module_path() { self.write_header_value(module_path) } else { Ok(()) } } fn 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("]"); write!(self.buf, "{} ", close_brace) } else { Ok(()) } } fn write_args(&mut self, record: &Record) -> io::Result<()> { match self.indent { // Fast path for no indentation 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 struct IndentWrapper<'a, 'b: 'a> { fmt: &'a mut DefaultFormat<'b>, indent_count: usize, } impl<'a, 'b> Write for IndentWrapper<'a, 'b> { fn write(&mut self, buf: &[u8]) -> io::Result { let mut first = true; for chunk in buf.split(|&x| x == b'\n') { if !first { write!( self.fmt.buf, "{}{:width$}", self.fmt.suffix, "", width = self.indent_count )?; } self.fmt.buf.write_all(chunk)?; first = false; } Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { self.fmt.buf.flush() } } // The explicit scope here is just to make older versions of Rust happy { let mut wrapper = IndentWrapper { fmt: self, indent_count, }; write!(wrapper, "{}", record.args())?; } write!(self.buf, "{}", self.suffix)?; Ok(()) } } } } #[cfg(test)] mod tests { use super::*; use log::{Level, Record}; fn write_record(record: Record, fmt: DefaultFormat) -> String { let buf = fmt.buf.buf.clone(); 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(target: &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() .write_style(WriteStyle::Never) .build(); let mut f = Formatter::new(&writer); let written = write(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); } #[test] fn format_no_header() { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); let mut f = Formatter::new(&writer); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, level: false, written_header_value: false, indent: None, suffix: "\n", buf: &mut f, }); assert_eq!("log\nmessage\n", written); } #[test] fn format_indent_spaces() { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); let mut f = Formatter::new(&writer); let written = write(DefaultFormat { timestamp: None, module_path: true, target: false, level: true, written_header_value: false, indent: Some(4), suffix: "\n", buf: &mut f, }); assert_eq!("[INFO test::path] log\n message\n", written); } #[test] fn format_indent_zero_spaces() { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); let mut f = Formatter::new(&writer); let written = write(DefaultFormat { timestamp: None, module_path: true, target: false, level: true, written_header_value: false, indent: Some(0), suffix: "\n", buf: &mut f, }); assert_eq!("[INFO test::path] log\nmessage\n", written); } #[test] fn format_indent_spaces_no_header() { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); let mut f = Formatter::new(&writer); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, 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, target: 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, target: 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); } #[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); } } env_logger-0.10.0/src/fmt/writer/atty.rs000064400000000000000000000014001046102023000162440ustar 00000000000000/* This internal module contains the terminal detection implementation. If the `auto-color` feature is enabled then we detect whether we're attached to a particular TTY. Otherwise, assume we're not attached to anything. This effectively prevents styles from being printed. */ #[cfg(feature = "auto-color")] mod imp { use is_terminal::IsTerminal; pub(in crate::fmt) fn is_stdout() -> bool { std::io::stdout().is_terminal() } pub(in crate::fmt) fn is_stderr() -> bool { std::io::stderr().is_terminal() } } #[cfg(not(feature = "auto-color"))] mod imp { pub(in crate::fmt) fn is_stdout() -> bool { false } pub(in crate::fmt) fn is_stderr() -> bool { false } } pub(in crate::fmt) use self::imp::*; env_logger-0.10.0/src/fmt/writer/mod.rs000064400000000000000000000144771046102023000160640ustar 00000000000000mod atty; mod termcolor; use self::atty::{is_stderr, is_stdout}; use self::termcolor::BufferWriter; use std::{fmt, io, mem, sync::Mutex}; pub(super) mod glob { pub use super::termcolor::glob::*; pub use super::*; } pub(super) use self::termcolor::Buffer; /// 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), } impl Default for Target { fn default() -> Self { Target::Stderr } } 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>), } impl From 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 { /// Try to print styles, but don't force the issue. Auto, /// Try very hard to print styles. Always, /// Never print styles. Never, } impl Default for WriteStyle { fn default() -> Self { WriteStyle::Auto } } /// A terminal target with color awareness. pub(crate) struct Writer { inner: BufferWriter, write_style: WriteStyle, } impl Writer { pub fn write_style(&self) -> WriteStyle { self.write_style } pub(super) fn buffer(&self) -> Buffer { self.inner.buffer() } pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> { self.inner.print(buf) } } /// A builder for a terminal writer. /// /// The target and style choice can be configured before building. #[derive(Debug)] pub(crate) struct Builder { target: WritableTarget, write_style: WriteStyle, is_test: bool, built: bool, } impl Builder { /// Initialize the writer builder with defaults. pub(crate) fn new() -> Self { Builder { target: Default::default(), write_style: Default::default(), is_test: false, built: false, } } /// Set the target to write to. pub(crate) fn target(&mut self, target: Target) -> &mut Self { self.target = target.into(); self } /// Parses a style choice string. /// /// See the [Disabling colors] section for more details. /// /// [Disabling colors]: ../index.html#disabling-colors pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self { self.write_style(parse_write_style(write_style)) } /// Whether or not to print style characters when writing. pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self { self.write_style = write_style; self } /// Whether or not to capture logs for `cargo test`. #[allow(clippy::wrong_self_convention)] pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self { self.is_test = is_test; self } /// Build a terminal writer. pub(crate) fn build(&mut self) -> Writer { assert!(!self.built, "attempt to re-use consumed builder"); self.built = true; let color_choice = match self.write_style { WriteStyle::Auto => { if match &self.target { WritableTarget::Stderr => is_stderr(), WritableTarget::Stdout => is_stdout(), WritableTarget::Pipe(_) => false, } { WriteStyle::Auto } else { WriteStyle::Never } } color_choice => 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(color_choice, pipe), }; Writer { inner: writer, write_style: self.write_style, } } } impl Default for Builder { fn default() -> Self { Builder::new() } } impl fmt::Debug for Writer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Writer").finish() } } fn parse_write_style(spec: &str) -> WriteStyle { match spec { "auto" => WriteStyle::Auto, "always" => WriteStyle::Always, "never" => WriteStyle::Never, _ => Default::default(), } } #[cfg(test)] mod tests { use super::*; #[test] fn parse_write_style_valid() { let inputs = vec![ ("auto", WriteStyle::Auto), ("always", WriteStyle::Always), ("never", WriteStyle::Never), ]; for (input, expected) in inputs { assert_eq!(expected, parse_write_style(input)); } } #[test] fn parse_write_style_invalid() { let inputs = vec!["", "true", "false", "NEVER!!"]; for input in inputs { assert_eq!(WriteStyle::Auto, parse_write_style(input)); } } } env_logger-0.10.0/src/fmt/writer/termcolor/extern_impl.rs000064400000000000000000000347301046102023000216330ustar 00000000000000use std::borrow::Cow; 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, WritableTarget, WriteStyle}; pub(in crate::fmt::writer) mod glob { pub use super::*; } impl Formatter { /// Begin a new [`Style`]. /// /// # Examples /// /// Create a bold, red colored style and use it to print the log level: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut level_style = buf.style(); /// /// level_style.set_color(Color::Red).set_bold(true); /// /// writeln!(buf, "{}: {}", /// level_style.value(record.level()), /// record.args()) /// }); /// ``` /// /// [`Style`]: struct.Style.html pub fn style(&self) -> Style { Style { buf: self.buf.clone(), spec: ColorSpec::new(), } } /// Get the default [`Style`] for the given level. /// /// The style can be used to print other values besides the level. pub fn default_level_style(&self, level: Level) -> Style { let mut level_style = self.style(); match level { Level::Trace => level_style.set_color(Color::Cyan), Level::Debug => level_style.set_color(Color::Blue), Level::Info => level_style.set_color(Color::Green), Level::Warn => level_style.set_color(Color::Yellow), Level::Error => level_style.set_color(Color::Red).set_bold(true), }; level_style } /// Get a printable [`Style`] for the given level. /// /// The style can only be used to print the level. pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> { self.default_level_style(level).into_value(level) } } pub(in crate::fmt::writer) struct BufferWriter { inner: termcolor::BufferWriter, uncolored_target: Option, } pub(in crate::fmt) struct Buffer { inner: termcolor::Buffer, has_uncolored_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()), uncolored_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()), uncolored_target: if is_test { Some(WritableTarget::Stdout) } else { None }, } } pub(in crate::fmt::writer) fn pipe( write_style: WriteStyle, pipe: Box>, ) -> Self { BufferWriter { // The inner Buffer is never printed from, but it is still needed to handle coloring and other formatting inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), uncolored_target: Some(WritableTarget::Pipe(pipe)), } } pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { Buffer { inner: self.inner.buffer(), has_uncolored_target: self.uncolored_target.is_some(), } } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { if let Some(target) = &self.uncolored_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 { WritableTarget::Stderr => eprint!("{}", log), WritableTarget::Stdout => print!("{}", log), WritableTarget::Pipe(pipe) => write!(pipe.lock().unwrap(), "{}", log)?, } Ok(()) } else { self.inner.print(&buf.inner) } } } impl Buffer { pub(in crate::fmt) fn clear(&mut self) { self.inner.clear() } pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { self.inner.flush() } pub(in crate::fmt) fn bytes(&self) -> &[u8] { self.inner.as_slice() } fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { // Ignore styles for test captured logs because they can't be printed if !self.has_uncolored_target { self.inner.set_color(spec) } else { Ok(()) } } fn reset(&mut self) -> io::Result<()> { // Ignore styles for test captured logs because they can't be printed if !self.has_uncolored_target { self.inner.reset() } else { Ok(()) } } } impl WriteStyle { fn into_color_choice(self) -> ColorChoice { match self { WriteStyle::Always => ColorChoice::Always, WriteStyle::Auto => ColorChoice::Auto, WriteStyle::Never => ColorChoice::Never, } } } /// A set of styles to apply to the terminal output. /// /// Call [`Formatter::style`] to get a `Style` and use the builder methods to /// set styling properties, like [color] and [weight]. /// To print a value using the style, wrap it in a call to [`value`] when the log /// record is formatted. /// /// # Examples /// /// Create a bold, red colored style and use it to print the log level: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut level_style = buf.style(); /// /// level_style.set_color(Color::Red).set_bold(true); /// /// writeln!(buf, "{}: {}", /// level_style.value(record.level()), /// record.args()) /// }); /// ``` /// /// Styles can be re-used to output multiple values: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut bold = buf.style(); /// /// bold.set_bold(true); /// /// writeln!(buf, "{}: {} {}", /// bold.value(record.level()), /// bold.value("some bold text"), /// record.args()) /// }); /// ``` /// /// [`Formatter::style`]: struct.Formatter.html#method.style /// [color]: #method.set_color /// [weight]: #method.set_bold /// [`value`]: #method.value #[derive(Clone)] pub struct Style { buf: Rc>, spec: ColorSpec, } /// A value that can be printed using the given styles. /// /// It is the result of calling [`Style::value`]. /// /// [`Style::value`]: struct.Style.html#method.value pub struct StyledValue<'a, T> { style: Cow<'a, Style>, value: T, } impl Style { /// Set the text color. /// /// # Examples /// /// Create a style with red text: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_color(Color::Red); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_color(&mut self, color: Color) -> &mut Style { self.spec.set_fg(Some(color.into_termcolor())); self } /// Set the text weight. /// /// If `yes` is true then text will be written in bold. /// If `yes` is false then text will be written in the default weight. /// /// # Examples /// /// Create a style with bold text: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_bold(true); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_bold(&mut self, yes: bool) -> &mut Style { self.spec.set_bold(yes); self } /// Set the text intensity. /// /// If `yes` is true then text will be written in a brighter color. /// If `yes` is false then text will be written in the default color. /// /// # Examples /// /// Create a style with intense text: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_intense(true); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_intense(&mut self, yes: bool) -> &mut Style { self.spec.set_intense(yes); self } /// Set whether the text is dimmed. /// /// If `yes` is true then text will be written in a dimmer color. /// If `yes` is false then text will be written in the default color. /// /// # Examples /// /// Create a style with dimmed text: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_dimmed(true); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_dimmed(&mut self, yes: bool) -> &mut Style { self.spec.set_dimmed(yes); self } /// Set the background color. /// /// # Examples /// /// Create a style with a yellow background: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_bg(Color::Yellow); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_bg(&mut self, color: Color) -> &mut Style { self.spec.set_bg(Some(color.into_termcolor())); self } /// Wrap a value in the style. /// /// The same `Style` can be used to print multiple different values. /// /// # Examples /// /// Create a bold, red colored style and use it to print the log level: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_color(Color::Red).set_bold(true); /// /// writeln!(buf, "{}: {}", /// style.value(record.level()), /// record.args()) /// }); /// ``` pub fn value(&self, value: T) -> StyledValue { StyledValue { style: Cow::Borrowed(self), value, } } /// Wrap a value in the style by taking ownership of it. pub(crate) fn into_value(self, value: T) -> StyledValue<'static, T> { StyledValue { style: Cow::Owned(self), value, } } } impl<'a, T> StyledValue<'a, T> { fn write_fmt(&self, f: F) -> fmt::Result where F: FnOnce() -> fmt::Result, { self.style .buf .borrow_mut() .set_color(&self.style.spec) .map_err(|_| fmt::Error)?; // Always try to reset the terminal style, even if writing failed let write = f(); let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error); write.and(reset) } } impl fmt::Debug for Style { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Style").field("spec", &self.spec).finish() } } macro_rules! impl_styled_value_fmt { ($($fmt_trait:path),*) => { $( impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> { fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { self.write_fmt(|| T::fmt(&self.value, f)) } } )* }; } impl_styled_value_fmt!( fmt::Debug, fmt::Display, fmt::Pointer, fmt::Octal, fmt::Binary, fmt::UpperHex, fmt::LowerHex, fmt::UpperExp, fmt::LowerExp ); // The `Color` type is copied from https://github.com/BurntSushi/termcolor /// The set of available colors for the terminal foreground/background. /// /// The `Ansi256` and `Rgb` colors will only output the correct codes when /// paired with the `Ansi` `WriteColor` implementation. /// /// The `Ansi256` and `Rgb` color types are not supported when writing colors /// on Windows using the console. If they are used on Windows, then they are /// silently ignored and no colors will be emitted. /// /// This set may expand over time. /// /// This type has a `FromStr` impl that can parse colors from their human /// readable form. The format is as follows: /// /// 1. Any of the explicitly listed colors in English. They are matched /// case insensitively. /// 2. A single 8-bit integer, in either decimal or hexadecimal format. /// 3. A triple of 8-bit integers separated by a comma, where each integer is /// in decimal or hexadecimal format. /// /// Hexadecimal numbers are written with a `0x` prefix. #[allow(missing_docs)] #[non_exhaustive] #[derive(Clone, Debug, Eq, PartialEq)] pub enum Color { Black, Blue, Green, Red, Cyan, Magenta, Yellow, White, Ansi256(u8), Rgb(u8, u8, u8), } impl Color { fn into_termcolor(self) -> termcolor::Color { match self { 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), } } } env_logger-0.10.0/src/fmt/writer/termcolor/mod.rs000064400000000000000000000006121046102023000200540ustar 00000000000000/* This internal module contains the style and terminal writing implementation. Its public API is available when the `termcolor` crate is available. The terminal printing is shimmed when the `termcolor` crate is not available. */ #[cfg_attr(feature = "color", path = "extern_impl.rs")] #[cfg_attr(not(feature = "color"), path = "shim_impl.rs")] mod imp; pub(in crate::fmt) use self::imp::*; env_logger-0.10.0/src/fmt/writer/termcolor/shim_impl.rs000064400000000000000000000037351046102023000212670ustar 00000000000000use std::{io, sync::Mutex}; use crate::fmt::{WritableTarget, WriteStyle}; pub(in crate::fmt::writer) mod glob {} pub(in crate::fmt::writer) struct BufferWriter { target: WritableTarget, } pub(in crate::fmt) struct Buffer(Vec); impl BufferWriter { pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { target: WritableTarget::Stderr, } } pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self { BufferWriter { target: WritableTarget::Stdout, } } pub(in crate::fmt::writer) fn pipe( _write_style: WriteStyle, pipe: Box>, ) -> Self { BufferWriter { target: WritableTarget::Pipe(pipe), } } pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { Buffer(Vec::new()) } 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`. 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(()) } } impl Buffer { pub(in crate::fmt) fn clear(&mut self) { self.0.clear(); } pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { self.0.extend(buf); Ok(buf.len()) } pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { Ok(()) } #[cfg(test)] pub(in crate::fmt) fn bytes(&self) -> &[u8] { &self.0 } } env_logger-0.10.0/src/lib.rs000064400000000000000000001224031046102023000137360ustar 00000000000000// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! 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 //! //! ``` //! use log::{debug, error, log_enabled, info, Level}; //! //! env_logger::init(); //! //! debug!("this is a debug {}", "message"); //! error!("this is printed by default"); //! //! if log_enabled!(Level::Info) { //! let x = 3 * 4; // expensive computation //! info!("the answer was: {}", x); //! } //! ``` //! //! Assumes the binary is `main`: //! //! ```{.bash} //! $ RUST_LOG=error ./main //! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! ``` //! //! ```{.bash} //! $ RUST_LOG=info ./main //! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! ```{.bash} //! $ RUST_LOG=debug ./main //! [2017-11-09T02:12:24Z DEBUG main] this is a debug message //! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! You can also set the log level on a per module basis: //! //! ```{.bash} //! $ RUST_LOG=main=info ./main //! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! And enable all logging: //! //! ```{.bash} //! $ RUST_LOG=main ./main //! [2017-11-09T02:12:24Z DEBUG main] this is a debug message //! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! [2017-11-09T02:12:24Z INFO main] the answer was: 12 //! ``` //! //! If the binary name contains hyphens, you will need to replace //! them with underscores: //! //! ```{.bash} //! $ RUST_LOG=my_app ./my-app //! [2017-11-09T02:12:24Z DEBUG my_app] this is a debug message //! [2017-11-09T02:12:24Z ERROR my_app] this is printed by default //! [2017-11-09T02:12:24Z INFO my_app] the answer was: 12 //! ``` //! //! This is because Rust modules and crates cannot contain hyphens //! in their name, although `cargo` continues to accept them. //! //! See the documentation for the [`log` crate][log-crate-url] for more //! information about its API. //! //! ## Enabling logging //! //! 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 //! example::log::target=level //! ``` //! //! The log target is typically equal to the path of the module the message //! in question originated from, though it can be overridden. //! //! 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 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 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.** //! //! 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 //! //! A `RUST_LOG` directive may include a regex filter. The syntax is to append `/` //! followed by a regex. Each message is checked against the regex, and is only //! logged if it matches. Note that the matching is done after formatting the //! log string but before adding any logging meta-data. There is a single filter //! for all modules. //! //! Some examples: //! //! * `hello/foo` turns on all logging for the 'hello' module where the log //! message includes 'foo'. //! * `info/f.o` turns on all info logging where the log message includes 'foo', //! 'f1o', 'fao', etc. //! * `hello=debug/foo*foo` turns on debug logging for 'hello' where the log //! message includes 'foofoo' or 'fofoo' or 'fooooooofoo', etc. //! * `error,hello=warn/[0-9]scopes` turn on global error logging and also //! warn for hello. In both cases the log message must include a single digit //! number followed by 'scopes'. //! //! ## Capturing logs in tests //! //! Records logged during `cargo test` will not be captured by the test harness by default. //! The [`Builder::is_test`] method can be used in unit tests to ensure logs will be captured: //! //! ``` //! # #[macro_use] extern crate log; //! #[cfg(test)] //! mod tests { //! fn init() { //! let _ = env_logger::builder().is_test(true).try_init(); //! } //! //! #[test] //! fn it_works() { //! init(); //! //! info!("This record will be captured by `cargo test`"); //! //! assert_eq!(2, 1 + 1); //! } //! } //! ``` //! //! Enabling test capturing comes at the expense of color and other style support //! and may have performance implications. //! //! ## Disabling colors //! //! Colors and other styles can be configured with the `RUST_LOG_STYLE` //! environment variable. It accepts the following values: //! //! * `auto` (default) will attempt to print style characters, but don't force the issue. //! If the console isn't available on Windows, or if TERM=dumb, for example, then don't print colors. //! * `always` will always print style characters even if they aren't supported by the terminal. //! This includes emitting ANSI colors on Windows if the console API is unavailable. //! * `never` will never print style characters. //! //! ## Tweaking the default format //! //! Parts of the default format can be excluded from the log output using the [`Builder`]. //! The following example excludes the timestamp from the log output: //! //! ``` //! env_logger::builder() //! .format_timestamp(None) //! .init(); //! ``` //! //! ### Stability of the default format //! //! The default format won't optimise for long-term stability, and explicitly makes no //! guarantees about the stability of its output across major, minor or patch version //! bumps during `0.x`. //! //! If you want to capture or interpret the output of `env_logger` programmatically //! then you should use a custom format. //! //! ### Using a custom format //! //! Custom formats can be provided as closures to the [`Builder`]. //! These closures take a [`Formatter`] and `log::Record` as arguments: //! //! ``` //! use std::io::Write; //! //! env_logger::builder() //! .format(|buf, record| { //! writeln!(buf, "{}: {}", record.level(), record.args()) //! }) //! .init(); //! ``` //! //! See the [`fmt`] module for more details about custom formats. //! //! ## Specifying defaults for environment variables //! //! `env_logger` can read configuration from environment variables. //! If these variables aren't present, the default value to use can be tweaked with the [`Env`] type. //! The following example defaults to log `warn` and above if the `RUST_LOG` environment variable //! isn't set: //! //! ``` //! use env_logger::Env; //! //! env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init(); //! ``` //! //! [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 //! [`Builder::is_test`]: struct.Builder.html#method.is_test //! [`Env`]: struct.Env.html //! [`fmt`]: fmt/index.html #![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" )] // 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)] use std::{borrow::Cow, cell::RefCell, env, io}; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; pub mod filter; pub mod fmt; pub use self::fmt::glob::*; use self::filter::Filter; use self::fmt::writer::{self, Writer}; use self::fmt::{FormatFn, Formatter}; /// The default name for the environment variable to read filters from. pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG"; /// The default name for the environment variable to read style preferences from. pub const DEFAULT_WRITE_STYLE_ENV: &str = "RUST_LOG_STYLE"; /// Set of environment variables to configure from. /// /// # Default environment variables /// /// By default, the `Env` will read the following environment variables: /// /// - `RUST_LOG`: the level filter /// - `RUST_LOG_STYLE`: whether or not to print styles with records. /// /// These sources can be configured using the builder methods on `Env`. #[derive(Debug)] pub struct Env<'a> { filter: Var<'a>, write_style: Var<'a>, } #[derive(Debug)] struct Var<'a> { name: Cow<'a, str>, default: Option>, } /// The env logger. /// /// This struct implements the `Log` trait from the [`log` crate][log-crate-url], /// which allows it to act as a logger. /// /// The [`init()`], [`try_init()`], [`Builder::init()`] and [`Builder::try_init()`] /// methods will each construct a `Logger` and immediately initialize it as the /// default global logger. /// /// If you'd instead need access to the constructed `Logger`, you can use /// the associated [`Builder`] and install it with the /// [`log` crate][log-crate-url] directly. /// /// [log-crate-url]: https://docs.rs/log/ /// [`init()`]: fn.init.html /// [`try_init()`]: fn.try_init.html /// [`Builder::init()`]: struct.Builder.html#method.init /// [`Builder::try_init()`]: struct.Builder.html#method.try_init /// [`Builder`]: struct.Builder.html pub struct Logger { writer: Writer, filter: Filter, format: FormatFn, } /// `Builder` acts as builder for initializing a `Logger`. /// /// It can be used to customize the log format, change the environment variable used /// to provide the logging directives and also set the default log level filter. /// /// # Examples /// /// ``` /// # #[macro_use] extern crate log; /// # use std::io::Write; /// use env_logger::Builder; /// use log::LevelFilter; /// /// let mut builder = Builder::from_default_env(); /// /// builder /// .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args())) /// .filter(None, LevelFilter::Info) /// .init(); /// /// error!("error message"); /// info!("info message"); /// ``` #[derive(Default)] pub struct Builder { filter: filter::Builder, writer: writer::Builder, format: fmt::Builder, built: bool, } impl Builder { /// Initializes the log builder with defaults. /// /// **NOTE:** This method won't read from any environment variables. /// Use the [`filter`] and [`write_style`] methods to configure the builder /// or use [`from_env`] or [`from_default_env`] instead. /// /// # Examples /// /// Create a new builder and configure filters and style: /// /// ``` /// use log::LevelFilter; /// use env_logger::{Builder, WriteStyle}; /// /// let mut builder = Builder::new(); /// /// builder /// .filter(None, LevelFilter::Info) /// .write_style(WriteStyle::Always) /// .init(); /// ``` /// /// [`filter`]: #method.filter /// [`write_style`]: #method.write_style /// [`from_env`]: #method.from_env /// [`from_default_env`]: #method.from_default_env pub fn new() -> Builder { Default::default() } /// Initializes the log builder from the environment. /// /// The variables used to read configuration from can be tweaked before /// passing in. /// /// # Examples /// /// Initialise a logger reading the log filter from an environment variable /// called `MY_LOG`: /// /// ``` /// use env_logger::Builder; /// /// let mut builder = Builder::from_env("MY_LOG"); /// builder.init(); /// ``` /// /// Initialise a logger using the `MY_LOG` variable for filtering and /// `MY_LOG_STYLE` for whether or not to write styles: /// /// ``` /// use env_logger::{Builder, Env}; /// /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); /// /// let mut builder = Builder::from_env(env); /// builder.init(); /// ``` pub fn from_env<'a, E>(env: E) -> Self where E: Into>, { let mut builder = Builder::new(); builder.parse_env(env); builder } /// Applies the configuration from the environment. /// /// This function allows a builder to be configured with default parameters, /// to be then overridden by the environment. /// /// # Examples /// /// Initialise a logger with filter level `Off`, then override the log /// filter from an environment variable called `MY_LOG`: /// /// ``` /// use log::LevelFilter; /// use env_logger::Builder; /// /// let mut builder = Builder::new(); /// /// builder.filter_level(LevelFilter::Off); /// builder.parse_env("MY_LOG"); /// builder.init(); /// ``` /// /// Initialise a logger with filter level `Off`, then use the `MY_LOG` /// variable to override filtering and `MY_LOG_STYLE` to override whether /// or not to write styles: /// /// ``` /// use log::LevelFilter; /// use env_logger::{Builder, Env}; /// /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); /// /// let mut builder = Builder::new(); /// builder.filter_level(LevelFilter::Off); /// builder.parse_env(env); /// builder.init(); /// ``` pub fn parse_env<'a, E>(&mut self, env: E) -> &mut Self where E: Into>, { let env = env.into(); if let Some(s) = env.get_filter() { self.parse_filters(&s); } if let Some(s) = env.get_write_style() { self.parse_write_style(&s); } self } /// Initializes the log builder from the environment using default variable names. /// /// This method is a convenient way to call `from_env(Env::default())` without /// having to use the `Env` type explicitly. The builder will use the /// [default environment variables]. /// /// # Examples /// /// Initialise a logger using the default environment variables: /// /// ``` /// use env_logger::Builder; /// /// let mut builder = Builder::from_default_env(); /// builder.init(); /// ``` /// /// [default environment variables]: struct.Env.html#default-environment-variables pub fn from_default_env() -> Self { Self::from_env(Env::default()) } /// Applies the configuration from the environment using default variable names. /// /// This method is a convenient way to call `parse_env(Env::default())` without /// having to use the `Env` type explicitly. The builder will use the /// [default environment variables]. /// /// # Examples /// /// Initialise a logger with filter level `Off`, then configure it using the /// default environment variables: /// /// ``` /// use log::LevelFilter; /// use env_logger::Builder; /// /// let mut builder = Builder::new(); /// builder.filter_level(LevelFilter::Off); /// builder.parse_default_env(); /// builder.init(); /// ``` /// /// [default environment variables]: struct.Env.html#default-environment-variables pub fn parse_default_env(&mut self) -> &mut Self { self.parse_env(Env::default()) } /// Sets the format function for formatting the log output. /// /// This function is called on each record logged and should format the /// log record and output it to the given [`Formatter`]. /// /// The format function is expected to output the string directly to the /// `Formatter` so that implementations can use the [`std::fmt`] macros /// to format and output without intermediate heap allocations. The default /// `env_logger` formatter takes advantage of this. /// /// # Examples /// /// Use a custom format to write only the log message: /// /// ``` /// use std::io::Write; /// use env_logger::Builder; /// /// let mut builder = Builder::new(); /// /// builder.format(|buf, record| writeln!(buf, "{}", record.args())); /// ``` /// /// [`Formatter`]: fmt/struct.Formatter.html /// [`String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html pub fn format(&mut self, format: F) -> &mut Self where F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send, { self.format.custom_format = Some(Box::new(format)); self } /// Use the default format. /// /// This method will clear any custom format set on the builder. pub fn default_format(&mut self) -> &mut Self { self.format = Default::default(); self } /// Whether or not to write the level in the default format. pub fn format_level(&mut self, write: bool) -> &mut Self { self.format.format_level = write; self } /// Whether or not to write the module path in the default format. pub fn format_module_path(&mut self, write: bool) -> &mut Self { self.format.format_module_path = write; 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) -> &mut Self { self.format.format_indent = indent; self } /// Configures if timestamp should be included and in what precision. pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { self.format.format_timestamp = timestamp; self } /// Configures the timestamp to use second precision. pub fn format_timestamp_secs(&mut self) -> &mut Self { self.format_timestamp(Some(fmt::TimestampPrecision::Seconds)) } /// Configures the timestamp to use millisecond precision. pub fn format_timestamp_millis(&mut self) -> &mut Self { self.format_timestamp(Some(fmt::TimestampPrecision::Millis)) } /// Configures the timestamp to use microsecond precision. pub fn format_timestamp_micros(&mut self) -> &mut Self { self.format_timestamp(Some(fmt::TimestampPrecision::Micros)) } /// Configures the timestamp to use nanosecond precision. pub fn format_timestamp_nanos(&mut self) -> &mut Self { self.format_timestamp(Some(fmt::TimestampPrecision::Nanos)) } /// 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 /// /// Only include messages for info and above for logs in `path::to::module`: /// /// ``` /// use env_logger::Builder; /// use log::LevelFilter; /// /// let mut builder = Builder::new(); /// /// builder.filter_module("path::to::module", LevelFilter::Info); /// ``` pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self { self.filter.filter_module(module, level); self } /// Adds a directive to the filter for all modules. /// /// # Examples /// /// Only include messages for info and above for logs globally: /// /// ``` /// use env_logger::Builder; /// use log::LevelFilter; /// /// let mut builder = Builder::new(); /// /// builder.filter_level(LevelFilter::Info); /// ``` pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self { self.filter.filter_level(level); self } /// Adds filters to the logger. /// /// The given module (if any) will log at most the specified level provided. /// If no module is provided then the filter will apply to all log messages. /// /// # Examples /// /// Only include messages for info and above for logs in `path::to::module`: /// /// ``` /// use env_logger::Builder; /// use log::LevelFilter; /// /// let mut builder = Builder::new(); /// /// builder.filter(Some("path::to::module"), LevelFilter::Info); /// ``` pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self { self.filter.filter(module, level); self } /// Parses the directives string in the same form as the `RUST_LOG` /// environment variable. /// /// See the module documentation for more details. pub fn parse_filters(&mut self, filters: &str) -> &mut Self { self.filter.parse(filters); self } /// Sets the target for the log output. /// /// 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 /// /// Write log message to `stdout`: /// /// ``` /// use env_logger::{Builder, Target}; /// /// let mut builder = Builder::new(); /// /// builder.target(Target::Stdout); /// ``` pub fn target(&mut self, target: fmt::Target) -> &mut Self { self.writer.target(target); self } /// Sets whether or not styles will be written. /// /// This can be useful in environments that don't support control characters /// for setting colors. /// /// # Examples /// /// Never attempt to write styles: /// /// ``` /// use env_logger::{Builder, WriteStyle}; /// /// let mut builder = Builder::new(); /// /// builder.write_style(WriteStyle::Never); /// ``` pub fn write_style(&mut self, write_style: fmt::WriteStyle) -> &mut Self { self.writer.write_style(write_style); self } /// Parses whether or not to write styles in the same form as the `RUST_LOG_STYLE` /// environment variable. /// /// See the module documentation for more details. pub fn parse_write_style(&mut self, write_style: &str) -> &mut Self { self.writer.parse_write_style(write_style); self } /// Sets whether or not the logger will be used in unit tests. /// /// If `is_test` is `true` then the logger will allow the testing framework to /// capture log records rather than printing them to the terminal directly. pub fn is_test(&mut self, is_test: bool) -> &mut Self { self.writer.is_test(is_test); self } /// Initializes the global logger with the built env logger. /// /// This should be called early in the execution of a Rust program. Any log /// events that occur before initialization will be ignored. /// /// # Errors /// /// This function will fail if it is called more than once, or if another /// library has already initialized a global logger. pub fn try_init(&mut self) -> Result<(), SetLoggerError> { let logger = self.build(); let max_level = logger.filter(); let r = log::set_boxed_logger(Box::new(logger)); if r.is_ok() { log::set_max_level(max_level); } r } /// Initializes the global logger with the built env logger. /// /// This should be called early in the execution of a Rust program. Any log /// events that occur before initialization will be ignored. /// /// # Panics /// /// This function will panic if it is called more than once, or if another /// library has already initialized a global logger. pub fn init(&mut self) { self.try_init() .expect("Builder::init should not be called after logger initialized"); } /// Build an env logger. /// /// The returned logger implements the `Log` trait and can be installed manually /// or nested within another logger. pub fn build(&mut self) -> Logger { assert!(!self.built, "attempt to re-use consumed builder"); self.built = true; Logger { writer: self.writer.build(), filter: self.filter.build(), format: self.format.build(), } } } impl Logger { /// Creates the logger from the environment. /// /// The variables used to read configuration from can be tweaked before /// passing in. /// /// # Examples /// /// Create a logger reading the log filter from an environment variable /// called `MY_LOG`: /// /// ``` /// use env_logger::Logger; /// /// let logger = Logger::from_env("MY_LOG"); /// ``` /// /// Create a logger using the `MY_LOG` variable for filtering and /// `MY_LOG_STYLE` for whether or not to write styles: /// /// ``` /// use env_logger::{Logger, Env}; /// /// let env = Env::new().filter_or("MY_LOG", "info").write_style_or("MY_LOG_STYLE", "always"); /// /// let logger = Logger::from_env(env); /// ``` pub fn from_env<'a, E>(env: E) -> Self where E: Into>, { Builder::from_env(env).build() } /// Creates the logger from the environment using default variable names. /// /// This method is a convenient way to call `from_env(Env::default())` without /// having to use the `Env` type explicitly. The logger will use the /// [default environment variables]. /// /// # Examples /// /// Creates a logger using the default environment variables: /// /// ``` /// use env_logger::Logger; /// /// let logger = Logger::from_default_env(); /// ``` /// /// [default environment variables]: struct.Env.html#default-environment-variables pub fn from_default_env() -> Self { Builder::from_default_env().build() } /// Returns the maximum `LevelFilter` that this env logger instance is /// configured to output. pub fn filter(&self) -> LevelFilter { self.filter.filter() } /// Checks if this record matches the configured filter. pub fn matches(&self, record: &Record) -> bool { self.filter.matches(record) } } impl Log for Logger { fn enabled(&self, metadata: &Metadata) -> bool { self.filter.enabled(metadata) } fn log(&self, record: &Record) { if self.matches(record) { // Log records are written to a thread-local buffer before being printed // to the terminal. We clear these buffers afterwards, but they aren't shrunk // so will always at least have capacity for the largest log record formatted // on that thread. // // If multiple `Logger`s are used by the same threads then the thread-local // formatter might have different color support. If this is the case the // formatter and its buffer are discarded and recreated. thread_local! { static FORMATTER: RefCell> = RefCell::new(None); } let print = |formatter: &mut Formatter, record: &Record| { let _ = (self.format)(formatter, record).and_then(|_| formatter.print(&self.writer)); // Always clear the buffer afterwards formatter.clear(); }; let printed = FORMATTER .try_with(|tl_buf| { match tl_buf.try_borrow_mut() { // There are no active borrows of the buffer Ok(mut tl_buf) => match *tl_buf { // We have a previously set formatter Some(ref mut formatter) => { // Check the buffer style. If it's different from the logger's // style then drop the buffer and recreate it. if formatter.write_style() != self.writer.write_style() { *formatter = Formatter::new(&self.writer); } print(formatter, record); } // We don't have a previously set formatter None => { let mut formatter = Formatter::new(&self.writer); print(&mut formatter, record); *tl_buf = Some(formatter); } }, // There's already an active borrow of the buffer (due to re-entrancy) Err(_) => { print(&mut Formatter::new(&self.writer), record); } } }) .is_ok(); if !printed { // The thread-local storage was not available (because its // destructor has already run). Create a new single-use // Formatter on the stack for this call. print(&mut Formatter::new(&self.writer), record); } } } fn flush(&self) {} } impl<'a> Env<'a> { /// Get a default set of environment variables. pub fn new() -> Self { Self::default() } /// Specify an environment variable to read the filter from. pub fn filter(mut self, filter_env: E) -> Self where E: Into>, { self.filter = Var::new(filter_env); self } /// Specify an environment variable to read the filter from. /// /// If the variable is not set, the default value will be used. pub fn filter_or(mut self, filter_env: E, default: V) -> Self where E: Into>, V: Into>, { self.filter = Var::new_with_default(filter_env, default); self } /// Use the default environment variable to read the filter from. /// /// If the variable is not set, the default value will be used. pub fn default_filter_or(mut self, default: V) -> Self where V: Into>, { self.filter = Var::new_with_default(DEFAULT_FILTER_ENV, default); self } fn get_filter(&self) -> Option { self.filter.get() } /// Specify an environment variable to read the style from. pub fn write_style(mut self, write_style_env: E) -> Self where E: Into>, { self.write_style = Var::new(write_style_env); self } /// Specify an environment variable to read the style from. /// /// If the variable is not set, the default value will be used. pub fn write_style_or(mut self, write_style_env: E, default: V) -> Self where E: Into>, V: Into>, { self.write_style = Var::new_with_default(write_style_env, default); self } /// Use the default environment variable to read the style from. /// /// If the variable is not set, the default value will be used. pub fn default_write_style_or(mut self, default: V) -> Self where V: Into>, { self.write_style = Var::new_with_default(DEFAULT_WRITE_STYLE_ENV, default); self } fn get_write_style(&self) -> Option { self.write_style.get() } } impl<'a> Var<'a> { fn new(name: E) -> Self where E: Into>, { Var { name: name.into(), default: None, } } fn new_with_default(name: E, default: V) -> Self where E: Into>, V: Into>, { Var { name: name.into(), default: Some(default.into()), } } fn get(&self) -> Option { env::var(&*self.name) .ok() .or_else(|| self.default.to_owned().map(|v| v.into_owned())) } } impl<'a, T> From for Env<'a> where T: Into>, { fn from(filter_env: T) -> Self { Env::default().filter(filter_env.into()) } } impl<'a> Default for Env<'a> { fn default() -> Self { Env { filter: Var::new(DEFAULT_FILTER_ENV), write_style: Var::new(DEFAULT_WRITE_STYLE_ENV), } } } mod std_fmt_impls { use super::*; use std::fmt; impl fmt::Debug for Logger { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Logger") .field("filter", &self.filter) .finish() } } impl fmt::Debug for Builder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.built { f.debug_struct("Logger").field("built", &true).finish() } else { f.debug_struct("Logger") .field("filter", &self.filter) .field("writer", &self.writer) .finish() } } } } /// Attempts to initialize the global logger with an env logger. /// /// This should be called early in the execution of a Rust program. Any log /// events that occur before initialization will be ignored. /// /// # Errors /// /// This function will fail if it is called more than once, or if another /// library has already initialized a global logger. pub fn try_init() -> Result<(), SetLoggerError> { try_init_from_env(Env::default()) } /// Initializes the global logger with an env logger. /// /// This should be called early in the execution of a Rust program. Any log /// events that occur before initialization will be ignored. /// /// # Panics /// /// This function will panic if it is called more than once, or if another /// library has already initialized a global logger. pub fn init() { try_init().expect("env_logger::init should not be called after logger initialized"); } /// Attempts to initialize the global logger with an env logger from the given /// environment variables. /// /// This should be called early in the execution of a Rust program. Any log /// events that occur before initialization will be ignored. /// /// # Examples /// /// Initialise a logger using the `MY_LOG` environment variable for filters /// and `MY_LOG_STYLE` for writing colors: /// /// ``` /// use env_logger::{Builder, Env}; /// /// # fn run() -> Result<(), Box> { /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); /// /// env_logger::try_init_from_env(env)?; /// /// Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// # Errors /// /// This function will fail if it is called more than once, or if another /// library has already initialized a global logger. pub fn try_init_from_env<'a, E>(env: E) -> Result<(), SetLoggerError> where E: Into>, { let mut builder = Builder::from_env(env); builder.try_init() } /// Initializes the global logger with an env logger from the given environment /// variables. /// /// This should be called early in the execution of a Rust program. Any log /// events that occur before initialization will be ignored. /// /// # Examples /// /// Initialise a logger using the `MY_LOG` environment variable for filters /// and `MY_LOG_STYLE` for writing colors: /// /// ``` /// use env_logger::{Builder, Env}; /// /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE"); /// /// env_logger::init_from_env(env); /// ``` /// /// # Panics /// /// This function will panic if it is called more than once, or if another /// library has already initialized a global logger. pub fn init_from_env<'a, E>(env: E) where E: Into>, { try_init_from_env(env) .expect("env_logger::init_from_env should not be called after logger initialized"); } /// Create a new builder with the default environment variables. /// /// The builder can be configured before being initialized. /// This is a convenient way of calling [`Builder::from_default_env`]. /// /// [`Builder::from_default_env`]: struct.Builder.html#method.from_default_env pub fn builder() -> Builder { Builder::from_default_env() } /// Create a builder from the given environment variables. /// /// The builder can be configured before being initialized. #[deprecated( since = "0.8.0", note = "Prefer `env_logger::Builder::from_env()` instead." )] pub fn from_env<'a, E>(env: E) -> Builder where E: Into>, { Builder::from_env(env) } #[cfg(test)] mod tests { use super::*; #[test] fn env_get_filter_reads_from_var_if_set() { env::set_var("env_get_filter_reads_from_var_if_set", "from var"); let env = Env::new().filter_or("env_get_filter_reads_from_var_if_set", "from default"); assert_eq!(Some("from var".to_owned()), env.get_filter()); } #[test] fn env_get_filter_reads_from_default_if_var_not_set() { env::remove_var("env_get_filter_reads_from_default_if_var_not_set"); let env = Env::new().filter_or( "env_get_filter_reads_from_default_if_var_not_set", "from default", ); assert_eq!(Some("from default".to_owned()), env.get_filter()); } #[test] fn env_get_write_style_reads_from_var_if_set() { env::set_var("env_get_write_style_reads_from_var_if_set", "from var"); let env = Env::new().write_style_or("env_get_write_style_reads_from_var_if_set", "from default"); assert_eq!(Some("from var".to_owned()), env.get_write_style()); } #[test] fn env_get_write_style_reads_from_default_if_var_not_set() { env::remove_var("env_get_write_style_reads_from_default_if_var_not_set"); let env = Env::new().write_style_or( "env_get_write_style_reads_from_default_if_var_not_set", "from default", ); 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); } } env_logger-0.10.0/tests/init-twice-retains-filter.rs000064400000000000000000000022101046102023000205360ustar 00000000000000extern crate env_logger; extern crate log; use std::env; use std::process; use std::str; fn main() { if env::var("YOU_ARE_TESTING_NOW").is_ok() { // Init from the env (which should set the max level to `Debug`) env_logger::init(); assert_eq!(log::LevelFilter::Debug, log::max_level()); // Init again using a different max level // This shouldn't clobber the level that was previously set env_logger::Builder::new() .parse_filters("info") .try_init() .unwrap_err(); assert_eq!(log::LevelFilter::Debug, log::max_level()); return; } let exe = env::current_exe().unwrap(); let out = process::Command::new(exe) .env("YOU_ARE_TESTING_NOW", "1") .env("RUST_LOG", "debug") .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); if out.status.success() { return; } println!("test failed: {}", out.status); println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap()); println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap()); process::exit(1); } env_logger-0.10.0/tests/log-in-log.rs000064400000000000000000000016261046102023000155120ustar 00000000000000#[macro_use] extern crate log; extern crate env_logger; use std::env; use std::fmt; use std::process; use std::str; struct Foo; impl fmt::Display for Foo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { info!("test"); f.write_str("bar") } } fn main() { env_logger::init(); if env::var("YOU_ARE_TESTING_NOW").is_ok() { return info!("{}", Foo); } let exe = env::current_exe().unwrap(); let out = process::Command::new(exe) .env("YOU_ARE_TESTING_NOW", "1") .env("RUST_LOG", "debug") .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); if out.status.success() { return; } println!("test failed: {}", out.status); println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap()); println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap()); process::exit(1); } env_logger-0.10.0/tests/log_tls_dtors.rs000064400000000000000000000040251046102023000164200ustar 00000000000000#[macro_use] extern crate log; extern crate env_logger; use std::env; use std::process; use std::str; use std::thread; struct DropMe; impl Drop for DropMe { fn drop(&mut self) { debug!("Dropping now"); } } fn run() { // Use multiple thread local values to increase the chance that our TLS // value will get destroyed after the FORMATTER key in the library thread_local! { static DROP_ME_0: DropMe = DropMe; static DROP_ME_1: DropMe = DropMe; static DROP_ME_2: DropMe = DropMe; static DROP_ME_3: DropMe = DropMe; static DROP_ME_4: DropMe = DropMe; static DROP_ME_5: DropMe = DropMe; static DROP_ME_6: DropMe = DropMe; static DROP_ME_7: DropMe = DropMe; static DROP_ME_8: DropMe = DropMe; static DROP_ME_9: DropMe = DropMe; } DROP_ME_0.with(|_| {}); DROP_ME_1.with(|_| {}); DROP_ME_2.with(|_| {}); DROP_ME_3.with(|_| {}); DROP_ME_4.with(|_| {}); DROP_ME_5.with(|_| {}); DROP_ME_6.with(|_| {}); DROP_ME_7.with(|_| {}); DROP_ME_8.with(|_| {}); DROP_ME_9.with(|_| {}); } fn main() { env_logger::init(); if env::var("YOU_ARE_TESTING_NOW").is_ok() { // Run on a separate thread because TLS values on the main thread // won't have their destructors run if pthread is used. // https://doc.rust-lang.org/std/thread/struct.LocalKey.html#platform-specific-behavior thread::spawn(run).join().unwrap(); } else { let exe = env::current_exe().unwrap(); let out = process::Command::new(exe) .env("YOU_ARE_TESTING_NOW", "1") .env("RUST_LOG", "debug") .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); if !out.status.success() { println!("test failed: {}", out.status); println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap()); println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap()); process::exit(1); } } } env_logger-0.10.0/tests/regexp_filter.rs000064400000000000000000000025061046102023000164030ustar 00000000000000#[macro_use] extern crate log; extern crate env_logger; use std::env; use std::process; use std::str; fn main() { if env::var("LOG_REGEXP_TEST").ok() == Some(String::from("1")) { child_main(); } else { parent_main() } } fn child_main() { env_logger::init(); info!("XYZ Message"); } fn run_child(rust_log: String) -> bool { let exe = env::current_exe().unwrap(); let out = process::Command::new(exe) .env("LOG_REGEXP_TEST", "1") .env("RUST_LOG", rust_log) .output() .unwrap_or_else(|e| panic!("Unable to start child process: {}", e)); str::from_utf8(out.stderr.as_ref()) .unwrap() .contains("XYZ Message") } fn assert_message_printed(rust_log: &str) { if !run_child(rust_log.to_string()) { panic!("RUST_LOG={} should allow the test log message", rust_log) } } fn assert_message_not_printed(rust_log: &str) { if run_child(rust_log.to_string()) { panic!( "RUST_LOG={} should not allow the test log message", rust_log ) } } fn parent_main() { // test normal log severity levels assert_message_printed("info"); assert_message_not_printed("warn"); // test of regular expression filters assert_message_printed("info/XYZ"); assert_message_not_printed("info/XXX"); }