env_logger-0.11.6/.cargo_vcs_info.json0000644000000001360000000000100132500ustar { "git": { "sha1": "dc1a01a79729d9a43f9dfaf32080c5e7bdf05090" }, "path_in_vcs": "" }env_logger-0.11.6/Cargo.lock0000644000000123640000000000100112310ustar # 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 = "anstream" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "env_filter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.6" dependencies = [ "anstream", "anstyle", "env_filter", "humantime", "log", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[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 = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 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.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" env_logger-0.11.6/Cargo.toml0000644000000126210000000000100112500ustar # 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.71" name = "env_logger" version = "0.11.6" build = false include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*", "tests/**/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", "--generate-link-to-definition", ] [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{version}}" search = "Unreleased" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = "...{{tag_name}}" search = '\.\.\.HEAD' [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{date}}" search = "ReleaseDate" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ ## [Unreleased] - ReleaseDate """ search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ [Unreleased]: https://github.com/rust-cli/env_logger/compare/{{tag_name}}...HEAD""" search = "" [features] auto-color = [ "color", "anstream/auto", ] color = [ "dep:anstream", "dep:anstyle", ] default = [ "auto-color", "humantime", "regex", ] humantime = ["dep:humantime"] regex = ["env_filter/regex"] unstable-kv = ["log/kv"] [lib] name = "env_logger" path = "src/lib.rs" [[example]] name = "custom_default_format" path = "examples/custom_default_format.rs" [[example]] name = "custom_format" path = "examples/custom_format.rs" [[example]] name = "default" path = "examples/default.rs" [[example]] name = "direct_logger" path = "examples/direct_logger.rs" [[example]] name = "filters_from_code" path = "examples/filters_from_code.rs" [[example]] name = "in_tests" path = "examples/in_tests.rs" [[example]] name = "syslog_friendly_format" path = "examples/syslog_friendly_format.rs" [[test]] name = "init-twice-retains-filter" path = "tests/init-twice-retains-filter.rs" harness = false [[test]] name = "log-in-log" path = "tests/log-in-log.rs" harness = false [[test]] name = "log_tls_dtors" path = "tests/log_tls_dtors.rs" harness = false [[test]] name = "regexp_filter" path = "tests/regexp_filter.rs" harness = false [dependencies.anstream] version = "0.6.11" features = ["wincon"] optional = true default-features = false [dependencies.anstyle] version = "1.0.6" optional = true [dependencies.env_filter] version = "0.1.0" default-features = false [dependencies.humantime] version = "2.0.0" optional = true [dependencies.log] version = "0.4.21" features = ["std"] [lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" result_large_err = "allow" same_functions_in_if_condition = "warn" self_named_module_files = "warn" semicolon_if_nothing_returned = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" uninlined_format_args = "warn" verbose_file_reads = "warn" wildcard_imports = "warn" zero_sized_map_values = "warn" [lints.rust] unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" [lints.rust.rust_2018_idioms] level = "warn" priority = -1 env_logger-0.11.6/Cargo.toml.orig000064400000000000000000000101771046102023000147350ustar 00000000000000[workspace] resolver = "2" members = ["crates/*"] [workspace.package] repository = "https://github.com/rust-cli/env_logger" license = "MIT OR Apache-2.0" edition = "2021" rust-version = "1.71" # MSRV include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*", "tests/**/*", ] [workspace.lints.rust] rust_2018_idioms = { level = "warn", priority = -1 } unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" [workspace.lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" # sometimes good to name what you are returning linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" result_large_err = "allow" same_functions_in_if_condition = "warn" self_named_module_files = "warn" semicolon_if_nothing_returned = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" uninlined_format_args = "warn" verbose_file_reads = "warn" wildcard_imports = "warn" zero_sized_map_values = "warn" [package] name = "env_logger" version = "0.11.6" description = """ A logging implementation for `log` which is configured via an environment variable. """ categories = ["development-tools::debugging"] keywords = ["logging", "log", "logger"] repository.workspace = true license.workspace = true edition.workspace = true rust-version.workspace = true include.workspace = true [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [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:anstream", "dep:anstyle"] auto-color = ["color", "anstream/auto"] humantime = ["dep:humantime"] regex = ["env_filter/regex"] unstable-kv = ["log/kv"] [dependencies] log = { version = "0.4.21", features = ["std"] } env_filter = { version = "0.1.0", path = "crates/env_filter", default-features = false } humantime = { version = "2.0.0", optional = true } anstream = { version = "0.6.11", default-features = false, features = ["wincon"], optional = true } anstyle = { version = "1.0.6", 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 [lints] workspace = true env_logger-0.11.6/LICENSE-APACHE000064400000000000000000000261361046102023000137740ustar 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.11.6/LICENSE-MIT000064400000000000000000000020461046102023000134760ustar 00000000000000Copyright (c) Individual contributors Permission 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.11.6/README.md000064400000000000000000000115011046102023000133150ustar 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) 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: ```console $ cargo add log env_logger ``` `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 use log::info; 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/rust-cli/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: ```console $ cargo add log $ cargo add --dev env_logger ``` ```rust fn add_one(num: i32) -> i32 { info!("add_one called with {}", num); num + 1 } #[cfg(test)] mod tests { use super::*; use log::info; 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.11.6/examples/custom_default_format.rs000064400000000000000000000014241046102023000206130ustar 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. */ use log::info; 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.11.6/examples/custom_format.rs000064400000000000000000000025711046102023000171130ustar 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::{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| { // We are reusing `anstyle` but there are `anstyle-*` crates to adapt it to your // preferred styling crate. let warn_style = buf.default_level_style(log::Level::Warn); let timestamp = buf.timestamp(); writeln!( buf, "My formatted log ({timestamp}): {warn_style}{}{warn_style:#}", record.args() ) }) .init(); } init_logger(); log::info!("a log from `MyLogger`"); } #[cfg(not(all(feature = "color", feature = "humantime")))] fn main() {} env_logger-0.11.6/examples/default.rs000064400000000000000000000015451046102023000156550ustar 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 ``` */ use log::{debug, error, info, trace, warn}; 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.11.6/examples/direct_logger.rs000064400000000000000000000022511046102023000170350ustar 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}; #[cfg(feature = "unstable-kv")] static KVS: (&str, &str) = ("test", "something"); fn record() -> Record<'static> { let error_metadata = MetadataBuilder::new() .target("myApp") .level(Level::Error) .build(); let mut builder = Record::builder(); builder .metadata(error_metadata) .args(format_args!("Error!")) .line(Some(433)) .file(Some("app.rs")) .module_path(Some("server")); #[cfg(feature = "unstable-kv")] { builder.key_values(&KVS); } builder.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.11.6/examples/filters_from_code.rs000064400000000000000000000006151046102023000177130ustar 00000000000000/*! Specify logging filters in code instead of using an environment variable. */ use env_logger::Builder; use log::{debug, error, info, trace, warn, 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.11.6/examples/in_tests.rs000064400000000000000000000020541046102023000160550ustar 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. */ fn main() {} #[cfg(test)] mod tests { use log::debug; 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.11.6/examples/syslog_friendly_format.rs000064400000000000000000000013561046102023000210150ustar 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.11.6/src/fmt/humantime.rs000064400000000000000000000062611046102023000157570ustar 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}; 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()) /// }); /// ``` 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`]: std::fmt::Display 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 fmt::Debug for TimestampValue<'_> { 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.11.6/src/fmt/kv.rs000064400000000000000000000042271046102023000144100ustar 00000000000000use std::io::{self, Write}; #[cfg(feature = "color")] use super::WriteStyle; use super::{Formatter, StyledValue}; #[cfg(feature = "color")] use anstyle::Style; use log::kv::{Error, Key, Source, Value, VisitSource}; /// Format function for serializing key/value pairs /// /// This function determines how key/value pairs for structured logs are serialized within the default /// format. pub(crate) type KvFormatFn = dyn Fn(&mut Formatter, &dyn Source) -> io::Result<()> + Sync + Send; /// Null Key Value Format /// /// This function is intended to be passed to /// [`Builder::format_key_values`](crate::Builder::format_key_values). /// /// This key value format simply ignores any key/value fields and doesn't include them in the /// output. pub fn hidden_kv_format(_formatter: &mut Formatter, _fields: &dyn Source) -> io::Result<()> { Ok(()) } /// Default Key Value Format /// /// This function is intended to be passed to /// [`Builder::format_key_values`](crate::Builder::format_key_values). /// /// This is the default key/value format. Which uses an "=" as the separator between the key and /// value and a " " between each pair. /// /// For example: `ip=127.0.0.1 port=123456 path=/example` pub fn default_kv_format(formatter: &mut Formatter, fields: &dyn Source) -> io::Result<()> { fields .visit(&mut DefaultVisitSource(formatter)) .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } struct DefaultVisitSource<'a>(&'a mut Formatter); impl<'kvs> VisitSource<'kvs> for DefaultVisitSource<'_> { fn visit_pair(&mut self, key: Key<'_>, value: Value<'kvs>) -> Result<(), Error> { write!(self.0, " {}={}", self.style_key(key), value)?; Ok(()) } } impl DefaultVisitSource<'_> { fn style_key<'k>(&self, text: Key<'k>) -> StyledValue> { #[cfg(feature = "color")] { StyledValue { style: if self.0.write_style == WriteStyle::Never { Style::new() } else { Style::new().italic() }, value: text, } } #[cfg(not(feature = "color"))] { text } } } env_logger-0.11.6/src/fmt/mod.rs000064400000000000000000000624411046102023000145510ustar 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. //! //! Terminal styling is done through ANSI escape codes and will be adapted to the capabilities of //! the target stream.s //! //! For example, you could use one of: //! - [anstyle](https://docs.rs/anstyle) is a minimal, runtime string styling API and is re-exported as [`style`] //! - [owo-colors](https://docs.rs/owo-colors) is a feature rich runtime string styling API //! - [color-print](https://docs.rs/color-print) for feature-rich compile-time styling API //! //! See also [`Formatter::default_level_style`] //! //! ``` //! use std::io::Write; //! //! let mut builder = env_logger::Builder::new(); //! //! builder.format(|buf, record| { //! writeln!(buf, "{}: {}", //! record.level(), //! record.args()) //! }); //! ``` //! //! # Key Value arguments //! //! If the `unstable-kv` feature is enabled, then the default format will include key values from //! the log by default, but this can be disabled by calling [`Builder::format_key_values`] //! with [`hidden_kv_format`] as the format function. //! //! The way these keys and values are formatted can also be customized with a separate format //! function that is called by the default format with [`Builder::format_key_values`]. //! //! ``` //! # #[cfg(feature= "unstable-kv")] //! # { //! use log::info; //! env_logger::init(); //! info!(x="45"; "Some message"); //! info!(x="12"; "Another message {x}", x="12"); //! # } //! ``` //! //! See . //! //! [`Builder::format`]: crate::Builder::format //! [`Write`]: std::io::Write //! [`Builder::format_key_values`]: crate::Builder::format_key_values use std::cell::RefCell; use std::fmt::Display; use std::io::prelude::Write; use std::rc::Rc; use std::{fmt, io, mem}; #[cfg(feature = "color")] use log::Level; use log::Record; #[cfg(feature = "humantime")] mod humantime; #[cfg(feature = "unstable-kv")] mod kv; pub(crate) mod writer; #[cfg(feature = "color")] pub use anstyle as style; #[cfg(feature = "humantime")] pub use self::humantime::Timestamp; #[cfg(feature = "unstable-kv")] pub use self::kv::*; pub use self::writer::Target; pub use self::writer::WriteStyle; use self::writer::{Buffer, Writer}; /// 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). #[allow(clippy::exhaustive_enums)] // compatibility #[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 styling using ANSI escape codes. /// /// # 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`]: std::io::Write /// [`writeln`]: std::writeln 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(); } } #[cfg(feature = "color")] impl Formatter { /// Get the default [`style::Style`] for the given level. /// /// The style can be used to print other values besides the level. /// /// See [`style`] for how to adapt it to the styling crate of your choice pub fn default_level_style(&self, level: Level) -> style::Style { if self.write_style == WriteStyle::Never { style::Style::new() } else { match level { Level::Trace => style::AnsiColor::Cyan.on_default(), Level::Debug => style::AnsiColor::Blue.on_default(), Level::Info => style::AnsiColor::Green.on_default(), Level::Warn => style::AnsiColor::Yellow.on_default(), Level::Error => style::AnsiColor::Red .on_default() .effects(style::Effects::BOLD), } } } } 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 { let buf = self.buf.borrow(); f.debug_struct("Formatter") .field("buf", &buf) .field("write_style", &self.write_style) .finish() } } pub(crate) type FormatFn = Box) -> io::Result<()> + Sync + Send>; pub(crate) struct Builder { pub(crate) format_timestamp: Option, pub(crate) format_module_path: bool, pub(crate) format_target: bool, pub(crate) format_level: bool, pub(crate) format_indent: Option, pub(crate) custom_format: Option, pub(crate) format_suffix: &'static str, pub(crate) format_file: bool, pub(crate) format_line_number: bool, #[cfg(feature = "unstable-kv")] pub(crate) kv_format: Option>, built: bool, } 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(crate) 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, source_file: built.format_file, source_line_number: built.format_line_number, #[cfg(feature = "unstable-kv")] kv_format: built.kv_format.as_deref().unwrap_or(&default_kv_format), buf, }; fmt.write(record) }) } } } impl Default for Builder { fn default() -> Self { Builder { format_timestamp: Some(Default::default()), format_module_path: false, format_target: true, format_level: true, format_file: false, format_line_number: false, format_indent: Some(4), custom_format: None, format_suffix: "\n", #[cfg(feature = "unstable-kv")] kv_format: None, built: false, } } } #[cfg(feature = "color")] type SubtleStyle = StyledValue<&'static str>; #[cfg(not(feature = "color"))] type SubtleStyle = &'static str; /// A value that can be printed using the given styles. #[cfg(feature = "color")] struct StyledValue { style: style::Style, value: T, } #[cfg(feature = "color")] impl Display for StyledValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let style = self.style; // We need to make sure `f`s settings don't get passed onto the styling but do get passed // to the value write!(f, "{style}")?; self.value.fmt(f)?; write!(f, "{style:#}")?; Ok(()) } } #[cfg(not(feature = "color"))] type StyledValue = T; /// 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, source_file: bool, source_line_number: bool, written_header_value: bool, indent: Option, buf: &'a mut Formatter, suffix: &'a str, #[cfg(feature = "unstable-kv")] kv_format: &'a KvFormatFn, } impl DefaultFormat<'_> { fn write(mut self, record: &Record<'_>) -> io::Result<()> { self.write_timestamp()?; self.write_level(record)?; self.write_module_path(record)?; self.write_source_location(record)?; self.write_target(record)?; self.finish_header()?; self.write_args(record)?; #[cfg(feature = "unstable-kv")] self.write_kv(record)?; write!(self.buf, "{}", self.suffix) } fn subtle_style(&self, text: &'static str) -> SubtleStyle { #[cfg(feature = "color")] { StyledValue { style: if self.buf.write_style == WriteStyle::Never { style::Style::new() } else { style::AnsiColor::BrightBlack.on_default() }, 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 = { let level = record.level(); #[cfg(feature = "color")] { StyledValue { style: self.buf.default_level_style(level), value: level, } } #[cfg(not(feature = "color"))] { level } }; self.write_header_value(format_args!("{level:<5}")) } fn write_timestamp(&mut self) -> io::Result<()> { #[cfg(feature = "humantime")] { use self::TimestampPrecision::{Micros, Millis, Nanos, Seconds}; 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_source_location(&mut self, record: &Record<'_>) -> io::Result<()> { if !self.source_file { return Ok(()); } if let Some(file_path) = record.file() { let line = self.source_line_number.then(|| record.line()).flatten(); match line { Some(line) => self.write_header_value(format_args!("{file_path}:{line}")), None => self.write_header_value(file_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()), Some(indent_count) => { // Create a wrapper around the buffer only if we have to actually indent the message struct IndentWrapper<'a, 'b> { fmt: &'a mut DefaultFormat<'b>, indent_count: usize, } impl Write for IndentWrapper<'_, '_> { 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())?; } Ok(()) } } } #[cfg(feature = "unstable-kv")] fn write_kv(&mut self, record: &Record<'_>) -> io::Result<()> { let format = self.kv_format; format(self.buf, record.key_values()) } } #[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.as_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) } fn formatter() -> Formatter { let writer = writer::Builder::new() .write_style(WriteStyle::Never) .build(); Formatter::new(&writer) } #[test] fn format_with_header() { let mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: true, target: false, level: true, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, level: false, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, written_header_value: false, indent: None, suffix: "\n", buf: &mut f, }); assert_eq!("log\nmessage\n", written); } #[test] fn format_indent_spaces() { let mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: true, target: false, level: true, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: true, target: false, level: true, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, level: false, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, written_header_value: false, indent: Some(4), suffix: "\n", buf: &mut f, }); assert_eq!("log\n message\n", written); } #[test] fn format_suffix() { let mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, level: false, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, level: false, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write_target( "target", DefaultFormat { timestamp: None, module_path: true, target: true, level: true, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: true, target: true, level: true, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, 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 mut f = formatter(); let written = write_target( "target", DefaultFormat { timestamp: None, module_path: true, target: false, level: true, source_file: false, source_line_number: false, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, written_header_value: false, indent: None, suffix: "\n", buf: &mut f, }, ); assert_eq!("[INFO test::path] log\nmessage\n", written); } #[test] fn format_with_source_file_and_line_number() { let mut f = formatter(); let written = write(DefaultFormat { timestamp: None, module_path: false, target: false, level: true, source_file: true, source_line_number: true, #[cfg(feature = "unstable-kv")] kv_format: &hidden_kv_format, written_header_value: false, indent: None, suffix: "\n", buf: &mut f, }); assert_eq!("[INFO test.rs:144] log\nmessage\n", written); } #[cfg(feature = "unstable-kv")] #[test] fn format_kv_default() { let kvs = &[("a", 1u32), ("b", 2u32)][..]; let mut f = formatter(); let record = Record::builder() .args(format_args!("log message")) .level(Level::Info) .module_path(Some("test::path")) .key_values(&kvs) .build(); let written = write_record( record, DefaultFormat { timestamp: None, module_path: false, target: false, level: true, source_file: false, source_line_number: false, kv_format: &default_kv_format, written_header_value: false, indent: None, suffix: "\n", buf: &mut f, }, ); assert_eq!("[INFO ] log message a=1 b=2\n", written); } #[cfg(feature = "unstable-kv")] #[test] fn format_kv_default_full() { let kvs = &[("a", 1u32), ("b", 2u32)][..]; let mut f = formatter(); let record = Record::builder() .args(format_args!("log\nmessage")) .level(Level::Info) .module_path(Some("test::path")) .target("target") .file(Some("test.rs")) .line(Some(42)) .key_values(&kvs) .build(); let written = write_record( record, DefaultFormat { timestamp: None, module_path: true, target: true, level: true, source_file: true, source_line_number: true, kv_format: &default_kv_format, written_header_value: false, indent: None, suffix: "\n", buf: &mut f, }, ); assert_eq!( "[INFO test::path test.rs:42 target] log\nmessage a=1 b=2\n", written ); } } env_logger-0.11.6/src/fmt/writer/buffer.rs000064400000000000000000000122751046102023000165570ustar 00000000000000use std::{io, sync::Mutex}; use crate::fmt::writer::WriteStyle; #[derive(Debug)] pub(in crate::fmt::writer) struct BufferWriter { target: WritableTarget, write_style: WriteStyle, } impl BufferWriter { pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self { BufferWriter { target: if is_test { WritableTarget::PrintStderr } else { WritableTarget::WriteStderr }, write_style, } } pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self { BufferWriter { target: if is_test { WritableTarget::PrintStdout } else { WritableTarget::WriteStdout }, write_style, } } pub(in crate::fmt::writer) fn pipe( pipe: Box>, write_style: WriteStyle, ) -> Self { BufferWriter { target: WritableTarget::Pipe(pipe), write_style, } } pub(in crate::fmt::writer) fn write_style(&self) -> WriteStyle { self.write_style } pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { Buffer(Vec::new()) } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { #![allow(clippy::print_stdout)] // enabled for tests only #![allow(clippy::print_stderr)] // enabled for tests only use std::io::Write as _; let buf = buf.as_bytes(); match &self.target { WritableTarget::WriteStdout => { let stream = io::stdout(); #[cfg(feature = "color")] let stream = anstream::AutoStream::new(stream, self.write_style.into()); let mut stream = stream.lock(); stream.write_all(buf)?; stream.flush()?; } WritableTarget::PrintStdout => { #[cfg(feature = "color")] let buf = adapt(buf, self.write_style)?; #[cfg(feature = "color")] let buf = &buf; let buf = String::from_utf8_lossy(buf); print!("{buf}"); } WritableTarget::WriteStderr => { let stream = io::stderr(); #[cfg(feature = "color")] let stream = anstream::AutoStream::new(stream, self.write_style.into()); let mut stream = stream.lock(); stream.write_all(buf)?; stream.flush()?; } WritableTarget::PrintStderr => { #[cfg(feature = "color")] let buf = adapt(buf, self.write_style)?; #[cfg(feature = "color")] let buf = &buf; let buf = String::from_utf8_lossy(buf); eprint!("{buf}"); } WritableTarget::Pipe(pipe) => { #[cfg(feature = "color")] let buf = adapt(buf, self.write_style)?; #[cfg(feature = "color")] let buf = &buf; let mut stream = pipe.lock().expect("no panics while held"); stream.write_all(buf)?; stream.flush()?; } } Ok(()) } } #[cfg(feature = "color")] fn adapt(buf: &[u8], write_style: WriteStyle) -> io::Result> { use std::io::Write as _; let adapted = Vec::with_capacity(buf.len()); let mut stream = anstream::AutoStream::new(adapted, write_style.into()); stream.write_all(buf)?; let adapted = stream.into_inner(); Ok(adapted) } pub(in crate::fmt) struct Buffer(Vec); 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(()) } pub(in crate::fmt) fn as_bytes(&self) -> &[u8] { &self.0 } } impl std::fmt::Debug for Buffer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { String::from_utf8_lossy(self.as_bytes()).fmt(f) } } /// 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 written to standard output. WriteStdout, /// Logs will be printed to standard output. PrintStdout, /// Logs will be written to standard error. WriteStderr, /// Logs will be printed to standard error. PrintStderr, /// Logs will be sent to a custom pipe. Pipe(Box>), } impl std::fmt::Debug for WritableTarget { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::WriteStdout => "stdout", Self::PrintStdout => "stdout", Self::WriteStderr => "stderr", Self::PrintStderr => "stderr", Self::Pipe(_) => "pipe", } ) } } env_logger-0.11.6/src/fmt/writer/mod.rs000064400000000000000000000120631046102023000160600ustar 00000000000000mod buffer; mod target; use self::buffer::BufferWriter; use std::{io, mem, sync::Mutex}; pub(super) use self::buffer::Buffer; pub use target::Target; /// Whether or not to print styles to the target. #[allow(clippy::exhaustive_enums)] // By definition don't need more #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] pub enum WriteStyle { /// Try to print styles, but don't force the issue. #[default] Auto, /// Try very hard to print styles. Always, /// Never print styles. Never, } #[cfg(feature = "color")] impl From for WriteStyle { fn from(choice: anstream::ColorChoice) -> Self { match choice { anstream::ColorChoice::Auto => Self::Auto, anstream::ColorChoice::Always => Self::Always, anstream::ColorChoice::AlwaysAnsi => Self::Always, anstream::ColorChoice::Never => Self::Never, } } } #[cfg(feature = "color")] impl From for anstream::ColorChoice { fn from(choice: WriteStyle) -> Self { match choice { WriteStyle::Auto => anstream::ColorChoice::Auto, WriteStyle::Always => anstream::ColorChoice::Always, WriteStyle::Never => anstream::ColorChoice::Never, } } } /// A terminal target with color awareness. #[derive(Debug)] pub(crate) struct Writer { inner: BufferWriter, } impl Writer { pub(crate) fn write_style(&self) -> WriteStyle { self.inner.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: Target, 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; 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 = self.write_style; #[cfg(feature = "auto-color")] let color_choice = if color_choice == WriteStyle::Auto { match &self.target { Target::Stdout => anstream::AutoStream::choice(&io::stdout()).into(), Target::Stderr => anstream::AutoStream::choice(&io::stderr()).into(), Target::Pipe(_) => color_choice, } } else { color_choice }; let color_choice = if color_choice == WriteStyle::Auto { WriteStyle::Never } else { color_choice }; let writer = match mem::take(&mut self.target) { Target::Stdout => BufferWriter::stdout(self.is_test, color_choice), Target::Stderr => BufferWriter::stderr(self.is_test, color_choice), Target::Pipe(pipe) => BufferWriter::pipe(Box::new(Mutex::new(pipe)), color_choice), }; Writer { inner: writer } } } impl Default for Builder { fn default() -> Self { Builder::new() } } 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.11.6/src/fmt/writer/target.rs000064400000000000000000000012521046102023000165650ustar 00000000000000/// Log target, either `stdout`, `stderr` or a custom pipe. #[non_exhaustive] #[derive(Default)] pub enum Target { /// Logs will be sent to standard output. Stdout, /// Logs will be sent to standard error. #[default] Stderr, /// Logs will be sent to a custom pipe. Pipe(Box), } impl std::fmt::Debug for Target { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Stdout => "stdout", Self::Stderr => "stderr", Self::Pipe(_) => "pipe", } ) } } env_logger-0.11.6/src/lib.rs000064400000000000000000000232571046102023000137540ustar 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`: //! //! ```console //! $ RUST_LOG=error ./main //! [2017-11-09T02:12:24Z ERROR main] this is printed by default //! ``` //! //! ```console //! $ 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 //! ``` //! //! ```console //! $ 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: //! //! ```console //! $ 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: //! //! ```console //! $ 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: //! //! ```console //! $ 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 //! //! **By default all logging is disabled except for the `error` level** //! //! The **`RUST_LOG`** environment variable controls logging with the syntax: //! ```console //! RUST_LOG=[target][=][level][,...] //! ``` //! Or in other words, its a comma-separated list of directives. //! Directives can filter by **target**, by **level**, or both (using `=`). //! //! For example, //! ```console //! RUST_LOG=data=debug,hardware=debug //! ``` //! //! **target** is typically 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 `hello`. //! //! Furthermore, the log can be filtered using prefix-search based on the //! specified log target. //! //! For example, `RUST_LOG=example` would match the following 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. //! //! **level** is the maximum [`log::Level`][level-enum] to be shown and includes: //! - `error` //! - `warn` //! - `info` //! - `debug` //! - `trace` //! - `off` (pseudo level to disable all logging for the target) //! //! Logging level names are case-insensitive; 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. //! //! Some examples of valid values of `RUST_LOG` are: //! //! - `RUST_LOG=hello` turns on all logging for the `hello` module //! - `RUST_LOG=trace` turns on all logging for the application, regardless of its name //! - `RUST_LOG=TRACE` turns on all logging for the application, regardless of its name (same as previous) //! - `RUST_LOG=info` turns on all info logging //! - `RUST_LOG=INFO` turns on all info logging (same as previous) //! - `RUST_LOG=hello=debug` turns on debug logging for `hello` //! - `RUST_LOG=hello=DEBUG` turns on debug logging for `hello` (same as previous) //! - `RUST_LOG=hello,std::option` turns on `hello`, and std's option logging //! - `RUST_LOG=error,hello=warn` turn on global error logging and also warn for `hello` //! - `RUST_LOG=error,hello=off` turn on global error logging, but turn off logging for `hello` //! - `RUST_LOG=off` turns off all logging for the application //! - `RUST_LOG=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: //! //! ``` //! #[cfg(test)] //! mod tests { //! use log::info; //! //! 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`][crate::fmt::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/rust-cli/env_logger/tree/main/examples //! [level-enum]: https://docs.rs/log/latest/log/enum.Level.html //! [log-crate-url]: https://docs.rs/log #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] mod logger; pub mod fmt; pub use self::fmt::{Target, TimestampPrecision, WriteStyle}; pub use self::logger::*; env_logger-0.11.6/src/logger.rs000064400000000000000000001002771046102023000144630ustar 00000000000000use std::{borrow::Cow, cell::RefCell, env, io}; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; use crate::fmt; use crate::fmt::writer::{self, Writer}; use crate::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"; /// `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 /// /// ``` /// # use std::io::Write; /// use env_logger::Builder; /// use log::{LevelFilter, error, info}; /// /// 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: env_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. /// /// When the `color` feature is enabled, styling via ANSI escape codes is supported and the /// output will automatically respect [`Builder::write_style`]. /// /// # 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 + 'static, { 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 source file path in the default format. pub fn format_file(&mut self, write: bool) -> &mut Self { self.format.format_file = write; self } /// Whether or not to write the source line number path in the default format. /// /// Only has effect if `format_file` is also enabled pub fn format_line_number(&mut self, write: bool) -> &mut Self { self.format.format_line_number = write; self } /// Whether or not to write the source path and line number /// /// Equivalent to calling both `format_file` and `format_line_number` /// with `true` pub fn format_source_path(&mut self, write: bool) -> &mut Self { self.format_file(write).format_line_number(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 } /// Set the format for structured key/value pairs in the log record /// /// With the default format, this function is called for each record and should format /// the structured key-value pairs as returned by [`log::Record::key_values`]. /// /// The format function is expected to output the string directly to the `Formatter` so that /// implementations can use the [`std::fmt`] macros, similar to the main format function. /// /// The default format uses a space to separate each key-value pair, with an "=" between /// the key and value. #[cfg(feature = "unstable-kv")] pub fn format_key_values(&mut self, format: F) -> &mut Self where F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static, { self.format.kv_format = Some(Box::new(format)); 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 std::fmt::Debug for Builder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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() } } } /// 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: env_filter::Filter, format: FormatFn, } 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> = const { 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| { if let Ok(mut tl_buf) = tl_buf.try_borrow_mut() { // There are no active borrows of the buffer if let Some(ref mut formatter) = *tl_buf { // We have a previously set 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); } else { // We don't have a previously set formatter let mut formatter = Formatter::new(&self.writer); print(&mut formatter, record); *tl_buf = Some(formatter); } } else { // There's already an active borrow of the buffer (due to re-entrancy) 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 std::fmt::Debug for Logger { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Logger") .field("filter", &self.filter) .finish() } } /// 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>, } 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, T> From for Env<'a> where T: Into>, { fn from(filter_env: T) -> Self { Env::default().filter(filter_env.into()) } } impl Default for Env<'_> { fn default() -> Self { Env { filter: Var::new(DEFAULT_FILTER_ENV), write_style: Var::new(DEFAULT_WRITE_STYLE_ENV), } } } #[derive(Debug)] struct Var<'a> { name: Cow<'a, str>, default: Option>, } 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.clone().map(|v| v.into_owned())) } } /// 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.11.6/tests/init-twice-retains-filter.rs000064400000000000000000000021721046102023000205540ustar 00000000000000#![allow(clippy::unwrap_used)] 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.11.6/tests/log-in-log.rs000064400000000000000000000016371046102023000155230ustar 00000000000000#![allow(clippy::unwrap_used)] #[macro_use] extern crate log; 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.11.6/tests/log_tls_dtors.rs000064400000000000000000000041761046102023000164360ustar 00000000000000#![allow(clippy::unwrap_used)] #[macro_use] extern crate log; 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 = const { DropMe }; static DROP_ME_1: DropMe = const { DropMe }; static DROP_ME_2: DropMe = const { DropMe }; static DROP_ME_3: DropMe = const { DropMe }; static DROP_ME_4: DropMe = const { DropMe }; static DROP_ME_5: DropMe = const { DropMe }; static DROP_ME_6: DropMe = const { DropMe }; static DROP_ME_7: DropMe = const { DropMe }; static DROP_ME_8: DropMe = const { DropMe }; static DROP_ME_9: DropMe = const { 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.11.6/tests/regexp_filter.rs000064400000000000000000000024441046102023000164130ustar 00000000000000#![allow(clippy::unwrap_used)] #[macro_use] extern crate log; 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_owned()) { panic!("RUST_LOG={rust_log} should allow the test log message") } } fn assert_message_not_printed(rust_log: &str) { if run_child(rust_log.to_owned()) { panic!("RUST_LOG={rust_log} should not allow the test log message") } } 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"); }