summaryrefslogtreecommitdiffstats
path: root/third_party/rust/env_logger
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/rust/env_logger
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/env_logger')
-rw-r--r--third_party/rust/env_logger/.cargo-checksum.json1
-rw-r--r--third_party/rust/env_logger/Cargo.lock254
-rw-r--r--third_party/rust/env_logger/Cargo.toml129
-rw-r--r--third_party/rust/env_logger/LICENSE-APACHE201
-rw-r--r--third_party/rust/env_logger/LICENSE-MIT23
-rw-r--r--third_party/rust/env_logger/README.md183
-rw-r--r--third_party/rust/env_logger/examples/custom_default_format.rs40
-rw-r--r--third_party/rust/env_logger/examples/custom_format.rs54
-rw-r--r--third_party/rust/env_logger/examples/custom_logger.rs62
-rw-r--r--third_party/rust/env_logger/examples/default.rs38
-rw-r--r--third_party/rust/env_logger/examples/direct_logger.rs39
-rw-r--r--third_party/rust/env_logger/examples/filters_from_code.rs20
-rw-r--r--third_party/rust/env_logger/examples/in_tests.rs54
-rw-r--r--third_party/rust/env_logger/examples/syslog_friendly_format.rs24
-rw-r--r--third_party/rust/env_logger/src/filter/mod.rs868
-rw-r--r--third_party/rust/env_logger/src/filter/regex.rs29
-rw-r--r--third_party/rust/env_logger/src/filter/string.rs24
-rw-r--r--third_party/rust/env_logger/src/fmt/humantime/extern_impl.rs118
-rw-r--r--third_party/rust/env_logger/src/fmt/humantime/mod.rs11
-rw-r--r--third_party/rust/env_logger/src/fmt/humantime/shim_impl.rs5
-rw-r--r--third_party/rust/env_logger/src/fmt/mod.rs652
-rw-r--r--third_party/rust/env_logger/src/fmt/writer/atty.rs33
-rw-r--r--third_party/rust/env_logger/src/fmt/writer/mod.rs253
-rw-r--r--third_party/rust/env_logger/src/fmt/writer/termcolor/extern_impl.rs532
-rw-r--r--third_party/rust/env_logger/src/fmt/writer/termcolor/mod.rs12
-rw-r--r--third_party/rust/env_logger/src/fmt/writer/termcolor/shim_impl.rs72
-rw-r--r--third_party/rust/env_logger/src/lib.rs1311
-rw-r--r--third_party/rust/env_logger/tests/init-twice-retains-filter.rs40
-rw-r--r--third_party/rust/env_logger/tests/log-in-log.rs39
-rw-r--r--third_party/rust/env_logger/tests/log_tls_dtors.rs66
-rw-r--r--third_party/rust/env_logger/tests/regexp_filter.rs57
31 files changed, 5244 insertions, 0 deletions
diff --git a/third_party/rust/env_logger/.cargo-checksum.json b/third_party/rust/env_logger/.cargo-checksum.json
new file mode 100644
index 0000000000..cece98a3aa
--- /dev/null
+++ b/third_party/rust/env_logger/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"a38131bba6684887371b472dec50908e55bfccf06eb3029b26e4dddc5ee55a7a","Cargo.toml":"4efdae82e55c1f6a72d80d9853dc2caab904e1ea822255418b78549e4d87fc89","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"c126e3dffb9c9e40c9355b5b0261ca84aa72425c5819bbe13abc40a161c51c78","examples/custom_default_format.rs":"a6fe2b9255248a19e2cac58033ac80ff1fbf78af9786a7331efcbafe469815a2","examples/custom_format.rs":"dbdf157c45a071e491cbe1038ad83d50a0154ea0691ee07dc4bd15fc1ca9205f","examples/custom_logger.rs":"d78a0998191b843bc902608dbb535c008ec53202528d7a626aaa0a2282a89494","examples/default.rs":"7ed1c6a8a8fe457a86676bd3a75c07d4ec7fb54147cf2825c9d299a5878a24cd","examples/direct_logger.rs":"5ae565fab0583c06de4cc3554fb4c8dc64cee8e2e1337b9aafff44e1bfb94e43","examples/filters_from_code.rs":"57edd53e811873c67b3069b4ea8fac619a0d5a507f6462ea4d0682bdf5d84a56","examples/in_tests.rs":"0b714146ab6f38d23394bfa5a5383fca2262a184a7628b8182d57b267e0917e7","examples/syslog_friendly_format.rs":"4269a86f549253bad54c7e8aae03d5ab09959a5c9d798d3fc6c4745cd5d96cd9","src/filter/mod.rs":"8c93f6ee0825ef537a554db0a6393c1c9334998852e9328b136058654a7bba1a","src/filter/regex.rs":"bdf875bac25e089e1e462f5dd01a88678067c24118ecd6268561c6a6af39747d","src/filter/string.rs":"fac54d51189fc0b5d2bff334b7a7e465177b431e3428299e345e1f90062d832e","src/fmt/humantime/extern_impl.rs":"97e0a128275c086b02760a33c22db8524f005994ebb1318a6ac582fd51cf444a","src/fmt/humantime/mod.rs":"f4111c26cf2ffb85c1d639bd7674d55af7e1736e7e98c52f7be3070046a3253f","src/fmt/humantime/shim_impl.rs":"cce9a252abd5952fa109a72b1dfb85a593d237e22606b2b608a32c69184560e9","src/fmt/mod.rs":"b4e23397f7b33df0c353339c4573901f259cfc222975c9f7bec1da0cfa50f159","src/fmt/writer/atty.rs":"098f4c358177d34b0d5a8202ac308f23b08a694b053557c6213f73ab29f8d96e","src/fmt/writer/mod.rs":"ba0723bfdbc5d5210dc59fe74de95affd038a7e15a9d9ca7a71b0413d77ff5bd","src/fmt/writer/termcolor/extern_impl.rs":"d3c0f1f8152b455cb7ac99cf29835ad1c8653d75b2873f1705517c0a45b3b549","src/fmt/writer/termcolor/mod.rs":"9c4e0ffea8ccffa87224af1dbf620483131bf346f86bd81233e4b69d872d1df6","src/fmt/writer/termcolor/shim_impl.rs":"8e6e7d40782b14e33c6b75b81899a612549c2c7937ce28b48cdc60e1e3f8b855","src/lib.rs":"90b2e514025283577be0e7d888746019cc755f0f490323237277e13a2e8d3fd2","tests/init-twice-retains-filter.rs":"be5cd2132342d89ede1f5c4266173bb3c4d51cc22a1847f133d299a1c5430ccb","tests/log-in-log.rs":"29fecc65c1e0d1c22d79c97e7ca843ad44a91f27934148d7a05c48899a3f39d8","tests/log_tls_dtors.rs":"7320667d774a9b05037f7bf273fb2574dec0705707692a9cd2f46f4cd5bc68dd","tests/regexp_filter.rs":"a84263c995b534b6479a1d0abadf63f4f0264958ff86d9173d6b2139b82c4dc5"},"package":"85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"} \ No newline at end of file
diff --git a/third_party/rust/env_logger/Cargo.lock b/third_party/rust/env_logger/Cargo.lock
new file mode 100644
index 0000000000..ef3b24145f
--- /dev/null
+++ b/third_party/rust/env_logger/Cargo.lock
@@ -0,0 +1,254 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cc"
+version = "1.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "env_logger"
+version = "0.10.0"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7d367024b3f3414d8e01f437f704f41a9f64ab36f9067fa73e526ad4c763c87"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "regex"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "rustix"
+version = "0.36.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
diff --git a/third_party/rust/env_logger/Cargo.toml b/third_party/rust/env_logger/Cargo.toml
new file mode 100644
index 0000000000..351c6e53dd
--- /dev/null
+++ b/third_party/rust/env_logger/Cargo.toml
@@ -0,0 +1,129 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.60.0"
+name = "env_logger"
+version = "0.10.0"
+include = [
+ "build.rs",
+ "src/**/*",
+ "Cargo.toml",
+ "LICENSE*",
+ "README.md",
+ "tests/**/*",
+ "benches/**/*",
+ "examples/**/*",
+]
+description = """
+A logging implementation for `log` which is configured via an environment
+variable.
+"""
+readme = "README.md"
+keywords = [
+ "logging",
+ "log",
+ "logger",
+]
+categories = ["development-tools::debugging"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/rust-cli/env_logger/"
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "Unreleased"
+replace = "{{version}}"
+min = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = '\.\.\.HEAD'
+replace = "...{{tag_name}}"
+exactly = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "ReleaseDate"
+replace = "{{date}}"
+min = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "<!-- next-header -->"
+replace = """
+<!-- next-header -->
+## [Unreleased] - ReleaseDate
+"""
+exactly = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "<!-- next-url -->"
+replace = """
+<!-- next-url -->
+[Unreleased]: https://github.com/rust-cli/env_logger/compare/{{tag_name}}...HEAD"""
+exactly = 1
+
+[[test]]
+name = "regexp_filter"
+harness = false
+
+[[test]]
+name = "log-in-log"
+harness = false
+
+[[test]]
+name = "log_tls_dtors"
+harness = false
+
+[[test]]
+name = "init-twice-retains-filter"
+harness = false
+
+[dependencies.humantime]
+version = "2.0.0"
+optional = true
+
+[dependencies.is-terminal]
+version = "0.4.0"
+optional = true
+
+[dependencies.log]
+version = "0.4.8"
+features = ["std"]
+
+[dependencies.regex]
+version = "1.0.3"
+features = [
+ "std",
+ "perf",
+]
+optional = true
+default-features = false
+
+[dependencies.termcolor]
+version = "1.1.1"
+optional = true
+
+[features]
+auto-color = [
+ "dep:is-terminal",
+ "color",
+]
+color = ["dep:termcolor"]
+default = [
+ "auto-color",
+ "humantime",
+ "regex",
+]
+humantime = ["dep:humantime"]
+regex = ["dep:regex"]
diff --git a/third_party/rust/env_logger/LICENSE-APACHE b/third_party/rust/env_logger/LICENSE-APACHE
new file mode 100644
index 0000000000..16fe87b06e
--- /dev/null
+++ b/third_party/rust/env_logger/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ 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.
diff --git a/third_party/rust/env_logger/LICENSE-MIT b/third_party/rust/env_logger/LICENSE-MIT
new file mode 100644
index 0000000000..31aa79387f
--- /dev/null
+++ b/third_party/rust/env_logger/LICENSE-MIT
@@ -0,0 +1,23 @@
+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.
diff --git a/third_party/rust/env_logger/README.md b/third_party/rust/env_logger/README.md
new file mode 100644
index 0000000000..75add88fe9
--- /dev/null
+++ b/third_party/rust/env_logger/README.md
@@ -0,0 +1,183 @@
+# env_logger
+
+[![crates.io](https://img.shields.io/crates/v/env_logger.svg)](https://crates.io/crates/env_logger)
+[![Documentation](https://docs.rs/env_logger/badge.svg)](https://docs.rs/env_logger)
+[![Documentation](https://img.shields.io/badge/docs-main-blue.svg)](https://env-logger-rs.github.io/env_logger/env_logger/index.html)
+
+Implements a logger that can be configured via environment variables.
+
+## Usage
+
+### In libraries
+
+`env_logger` makes sense when used in executables (binary projects). Libraries should use the [`log`](https://docs.rs/log) crate instead.
+
+### In executables
+
+It must be added along with `log` to the project dependencies:
+
+```toml
+[dependencies]
+log = "0.4.0"
+env_logger = "0.9.0"
+```
+
+`env_logger` must be initialized as early as possible in the project. After it's initialized, you can use the `log` macros to do actual logging.
+
+```rust
+#[macro_use]
+extern crate log;
+
+fn main() {
+ env_logger::init();
+
+ info!("starting up");
+
+ // ...
+}
+```
+
+Then when running the executable, specify a value for the **`RUST_LOG`**
+environment variable that corresponds with the log messages you want to show.
+
+```bash
+$ RUST_LOG=info ./main
+[2018-11-03T06:09:06Z INFO default] starting up
+```
+
+The letter case is not significant for the logging level names; e.g., `debug`,
+`DEBUG`, and `dEbuG` all represent the same logging level. Therefore, the
+previous example could also have been written this way, specifying the log
+level as `INFO` rather than as `info`:
+
+```bash
+$ RUST_LOG=INFO ./main
+[2018-11-03T06:09:06Z INFO default] starting up
+```
+
+So which form should you use? For consistency, our convention is to use lower
+case names. Where our docs do use other forms, they do so in the context of
+specific examples, so you won't be surprised if you see similar usage in the
+wild.
+
+The log levels that may be specified correspond to the [`log::Level`][level-enum]
+enum from the `log` crate. They are:
+
+ * `error`
+ * `warn`
+ * `info`
+ * `debug`
+ * `trace`
+
+[level-enum]: https://docs.rs/log/latest/log/enum.Level.html "log::Level (docs.rs)"
+
+There is also a pseudo logging level, `off`, which may be specified to disable
+all logging for a given module or for the entire application. As with the
+logging levels, the letter case is not significant.
+
+`env_logger` can be configured in other ways besides an environment variable. See [the examples](https://github.com/env-logger-rs/env_logger/tree/main/examples) for more approaches.
+
+### In tests
+
+Tests can use the `env_logger` crate to see log messages generated during that test:
+
+```toml
+[dependencies]
+log = "0.4.0"
+
+[dev-dependencies]
+env_logger = "0.9.0"
+```
+
+```rust
+#[macro_use]
+extern crate log;
+
+fn add_one(num: i32) -> i32 {
+ info!("add_one called with {}", num);
+ num + 1
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn init() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ }
+
+ #[test]
+ fn it_adds_one() {
+ init();
+
+ info!("can log from the test too");
+ assert_eq!(3, add_one(2));
+ }
+
+ #[test]
+ fn it_handles_negative_numbers() {
+ init();
+
+ info!("logging from another test");
+ assert_eq!(-7, add_one(-8));
+ }
+}
+```
+
+Assuming the module under test is called `my_lib`, running the tests with the
+`RUST_LOG` filtering to info messages from this module looks like:
+
+```bash
+$ RUST_LOG=my_lib=info cargo test
+ Running target/debug/my_lib-...
+
+running 2 tests
+[INFO my_lib::tests] logging from another test
+[INFO my_lib] add_one called with -8
+test tests::it_handles_negative_numbers ... ok
+[INFO my_lib::tests] can log from the test too
+[INFO my_lib] add_one called with 2
+test tests::it_adds_one ... ok
+
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
+```
+
+Note that `env_logger::try_init()` needs to be called in each test in which you
+want to enable logging. Additionally, the default behavior of tests to
+run in parallel means that logging output may be interleaved with test output.
+Either run tests in a single thread by specifying `RUST_TEST_THREADS=1` or by
+running one test by specifying its name as an argument to the test binaries as
+directed by the `cargo test` help docs:
+
+```bash
+$ RUST_LOG=my_lib=info cargo test it_adds_one
+ Running target/debug/my_lib-...
+
+running 1 test
+[INFO my_lib::tests] can log from the test too
+[INFO my_lib] add_one called with 2
+test tests::it_adds_one ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+```
+
+## Configuring log target
+
+By default, `env_logger` logs to stderr. If you want to log to stdout instead,
+you can use the `Builder` to change the log target:
+
+```rust
+use std::env;
+use env_logger::{Builder, Target};
+
+let mut builder = Builder::from_default_env();
+builder.target(Target::Stdout);
+
+builder.init();
+```
+
+## Stability of the default format
+
+The default format won't optimise for long-term stability, and explicitly makes no guarantees about the stability of its output across major, minor or patch version bumps during `0.x`.
+
+If you want to capture or interpret the output of `env_logger` programmatically then you should use a custom format.
diff --git a/third_party/rust/env_logger/examples/custom_default_format.rs b/third_party/rust/env_logger/examples/custom_default_format.rs
new file mode 100644
index 0000000000..b04eb6028d
--- /dev/null
+++ b/third_party/rust/env_logger/examples/custom_default_format.rs
@@ -0,0 +1,40 @@
+/*!
+Disabling parts of the default format.
+
+Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
+
+```no_run,shell
+$ export MY_LOG_LEVEL='info'
+```
+
+Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors
+or `auto` to enable them:
+
+```no_run,shell
+$ export MY_LOG_STYLE=never
+```
+
+If you want to control the logging output completely, see the `custom_logger` example.
+*/
+
+#[macro_use]
+extern crate log;
+
+use env_logger::{Builder, Env};
+
+fn init_logger() {
+ let env = Env::default()
+ .filter("MY_LOG_LEVEL")
+ .write_style("MY_LOG_STYLE");
+
+ Builder::from_env(env)
+ .format_level(false)
+ .format_timestamp_nanos()
+ .init();
+}
+
+fn main() {
+ init_logger();
+
+ info!("a log from `MyLogger`");
+}
diff --git a/third_party/rust/env_logger/examples/custom_format.rs b/third_party/rust/env_logger/examples/custom_format.rs
new file mode 100644
index 0000000000..cc16b336ff
--- /dev/null
+++ b/third_party/rust/env_logger/examples/custom_format.rs
@@ -0,0 +1,54 @@
+/*!
+Changing the default logging format.
+
+Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
+
+```no_run,shell
+$ export MY_LOG_LEVEL='info'
+```
+
+Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors
+or `auto` to enable them:
+
+```no_run,shell
+$ export MY_LOG_STYLE=never
+```
+
+If you want to control the logging output completely, see the `custom_logger` example.
+*/
+
+#[cfg(all(feature = "color", feature = "humantime"))]
+fn main() {
+ use env_logger::{fmt::Color, Builder, Env};
+
+ use std::io::Write;
+
+ fn init_logger() {
+ let env = Env::default()
+ .filter("MY_LOG_LEVEL")
+ .write_style("MY_LOG_STYLE");
+
+ Builder::from_env(env)
+ .format(|buf, record| {
+ let mut style = buf.style();
+ style.set_bg(Color::Yellow).set_bold(true);
+
+ let timestamp = buf.timestamp();
+
+ writeln!(
+ buf,
+ "My formatted log ({}): {}",
+ timestamp,
+ style.value(record.args())
+ )
+ })
+ .init();
+ }
+
+ init_logger();
+
+ log::info!("a log from `MyLogger`");
+}
+
+#[cfg(not(all(feature = "color", feature = "humantime")))]
+fn main() {}
diff --git a/third_party/rust/env_logger/examples/custom_logger.rs b/third_party/rust/env_logger/examples/custom_logger.rs
new file mode 100644
index 0000000000..ca3a4a9012
--- /dev/null
+++ b/third_party/rust/env_logger/examples/custom_logger.rs
@@ -0,0 +1,62 @@
+/*!
+Using `env_logger` to drive a custom logger.
+
+Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
+
+```no_run,shell
+$ export MY_LOG_LEVEL='info'
+```
+
+If you only want to change the way logs are formatted, look at the `custom_format` example.
+*/
+
+#[macro_use]
+extern crate log;
+
+use env_logger::filter::{Builder, Filter};
+
+use log::{Log, Metadata, Record, SetLoggerError};
+
+const FILTER_ENV: &str = "MY_LOG_LEVEL";
+
+struct MyLogger {
+ inner: Filter,
+}
+
+impl MyLogger {
+ fn new() -> MyLogger {
+ let mut builder = Builder::from_env(FILTER_ENV);
+
+ MyLogger {
+ inner: builder.build(),
+ }
+ }
+
+ fn init() -> Result<(), SetLoggerError> {
+ let logger = Self::new();
+
+ log::set_max_level(logger.inner.filter());
+ log::set_boxed_logger(Box::new(logger))
+ }
+}
+
+impl Log for MyLogger {
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ self.inner.enabled(metadata)
+ }
+
+ fn log(&self, record: &Record) {
+ // Check if the record is matched by the logger before logging
+ if self.inner.matches(record) {
+ println!("{} - {}", record.level(), record.args());
+ }
+ }
+
+ fn flush(&self) {}
+}
+
+fn main() {
+ MyLogger::init().unwrap();
+
+ info!("a log from `MyLogger`");
+}
diff --git a/third_party/rust/env_logger/examples/default.rs b/third_party/rust/env_logger/examples/default.rs
new file mode 100644
index 0000000000..67bb030745
--- /dev/null
+++ b/third_party/rust/env_logger/examples/default.rs
@@ -0,0 +1,38 @@
+/*!
+Using `env_logger`.
+
+Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
+
+```no_run,shell
+$ export MY_LOG_LEVEL='info'
+```
+
+Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors
+or `auto` to enable them:
+
+```no_run,shell
+$ export MY_LOG_STYLE=never
+```
+*/
+
+#[macro_use]
+extern crate log;
+
+use env_logger::Env;
+
+fn main() {
+ // The `Env` lets us tweak what the environment
+ // variables to read are and what the default
+ // value is if they're missing
+ let env = Env::default()
+ .filter_or("MY_LOG_LEVEL", "trace")
+ .write_style_or("MY_LOG_STYLE", "always");
+
+ env_logger::init_from_env(env);
+
+ trace!("some trace log");
+ debug!("some debug log");
+ info!("some information log");
+ warn!("some warning log");
+ error!("some error log");
+}
diff --git a/third_party/rust/env_logger/examples/direct_logger.rs b/third_party/rust/env_logger/examples/direct_logger.rs
new file mode 100644
index 0000000000..4d7f39d0ae
--- /dev/null
+++ b/third_party/rust/env_logger/examples/direct_logger.rs
@@ -0,0 +1,39 @@
+/*!
+Using `env_logger::Logger` and the `log::Log` trait directly.
+
+This example doesn't rely on environment variables, or having a static logger installed.
+*/
+
+use env_logger::{Builder, WriteStyle};
+
+use log::{Level, LevelFilter, Log, MetadataBuilder, Record};
+
+fn record() -> Record<'static> {
+ let error_metadata = MetadataBuilder::new()
+ .target("myApp")
+ .level(Level::Error)
+ .build();
+
+ Record::builder()
+ .metadata(error_metadata)
+ .args(format_args!("Error!"))
+ .line(Some(433))
+ .file(Some("app.rs"))
+ .module_path(Some("server"))
+ .build()
+}
+
+fn main() {
+ let stylish_logger = Builder::new()
+ .filter(None, LevelFilter::Error)
+ .write_style(WriteStyle::Always)
+ .build();
+
+ let unstylish_logger = Builder::new()
+ .filter(None, LevelFilter::Error)
+ .write_style(WriteStyle::Never)
+ .build();
+
+ stylish_logger.log(&record());
+ unstylish_logger.log(&record());
+}
diff --git a/third_party/rust/env_logger/examples/filters_from_code.rs b/third_party/rust/env_logger/examples/filters_from_code.rs
new file mode 100644
index 0000000000..c877a44253
--- /dev/null
+++ b/third_party/rust/env_logger/examples/filters_from_code.rs
@@ -0,0 +1,20 @@
+/*!
+Specify logging filters in code instead of using an environment variable.
+*/
+
+#[macro_use]
+extern crate log;
+
+use env_logger::Builder;
+
+use log::LevelFilter;
+
+fn main() {
+ Builder::new().filter_level(LevelFilter::max()).init();
+
+ trace!("some trace log");
+ debug!("some debug log");
+ info!("some information log");
+ warn!("some warning log");
+ error!("some error log");
+}
diff --git a/third_party/rust/env_logger/examples/in_tests.rs b/third_party/rust/env_logger/examples/in_tests.rs
new file mode 100644
index 0000000000..c4487cc9c1
--- /dev/null
+++ b/third_party/rust/env_logger/examples/in_tests.rs
@@ -0,0 +1,54 @@
+/*!
+Using `env_logger` in tests.
+
+Log events will be captured by `cargo` and only printed if the test fails.
+You can run this example by calling:
+
+```text
+cargo test --example in_tests
+```
+
+You should see the `it_does_not_work` test fail and include its log output.
+*/
+
+#[cfg_attr(test, macro_use)]
+extern crate log;
+
+fn main() {}
+
+#[cfg(test)]
+mod tests {
+ fn init_logger() {
+ let _ = env_logger::builder()
+ // Include all events in tests
+ .filter_level(log::LevelFilter::max())
+ // Ensure events are captured by `cargo test`
+ .is_test(true)
+ // Ignore errors initializing the logger if tests race to configure it
+ .try_init();
+ }
+
+ #[test]
+ fn it_works() {
+ init_logger();
+
+ let a = 1;
+ let b = 2;
+
+ debug!("checking whether {} + {} = 3", a, b);
+
+ assert_eq!(3, a + b);
+ }
+
+ #[test]
+ fn it_does_not_work() {
+ init_logger();
+
+ let a = 1;
+ let b = 2;
+
+ debug!("checking whether {} + {} = 6", a, b);
+
+ assert_eq!(6, a + b);
+ }
+}
diff --git a/third_party/rust/env_logger/examples/syslog_friendly_format.rs b/third_party/rust/env_logger/examples/syslog_friendly_format.rs
new file mode 100644
index 0000000000..9809ab3f87
--- /dev/null
+++ b/third_party/rust/env_logger/examples/syslog_friendly_format.rs
@@ -0,0 +1,24 @@
+use std::io::Write;
+
+fn main() {
+ match std::env::var("RUST_LOG_STYLE") {
+ Ok(s) if s == "SYSTEMD" => env_logger::builder()
+ .format(|buf, record| {
+ writeln!(
+ buf,
+ "<{}>{}: {}",
+ match record.level() {
+ log::Level::Error => 3,
+ log::Level::Warn => 4,
+ log::Level::Info => 6,
+ log::Level::Debug => 7,
+ log::Level::Trace => 7,
+ },
+ record.target(),
+ record.args()
+ )
+ })
+ .init(),
+ _ => env_logger::init(),
+ };
+}
diff --git a/third_party/rust/env_logger/src/filter/mod.rs b/third_party/rust/env_logger/src/filter/mod.rs
new file mode 100644
index 0000000000..721dcb4634
--- /dev/null
+++ b/third_party/rust/env_logger/src/filter/mod.rs
@@ -0,0 +1,868 @@
+//! Filtering for log records.
+//!
+//! This module contains the log filtering used by `env_logger` to match records.
+//! You can use the `Filter` type in your own logger implementation to use the same
+//! filter parsing and matching as `env_logger`. For more details about the format
+//! for directive strings see [Enabling Logging].
+//!
+//! ## Using `env_logger` in your own logger
+//!
+//! You can use `env_logger`'s filtering functionality with your own logger.
+//! Call [`Builder::parse`] to parse directives from a string when constructing
+//! your logger. Call [`Filter::matches`] to check whether a record should be
+//! logged based on the parsed filters when log records are received.
+//!
+//! ```
+//! extern crate log;
+//! extern crate env_logger;
+//! use env_logger::filter::Filter;
+//! use log::{Log, Metadata, Record};
+//!
+//! struct MyLogger {
+//! filter: Filter
+//! }
+//!
+//! impl MyLogger {
+//! fn new() -> MyLogger {
+//! use env_logger::filter::Builder;
+//! let mut builder = Builder::new();
+//!
+//! // Parse a directives string from an environment variable
+//! if let Ok(ref filter) = std::env::var("MY_LOG_LEVEL") {
+//! builder.parse(filter);
+//! }
+//!
+//! MyLogger {
+//! filter: builder.build()
+//! }
+//! }
+//! }
+//!
+//! impl Log for MyLogger {
+//! fn enabled(&self, metadata: &Metadata) -> bool {
+//! self.filter.enabled(metadata)
+//! }
+//!
+//! fn log(&self, record: &Record) {
+//! // Check if the record is matched by the filter
+//! if self.filter.matches(record) {
+//! println!("{:?}", record);
+//! }
+//! }
+//!
+//! fn flush(&self) {}
+//! }
+//! ```
+//!
+//! [Enabling Logging]: ../index.html#enabling-logging
+//! [`Builder::parse`]: struct.Builder.html#method.parse
+//! [`Filter::matches`]: struct.Filter.html#method.matches
+
+use log::{Level, LevelFilter, Metadata, Record};
+use std::collections::HashMap;
+use std::env;
+use std::fmt;
+use std::mem;
+
+#[cfg(feature = "regex")]
+#[path = "regex.rs"]
+mod inner;
+
+#[cfg(not(feature = "regex"))]
+#[path = "string.rs"]
+mod inner;
+
+/// A log filter.
+///
+/// This struct can be used to determine whether or not a log record
+/// should be written to the output.
+/// Use the [`Builder`] type to parse and construct a `Filter`.
+///
+/// [`Builder`]: struct.Builder.html
+pub struct Filter {
+ directives: Vec<Directive>,
+ filter: Option<inner::Filter>,
+}
+
+/// A builder for a log filter.
+///
+/// It can be used to parse a set of directives from a string before building
+/// a [`Filter`] instance.
+///
+/// ## Example
+///
+/// ```
+/// # #[macro_use] extern crate log;
+/// # use std::env;
+/// use env_logger::filter::Builder;
+///
+/// let mut builder = Builder::new();
+///
+/// // Parse a logging filter from an environment variable.
+/// if let Ok(rust_log) = env::var("RUST_LOG") {
+/// builder.parse(&rust_log);
+/// }
+///
+/// let filter = builder.build();
+/// ```
+///
+/// [`Filter`]: struct.Filter.html
+pub struct Builder {
+ directives: HashMap<Option<String>, LevelFilter>,
+ filter: Option<inner::Filter>,
+ built: bool,
+}
+
+#[derive(Debug)]
+struct Directive {
+ name: Option<String>,
+ level: LevelFilter,
+}
+
+impl Filter {
+ /// Returns the maximum `LevelFilter` that this filter instance is
+ /// configured to output.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use log::LevelFilter;
+ /// use env_logger::filter::Builder;
+ ///
+ /// let mut builder = Builder::new();
+ /// builder.filter(Some("module1"), LevelFilter::Info);
+ /// builder.filter(Some("module2"), LevelFilter::Error);
+ ///
+ /// let filter = builder.build();
+ /// assert_eq!(filter.filter(), LevelFilter::Info);
+ /// ```
+ pub fn filter(&self) -> LevelFilter {
+ self.directives
+ .iter()
+ .map(|d| d.level)
+ .max()
+ .unwrap_or(LevelFilter::Off)
+ }
+
+ /// Checks if this record matches the configured filter.
+ pub fn matches(&self, record: &Record) -> bool {
+ if !self.enabled(record.metadata()) {
+ return false;
+ }
+
+ if let Some(filter) = self.filter.as_ref() {
+ if !filter.is_match(&record.args().to_string()) {
+ return false;
+ }
+ }
+
+ true
+ }
+
+ /// Determines if a log message with the specified metadata would be logged.
+ pub fn enabled(&self, metadata: &Metadata) -> bool {
+ let level = metadata.level();
+ let target = metadata.target();
+
+ enabled(&self.directives, level, target)
+ }
+}
+
+impl Builder {
+ /// Initializes the filter builder with defaults.
+ pub fn new() -> Builder {
+ Builder {
+ directives: HashMap::new(),
+ filter: None,
+ built: false,
+ }
+ }
+
+ /// Initializes the filter builder from an environment.
+ pub fn from_env(env: &str) -> Builder {
+ let mut builder = Builder::new();
+
+ if let Ok(s) = env::var(env) {
+ builder.parse(&s);
+ }
+
+ builder
+ }
+
+ /// Adds a directive to the filter for a specific module.
+ pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
+ self.filter(Some(module), level)
+ }
+
+ /// Adds a directive to the filter for all modules.
+ pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
+ self.filter(None, level)
+ }
+
+ /// Adds a directive to the filter.
+ ///
+ /// The given module (if any) will log at most the specified level provided.
+ /// If no module is provided then the filter will apply to all log messages.
+ pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
+ self.directives.insert(module.map(|s| s.to_string()), level);
+ self
+ }
+
+ /// Parses the directives string.
+ ///
+ /// See the [Enabling Logging] section for more details.
+ ///
+ /// [Enabling Logging]: ../index.html#enabling-logging
+ pub fn parse(&mut self, filters: &str) -> &mut Self {
+ let (directives, filter) = parse_spec(filters);
+
+ self.filter = filter;
+
+ for directive in directives {
+ self.directives.insert(directive.name, directive.level);
+ }
+ self
+ }
+
+ /// Build a log filter.
+ pub fn build(&mut self) -> Filter {
+ assert!(!self.built, "attempt to re-use consumed builder");
+ self.built = true;
+
+ let mut directives = Vec::new();
+ if self.directives.is_empty() {
+ // Adds the default filter if none exist
+ directives.push(Directive {
+ name: None,
+ level: LevelFilter::Error,
+ });
+ } else {
+ // Consume map of directives.
+ let directives_map = mem::take(&mut self.directives);
+ directives = directives_map
+ .into_iter()
+ .map(|(name, level)| Directive { name, level })
+ .collect();
+ // Sort the directives by length of their name, this allows a
+ // little more efficient lookup at runtime.
+ directives.sort_by(|a, b| {
+ let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
+ let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
+ alen.cmp(&blen)
+ });
+ }
+
+ Filter {
+ directives: mem::take(&mut directives),
+ filter: mem::replace(&mut self.filter, None),
+ }
+ }
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Builder::new()
+ }
+}
+
+impl fmt::Debug for Filter {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Filter")
+ .field("filter", &self.filter)
+ .field("directives", &self.directives)
+ .finish()
+ }
+}
+
+impl fmt::Debug for Builder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.built {
+ f.debug_struct("Filter").field("built", &true).finish()
+ } else {
+ f.debug_struct("Filter")
+ .field("filter", &self.filter)
+ .field("directives", &self.directives)
+ .finish()
+ }
+ }
+}
+
+/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
+/// and return a vector with log directives.
+fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
+ let mut dirs = Vec::new();
+
+ let mut parts = spec.split('/');
+ let mods = parts.next();
+ let filter = parts.next();
+ if parts.next().is_some() {
+ eprintln!(
+ "warning: invalid logging spec '{}', \
+ ignoring it (too many '/'s)",
+ spec
+ );
+ return (dirs, None);
+ }
+ if let Some(m) = mods {
+ for s in m.split(',').map(|ss| ss.trim()) {
+ if s.is_empty() {
+ continue;
+ }
+ let mut parts = s.split('=');
+ let (log_level, name) =
+ match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
+ (Some(part0), None, None) => {
+ // if the single argument is a log-level string or number,
+ // treat that as a global fallback
+ match part0.parse() {
+ Ok(num) => (num, None),
+ Err(_) => (LevelFilter::max(), Some(part0)),
+ }
+ }
+ (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
+ (Some(part0), Some(part1), None) => match part1.parse() {
+ Ok(num) => (num, Some(part0)),
+ _ => {
+ eprintln!(
+ "warning: invalid logging spec '{}', \
+ ignoring it",
+ part1
+ );
+ continue;
+ }
+ },
+ _ => {
+ eprintln!(
+ "warning: invalid logging spec '{}', \
+ ignoring it",
+ s
+ );
+ continue;
+ }
+ };
+ dirs.push(Directive {
+ name: name.map(|s| s.to_string()),
+ level: log_level,
+ });
+ }
+ }
+
+ let filter = filter.and_then(|filter| match inner::Filter::new(filter) {
+ Ok(re) => Some(re),
+ Err(e) => {
+ eprintln!("warning: invalid regex filter - {}", e);
+ None
+ }
+ });
+
+ (dirs, filter)
+}
+
+// Check whether a level and target are enabled by the set of directives.
+fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
+ // Search for the longest match, the vector is assumed to be pre-sorted.
+ for directive in directives.iter().rev() {
+ match directive.name {
+ Some(ref name) if !target.starts_with(&**name) => {}
+ Some(..) | None => return level <= directive.level,
+ }
+ }
+ false
+}
+
+#[cfg(test)]
+mod tests {
+ use log::{Level, LevelFilter};
+
+ use super::{enabled, parse_spec, Builder, Directive, Filter};
+
+ fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
+ let mut logger = Builder::new().build();
+ logger.directives = dirs;
+ logger
+ }
+
+ #[test]
+ fn filter_info() {
+ let logger = Builder::new().filter(None, LevelFilter::Info).build();
+ assert!(enabled(&logger.directives, Level::Info, "crate1"));
+ assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
+ }
+
+ #[test]
+ fn filter_beginning_longest_match() {
+ let logger = Builder::new()
+ .filter(Some("crate2"), LevelFilter::Info)
+ .filter(Some("crate2::mod"), LevelFilter::Debug)
+ .filter(Some("crate1::mod1"), LevelFilter::Warn)
+ .build();
+ assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
+ assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
+ }
+
+ // Some of our tests are only correct or complete when they cover the full
+ // universe of variants for log::Level. In the unlikely event that a new
+ // variant is added in the future, this test will detect the scenario and
+ // alert us to the need to review and update the tests. In such a
+ // situation, this test will fail to compile, and the error message will
+ // look something like this:
+ //
+ // error[E0004]: non-exhaustive patterns: `NewVariant` not covered
+ // --> src/filter/mod.rs:413:15
+ // |
+ // 413 | match level_universe {
+ // | ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
+ #[test]
+ fn ensure_tests_cover_level_universe() {
+ let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
+ match level_universe {
+ Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
+ }
+ }
+
+ #[test]
+ fn parse_default() {
+ let logger = Builder::new().parse("info,crate1::mod1=warn").build();
+ assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
+ assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
+ }
+
+ #[test]
+ fn parse_default_bare_level_off_lc() {
+ let logger = Builder::new().parse("off").build();
+ assert!(!enabled(&logger.directives, Level::Error, ""));
+ assert!(!enabled(&logger.directives, Level::Warn, ""));
+ assert!(!enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_off_uc() {
+ let logger = Builder::new().parse("OFF").build();
+ assert!(!enabled(&logger.directives, Level::Error, ""));
+ assert!(!enabled(&logger.directives, Level::Warn, ""));
+ assert!(!enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_error_lc() {
+ let logger = Builder::new().parse("error").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(!enabled(&logger.directives, Level::Warn, ""));
+ assert!(!enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_error_uc() {
+ let logger = Builder::new().parse("ERROR").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(!enabled(&logger.directives, Level::Warn, ""));
+ assert!(!enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_warn_lc() {
+ let logger = Builder::new().parse("warn").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(!enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_warn_uc() {
+ let logger = Builder::new().parse("WARN").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(!enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_info_lc() {
+ let logger = Builder::new().parse("info").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_info_uc() {
+ let logger = Builder::new().parse("INFO").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(!enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_debug_lc() {
+ let logger = Builder::new().parse("debug").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_debug_uc() {
+ let logger = Builder::new().parse("DEBUG").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_trace_lc() {
+ let logger = Builder::new().parse("trace").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ #[test]
+ fn parse_default_bare_level_trace_uc() {
+ let logger = Builder::new().parse("TRACE").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(enabled(&logger.directives, Level::Trace, ""));
+ }
+
+ // In practice, the desired log level is typically specified by a token
+ // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
+ // 'TRACE'), but this tests serves as a reminder that
+ // log::Level::from_str() ignores all case variants.
+ #[test]
+ fn parse_default_bare_level_debug_mixed() {
+ {
+ let logger = Builder::new().parse("Debug").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+ {
+ let logger = Builder::new().parse("debuG").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+ {
+ let logger = Builder::new().parse("deBug").build();
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+ {
+ let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
+ assert!(enabled(&logger.directives, Level::Error, ""));
+ assert!(enabled(&logger.directives, Level::Warn, ""));
+ assert!(enabled(&logger.directives, Level::Info, ""));
+ assert!(enabled(&logger.directives, Level::Debug, ""));
+ assert!(!enabled(&logger.directives, Level::Trace, ""));
+ }
+ }
+
+ #[test]
+ fn match_full_path() {
+ let logger = make_logger_filter(vec![
+ Directive {
+ name: Some("crate2".to_string()),
+ level: LevelFilter::Info,
+ },
+ Directive {
+ name: Some("crate1::mod1".to_string()),
+ level: LevelFilter::Warn,
+ },
+ ]);
+ assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
+ assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
+ assert!(enabled(&logger.directives, Level::Info, "crate2"));
+ assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
+ }
+
+ #[test]
+ fn no_match() {
+ let logger = make_logger_filter(vec![
+ Directive {
+ name: Some("crate2".to_string()),
+ level: LevelFilter::Info,
+ },
+ Directive {
+ name: Some("crate1::mod1".to_string()),
+ level: LevelFilter::Warn,
+ },
+ ]);
+ assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
+ }
+
+ #[test]
+ fn match_beginning() {
+ let logger = make_logger_filter(vec![
+ Directive {
+ name: Some("crate2".to_string()),
+ level: LevelFilter::Info,
+ },
+ Directive {
+ name: Some("crate1::mod1".to_string()),
+ level: LevelFilter::Warn,
+ },
+ ]);
+ assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
+ }
+
+ #[test]
+ fn match_beginning_longest_match() {
+ let logger = make_logger_filter(vec![
+ Directive {
+ name: Some("crate2".to_string()),
+ level: LevelFilter::Info,
+ },
+ Directive {
+ name: Some("crate2::mod".to_string()),
+ level: LevelFilter::Debug,
+ },
+ Directive {
+ name: Some("crate1::mod1".to_string()),
+ level: LevelFilter::Warn,
+ },
+ ]);
+ assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
+ assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
+ }
+
+ #[test]
+ fn match_default() {
+ let logger = make_logger_filter(vec![
+ Directive {
+ name: None,
+ level: LevelFilter::Info,
+ },
+ Directive {
+ name: Some("crate1::mod1".to_string()),
+ level: LevelFilter::Warn,
+ },
+ ]);
+ assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
+ assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
+ }
+
+ #[test]
+ fn zero_level() {
+ let logger = make_logger_filter(vec![
+ Directive {
+ name: None,
+ level: LevelFilter::Info,
+ },
+ Directive {
+ name: Some("crate1::mod1".to_string()),
+ level: LevelFilter::Off,
+ },
+ ]);
+ assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
+ assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
+ }
+
+ #[test]
+ fn parse_spec_valid() {
+ let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
+ assert_eq!(dirs.len(), 3);
+ assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::Error);
+
+ assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
+ assert_eq!(dirs[1].level, LevelFilter::max());
+
+ assert_eq!(dirs[2].name, Some("crate2".to_string()));
+ assert_eq!(dirs[2].level, LevelFilter::Debug);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_invalid_crate() {
+ // test parse_spec with multiple = in specification
+ let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, Some("crate2".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::Debug);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_invalid_level() {
+ // test parse_spec with 'noNumber' as log level
+ let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, Some("crate2".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::Debug);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_string_level() {
+ // test parse_spec with 'warn' as log level
+ let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, Some("crate2".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::Warn);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_empty_level() {
+ // test parse_spec with '' as log level
+ let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, Some("crate2".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::max());
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_empty_level_isolated() {
+ // test parse_spec with "" as log level (and the entire spec str)
+ let (dirs, filter) = parse_spec(""); // should be ignored
+ assert_eq!(dirs.len(), 0);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_blank_level_isolated() {
+ // test parse_spec with a white-space-only string specified as the log
+ // level (and the entire spec str)
+ let (dirs, filter) = parse_spec(" "); // should be ignored
+ assert_eq!(dirs.len(), 0);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_blank_level_isolated_comma_only() {
+ // The spec should contain zero or more comma-separated string slices,
+ // so a comma-only string should be interpreted as two empty strings
+ // (which should both be treated as invalid, so ignored).
+ let (dirs, filter) = parse_spec(","); // should be ignored
+ assert_eq!(dirs.len(), 0);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_blank_level_isolated_comma_blank() {
+ // The spec should contain zero or more comma-separated string slices,
+ // so this bogus spec should be interpreted as containing one empty
+ // string and one blank string. Both should both be treated as
+ // invalid, so ignored.
+ let (dirs, filter) = parse_spec(", "); // should be ignored
+ assert_eq!(dirs.len(), 0);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_blank_level_isolated_blank_comma() {
+ // The spec should contain zero or more comma-separated string slices,
+ // so this bogus spec should be interpreted as containing one blank
+ // string and one empty string. Both should both be treated as
+ // invalid, so ignored.
+ let (dirs, filter) = parse_spec(" ,"); // should be ignored
+ assert_eq!(dirs.len(), 0);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_global() {
+ // test parse_spec with no crate
+ let (dirs, filter) = parse_spec("warn,crate2=debug");
+ assert_eq!(dirs.len(), 2);
+ assert_eq!(dirs[0].name, None);
+ assert_eq!(dirs[0].level, LevelFilter::Warn);
+ assert_eq!(dirs[1].name, Some("crate2".to_string()));
+ assert_eq!(dirs[1].level, LevelFilter::Debug);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_global_bare_warn_lc() {
+ // test parse_spec with no crate, in isolation, all lowercase
+ let (dirs, filter) = parse_spec("warn");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, None);
+ assert_eq!(dirs[0].level, LevelFilter::Warn);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_global_bare_warn_uc() {
+ // test parse_spec with no crate, in isolation, all uppercase
+ let (dirs, filter) = parse_spec("WARN");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, None);
+ assert_eq!(dirs[0].level, LevelFilter::Warn);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_global_bare_warn_mixed() {
+ // test parse_spec with no crate, in isolation, mixed case
+ let (dirs, filter) = parse_spec("wArN");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, None);
+ assert_eq!(dirs[0].level, LevelFilter::Warn);
+ assert!(filter.is_none());
+ }
+
+ #[test]
+ fn parse_spec_valid_filter() {
+ let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
+ assert_eq!(dirs.len(), 3);
+ assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::Error);
+
+ assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
+ assert_eq!(dirs[1].level, LevelFilter::max());
+
+ assert_eq!(dirs[2].name, Some("crate2".to_string()));
+ assert_eq!(dirs[2].level, LevelFilter::Debug);
+ assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
+ }
+
+ #[test]
+ fn parse_spec_invalid_crate_filter() {
+ let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, Some("crate2".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::Debug);
+ assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
+ }
+
+ #[test]
+ fn parse_spec_empty_with_filter() {
+ let (dirs, filter) = parse_spec("crate1/a*c");
+ assert_eq!(dirs.len(), 1);
+ assert_eq!(dirs[0].name, Some("crate1".to_string()));
+ assert_eq!(dirs[0].level, LevelFilter::max());
+ assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
+ }
+}
diff --git a/third_party/rust/env_logger/src/filter/regex.rs b/third_party/rust/env_logger/src/filter/regex.rs
new file mode 100644
index 0000000000..fb21528a12
--- /dev/null
+++ b/third_party/rust/env_logger/src/filter/regex.rs
@@ -0,0 +1,29 @@
+extern crate regex;
+
+use std::fmt;
+
+use self::regex::Regex;
+
+#[derive(Debug)]
+pub struct Filter {
+ inner: Regex,
+}
+
+impl Filter {
+ pub fn new(spec: &str) -> Result<Filter, String> {
+ match Regex::new(spec) {
+ Ok(r) => Ok(Filter { inner: r }),
+ Err(e) => Err(e.to_string()),
+ }
+ }
+
+ pub fn is_match(&self, s: &str) -> bool {
+ self.inner.is_match(s)
+ }
+}
+
+impl fmt::Display for Filter {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.inner.fmt(f)
+ }
+}
diff --git a/third_party/rust/env_logger/src/filter/string.rs b/third_party/rust/env_logger/src/filter/string.rs
new file mode 100644
index 0000000000..ea476e42f9
--- /dev/null
+++ b/third_party/rust/env_logger/src/filter/string.rs
@@ -0,0 +1,24 @@
+use std::fmt;
+
+#[derive(Debug)]
+pub struct Filter {
+ inner: String,
+}
+
+impl Filter {
+ pub fn new(spec: &str) -> Result<Filter, String> {
+ Ok(Filter {
+ inner: spec.to_string(),
+ })
+ }
+
+ pub fn is_match(&self, s: &str) -> bool {
+ s.contains(&self.inner)
+ }
+}
+
+impl fmt::Display for Filter {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.inner.fmt(f)
+ }
+}
diff --git a/third_party/rust/env_logger/src/fmt/humantime/extern_impl.rs b/third_party/rust/env_logger/src/fmt/humantime/extern_impl.rs
new file mode 100644
index 0000000000..bdf165c4eb
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/humantime/extern_impl.rs
@@ -0,0 +1,118 @@
+use std::fmt;
+use std::time::SystemTime;
+
+use humantime::{
+ format_rfc3339_micros, format_rfc3339_millis, format_rfc3339_nanos, format_rfc3339_seconds,
+};
+
+use crate::fmt::{Formatter, TimestampPrecision};
+
+pub(in crate::fmt) mod glob {
+ pub use super::*;
+}
+
+impl Formatter {
+ /// Get a [`Timestamp`] for the current date and time in UTC.
+ ///
+ /// # Examples
+ ///
+ /// Include the current timestamp with the log record:
+ ///
+ /// ```
+ /// use std::io::Write;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let ts = buf.timestamp();
+ ///
+ /// writeln!(buf, "{}: {}: {}", ts, record.level(), record.args())
+ /// });
+ /// ```
+ ///
+ /// [`Timestamp`]: struct.Timestamp.html
+ pub fn timestamp(&self) -> Timestamp {
+ Timestamp {
+ time: SystemTime::now(),
+ precision: TimestampPrecision::Seconds,
+ }
+ }
+
+ /// Get a [`Timestamp`] for the current date and time in UTC with full
+ /// second precision.
+ pub fn timestamp_seconds(&self) -> Timestamp {
+ Timestamp {
+ time: SystemTime::now(),
+ precision: TimestampPrecision::Seconds,
+ }
+ }
+
+ /// Get a [`Timestamp`] for the current date and time in UTC with
+ /// millisecond precision.
+ pub fn timestamp_millis(&self) -> Timestamp {
+ Timestamp {
+ time: SystemTime::now(),
+ precision: TimestampPrecision::Millis,
+ }
+ }
+
+ /// Get a [`Timestamp`] for the current date and time in UTC with
+ /// microsecond precision.
+ pub fn timestamp_micros(&self) -> Timestamp {
+ Timestamp {
+ time: SystemTime::now(),
+ precision: TimestampPrecision::Micros,
+ }
+ }
+
+ /// Get a [`Timestamp`] for the current date and time in UTC with
+ /// nanosecond precision.
+ pub fn timestamp_nanos(&self) -> Timestamp {
+ Timestamp {
+ time: SystemTime::now(),
+ precision: TimestampPrecision::Nanos,
+ }
+ }
+}
+
+/// An [RFC3339] formatted timestamp.
+///
+/// The timestamp implements [`Display`] and can be written to a [`Formatter`].
+///
+/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt
+/// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
+/// [`Formatter`]: struct.Formatter.html
+pub struct Timestamp {
+ time: SystemTime,
+ precision: TimestampPrecision,
+}
+
+impl fmt::Debug for Timestamp {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ /// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation.
+ struct TimestampValue<'a>(&'a Timestamp);
+
+ impl<'a> fmt::Debug for TimestampValue<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+ }
+
+ f.debug_tuple("Timestamp")
+ .field(&TimestampValue(self))
+ .finish()
+ }
+}
+
+impl fmt::Display for Timestamp {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let formatter = match self.precision {
+ TimestampPrecision::Seconds => format_rfc3339_seconds,
+ TimestampPrecision::Millis => format_rfc3339_millis,
+ TimestampPrecision::Micros => format_rfc3339_micros,
+ TimestampPrecision::Nanos => format_rfc3339_nanos,
+ };
+
+ formatter(self.time).fmt(f)
+ }
+}
diff --git a/third_party/rust/env_logger/src/fmt/humantime/mod.rs b/third_party/rust/env_logger/src/fmt/humantime/mod.rs
new file mode 100644
index 0000000000..ac23ae2493
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/humantime/mod.rs
@@ -0,0 +1,11 @@
+/*
+This internal module contains the timestamp implementation.
+
+Its public API is available when the `humantime` crate is available.
+*/
+
+#[cfg_attr(feature = "humantime", path = "extern_impl.rs")]
+#[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")]
+mod imp;
+
+pub(in crate::fmt) use self::imp::*;
diff --git a/third_party/rust/env_logger/src/fmt/humantime/shim_impl.rs b/third_party/rust/env_logger/src/fmt/humantime/shim_impl.rs
new file mode 100644
index 0000000000..906bf9e4c1
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/humantime/shim_impl.rs
@@ -0,0 +1,5 @@
+/*
+Timestamps aren't available when we don't have a `humantime` dependency.
+*/
+
+pub(in crate::fmt) mod glob {}
diff --git a/third_party/rust/env_logger/src/fmt/mod.rs b/third_party/rust/env_logger/src/fmt/mod.rs
new file mode 100644
index 0000000000..86c093f0e0
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/mod.rs
@@ -0,0 +1,652 @@
+//! Formatting for log records.
+//!
+//! This module contains a [`Formatter`] that can be used to format log records
+//! into without needing temporary allocations. Usually you won't need to worry
+//! about the contents of this module and can use the `Formatter` like an ordinary
+//! [`Write`].
+//!
+//! # Formatting log records
+//!
+//! The format used to print log records can be customised using the [`Builder::format`]
+//! method.
+//! Custom formats can apply different color and weight to printed values using
+//! [`Style`] builders.
+//!
+//! ```
+//! use std::io::Write;
+//!
+//! let mut builder = env_logger::Builder::new();
+//!
+//! builder.format(|buf, record| {
+//! writeln!(buf, "{}: {}",
+//! record.level(),
+//! record.args())
+//! });
+//! ```
+//!
+//! [`Formatter`]: struct.Formatter.html
+//! [`Style`]: struct.Style.html
+//! [`Builder::format`]: ../struct.Builder.html#method.format
+//! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
+
+use std::cell::RefCell;
+use std::fmt::Display;
+use std::io::prelude::*;
+use std::rc::Rc;
+use std::{fmt, io, mem};
+
+use log::Record;
+
+mod humantime;
+pub(crate) mod writer;
+
+pub use self::humantime::glob::*;
+pub use self::writer::glob::*;
+
+use self::writer::{Buffer, Writer};
+
+pub(crate) mod glob {
+ pub use super::{Target, TimestampPrecision, WriteStyle};
+}
+
+/// Formatting precision of timestamps.
+///
+/// Seconds give precision of full seconds, milliseconds give thousands of a
+/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
+/// digits) and nanoseconds are billionth of a second (9 decimal digits).
+#[derive(Copy, Clone, Debug)]
+pub enum TimestampPrecision {
+ /// Full second precision (0 decimal digits)
+ Seconds,
+ /// Millisecond precision (3 decimal digits)
+ Millis,
+ /// Microsecond precision (6 decimal digits)
+ Micros,
+ /// Nanosecond precision (9 decimal digits)
+ Nanos,
+}
+
+/// The default timestamp precision is seconds.
+impl Default for TimestampPrecision {
+ fn default() -> Self {
+ TimestampPrecision::Seconds
+ }
+}
+
+/// A formatter to write logs into.
+///
+/// `Formatter` implements the standard [`Write`] trait for writing log records.
+/// It also supports terminal colors, through the [`style`] method.
+///
+/// # Examples
+///
+/// Use the [`writeln`] macro to format a log record.
+/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
+///
+/// ```
+/// use std::io::Write;
+///
+/// let mut builder = env_logger::Builder::new();
+///
+/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
+/// ```
+///
+/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
+/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html
+/// [`style`]: #method.style
+pub struct Formatter {
+ buf: Rc<RefCell<Buffer>>,
+ write_style: WriteStyle,
+}
+
+impl Formatter {
+ pub(crate) fn new(writer: &Writer) -> Self {
+ Formatter {
+ buf: Rc::new(RefCell::new(writer.buffer())),
+ write_style: writer.write_style(),
+ }
+ }
+
+ pub(crate) fn write_style(&self) -> WriteStyle {
+ self.write_style
+ }
+
+ pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
+ writer.print(&self.buf.borrow())
+ }
+
+ pub(crate) fn clear(&mut self) {
+ self.buf.borrow_mut().clear()
+ }
+}
+
+impl Write for Formatter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.buf.borrow_mut().write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.buf.borrow_mut().flush()
+ }
+}
+
+impl fmt::Debug for Formatter {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Formatter").finish()
+ }
+}
+
+pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
+
+pub(crate) struct Builder {
+ pub format_timestamp: Option<TimestampPrecision>,
+ pub format_module_path: bool,
+ pub format_target: bool,
+ pub format_level: bool,
+ pub format_indent: Option<usize>,
+ pub custom_format: Option<FormatFn>,
+ pub format_suffix: &'static str,
+ built: bool,
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Builder {
+ format_timestamp: Some(Default::default()),
+ format_module_path: false,
+ format_target: true,
+ format_level: true,
+ format_indent: Some(4),
+ custom_format: None,
+ format_suffix: "\n",
+ built: false,
+ }
+ }
+}
+
+impl Builder {
+ /// Convert the format into a callable function.
+ ///
+ /// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
+ /// If the `custom_format` is `None`, then a default format is returned.
+ /// Any `default_format` switches set to `false` won't be written by the format.
+ pub fn build(&mut self) -> FormatFn {
+ assert!(!self.built, "attempt to re-use consumed builder");
+
+ let built = mem::replace(
+ self,
+ Builder {
+ built: true,
+ ..Default::default()
+ },
+ );
+
+ if let Some(fmt) = built.custom_format {
+ fmt
+ } else {
+ Box::new(move |buf, record| {
+ let fmt = DefaultFormat {
+ timestamp: built.format_timestamp,
+ module_path: built.format_module_path,
+ target: built.format_target,
+ level: built.format_level,
+ written_header_value: false,
+ indent: built.format_indent,
+ suffix: built.format_suffix,
+ buf,
+ };
+
+ fmt.write(record)
+ })
+ }
+ }
+}
+
+#[cfg(feature = "color")]
+type SubtleStyle = StyledValue<'static, &'static str>;
+#[cfg(not(feature = "color"))]
+type SubtleStyle = &'static str;
+
+/// The default format.
+///
+/// This format needs to work with any combination of crate features.
+struct DefaultFormat<'a> {
+ timestamp: Option<TimestampPrecision>,
+ module_path: bool,
+ target: bool,
+ level: bool,
+ written_header_value: bool,
+ indent: Option<usize>,
+ buf: &'a mut Formatter,
+ suffix: &'a str,
+}
+
+impl<'a> DefaultFormat<'a> {
+ fn write(mut self, record: &Record) -> io::Result<()> {
+ self.write_timestamp()?;
+ self.write_level(record)?;
+ self.write_module_path(record)?;
+ self.write_target(record)?;
+ self.finish_header()?;
+
+ self.write_args(record)
+ }
+
+ fn subtle_style(&self, text: &'static str) -> SubtleStyle {
+ #[cfg(feature = "color")]
+ {
+ self.buf
+ .style()
+ .set_color(Color::Black)
+ .set_intense(true)
+ .clone()
+ .into_value(text)
+ }
+ #[cfg(not(feature = "color"))]
+ {
+ text
+ }
+ }
+
+ fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
+ where
+ T: Display,
+ {
+ if !self.written_header_value {
+ self.written_header_value = true;
+
+ let open_brace = self.subtle_style("[");
+ write!(self.buf, "{}{}", open_brace, value)
+ } else {
+ write!(self.buf, " {}", value)
+ }
+ }
+
+ fn write_level(&mut self, record: &Record) -> io::Result<()> {
+ if !self.level {
+ return Ok(());
+ }
+
+ let level = {
+ #[cfg(feature = "color")]
+ {
+ self.buf.default_styled_level(record.level())
+ }
+ #[cfg(not(feature = "color"))]
+ {
+ record.level()
+ }
+ };
+
+ self.write_header_value(format_args!("{:<5}", level))
+ }
+
+ fn write_timestamp(&mut self) -> io::Result<()> {
+ #[cfg(feature = "humantime")]
+ {
+ use self::TimestampPrecision::*;
+ let ts = match self.timestamp {
+ None => return Ok(()),
+ Some(Seconds) => self.buf.timestamp_seconds(),
+ Some(Millis) => self.buf.timestamp_millis(),
+ Some(Micros) => self.buf.timestamp_micros(),
+ Some(Nanos) => self.buf.timestamp_nanos(),
+ };
+
+ self.write_header_value(ts)
+ }
+ #[cfg(not(feature = "humantime"))]
+ {
+ // Trick the compiler to think we have used self.timestamp
+ // Workaround for "field is never used: `timestamp`" compiler nag.
+ let _ = self.timestamp;
+ Ok(())
+ }
+ }
+
+ fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
+ if !self.module_path {
+ return Ok(());
+ }
+
+ if let Some(module_path) = record.module_path() {
+ self.write_header_value(module_path)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn write_target(&mut self, record: &Record) -> io::Result<()> {
+ if !self.target {
+ return Ok(());
+ }
+
+ match record.target() {
+ "" => Ok(()),
+ target => self.write_header_value(target),
+ }
+ }
+
+ fn finish_header(&mut self) -> io::Result<()> {
+ if self.written_header_value {
+ let close_brace = self.subtle_style("]");
+ write!(self.buf, "{} ", close_brace)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn write_args(&mut self, record: &Record) -> io::Result<()> {
+ match self.indent {
+ // Fast path for no indentation
+ None => write!(self.buf, "{}{}", record.args(), self.suffix),
+
+ Some(indent_count) => {
+ // Create a wrapper around the buffer only if we have to actually indent the message
+
+ struct IndentWrapper<'a, 'b: 'a> {
+ fmt: &'a mut DefaultFormat<'b>,
+ indent_count: usize,
+ }
+
+ impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let mut first = true;
+ for chunk in buf.split(|&x| x == b'\n') {
+ if !first {
+ write!(
+ self.fmt.buf,
+ "{}{:width$}",
+ self.fmt.suffix,
+ "",
+ width = self.indent_count
+ )?;
+ }
+ self.fmt.buf.write_all(chunk)?;
+ first = false;
+ }
+
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.fmt.buf.flush()
+ }
+ }
+
+ // The explicit scope here is just to make older versions of Rust happy
+ {
+ let mut wrapper = IndentWrapper {
+ fmt: self,
+ indent_count,
+ };
+ write!(wrapper, "{}", record.args())?;
+ }
+
+ write!(self.buf, "{}", self.suffix)?;
+
+ Ok(())
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use log::{Level, Record};
+
+ fn write_record(record: Record, fmt: DefaultFormat) -> String {
+ let buf = fmt.buf.buf.clone();
+
+ fmt.write(&record).expect("failed to write record");
+
+ let buf = buf.borrow();
+ String::from_utf8(buf.bytes().to_vec()).expect("failed to read record")
+ }
+
+ fn write_target(target: &str, fmt: DefaultFormat) -> String {
+ write_record(
+ Record::builder()
+ .args(format_args!("log\nmessage"))
+ .level(Level::Info)
+ .file(Some("test.rs"))
+ .line(Some(144))
+ .module_path(Some("test::path"))
+ .target(target)
+ .build(),
+ fmt,
+ )
+ }
+
+ fn write(fmt: DefaultFormat) -> String {
+ write_target("", fmt)
+ }
+
+ #[test]
+ fn format_with_header() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_no_header() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_indent_spaces() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: Some(4),
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\n message\n", written);
+ }
+
+ #[test]
+ fn format_indent_zero_spaces() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: Some(0),
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_indent_spaces_no_header() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: Some(4),
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\n message\n", written);
+ }
+
+ #[test]
+ fn format_suffix() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\nmessage\n\n", written);
+ }
+
+ #[test]
+ fn format_suffix_with_indent() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: Some(4),
+ suffix: "\n\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\n\n message\n\n", written);
+ }
+
+ #[test]
+ fn format_target() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write_target(
+ "target",
+ DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: true,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ },
+ );
+
+ assert_eq!("[INFO test::path target] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_empty_target() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: true,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_no_target() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write_target(
+ "target",
+ DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ },
+ );
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+}
diff --git a/third_party/rust/env_logger/src/fmt/writer/atty.rs b/third_party/rust/env_logger/src/fmt/writer/atty.rs
new file mode 100644
index 0000000000..1a133eeff7
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/writer/atty.rs
@@ -0,0 +1,33 @@
+/*
+This internal module contains the terminal detection implementation.
+
+If the `auto-color` feature is enabled then we detect whether we're attached to a particular TTY.
+Otherwise, assume we're not attached to anything. This effectively prevents styles from being
+printed.
+*/
+
+#[cfg(feature = "auto-color")]
+mod imp {
+ use is_terminal::IsTerminal;
+
+ pub(in crate::fmt) fn is_stdout() -> bool {
+ std::io::stdout().is_terminal()
+ }
+
+ pub(in crate::fmt) fn is_stderr() -> bool {
+ std::io::stderr().is_terminal()
+ }
+}
+
+#[cfg(not(feature = "auto-color"))]
+mod imp {
+ pub(in crate::fmt) fn is_stdout() -> bool {
+ false
+ }
+
+ pub(in crate::fmt) fn is_stderr() -> bool {
+ false
+ }
+}
+
+pub(in crate::fmt) use self::imp::*;
diff --git a/third_party/rust/env_logger/src/fmt/writer/mod.rs b/third_party/rust/env_logger/src/fmt/writer/mod.rs
new file mode 100644
index 0000000000..7f4b6f9495
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/writer/mod.rs
@@ -0,0 +1,253 @@
+mod atty;
+mod termcolor;
+
+use self::atty::{is_stderr, is_stdout};
+use self::termcolor::BufferWriter;
+use std::{fmt, io, mem, sync::Mutex};
+
+pub(super) mod glob {
+ pub use super::termcolor::glob::*;
+ pub use super::*;
+}
+
+pub(super) use self::termcolor::Buffer;
+
+/// Log target, either `stdout`, `stderr` or a custom pipe.
+#[non_exhaustive]
+pub enum Target {
+ /// Logs will be sent to standard output.
+ Stdout,
+ /// Logs will be sent to standard error.
+ Stderr,
+ /// Logs will be sent to a custom pipe.
+ Pipe(Box<dyn io::Write + Send + 'static>),
+}
+
+impl Default for Target {
+ fn default() -> Self {
+ Target::Stderr
+ }
+}
+
+impl fmt::Debug for Target {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Self::Stdout => "stdout",
+ Self::Stderr => "stderr",
+ Self::Pipe(_) => "pipe",
+ }
+ )
+ }
+}
+
+/// Log target, either `stdout`, `stderr` or a custom pipe.
+///
+/// Same as `Target`, except the pipe is wrapped in a mutex for interior mutability.
+pub(super) enum WritableTarget {
+ /// Logs will be sent to standard output.
+ Stdout,
+ /// Logs will be sent to standard error.
+ Stderr,
+ /// Logs will be sent to a custom pipe.
+ Pipe(Box<Mutex<dyn io::Write + Send + 'static>>),
+}
+
+impl From<Target> for WritableTarget {
+ fn from(target: Target) -> Self {
+ match target {
+ Target::Stdout => Self::Stdout,
+ Target::Stderr => Self::Stderr,
+ Target::Pipe(pipe) => Self::Pipe(Box::new(Mutex::new(pipe))),
+ }
+ }
+}
+
+impl Default for WritableTarget {
+ fn default() -> Self {
+ Self::from(Target::default())
+ }
+}
+
+impl fmt::Debug for WritableTarget {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Self::Stdout => "stdout",
+ Self::Stderr => "stderr",
+ Self::Pipe(_) => "pipe",
+ }
+ )
+ }
+}
+/// Whether or not to print styles to the target.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum WriteStyle {
+ /// Try to print styles, but don't force the issue.
+ Auto,
+ /// Try very hard to print styles.
+ Always,
+ /// Never print styles.
+ Never,
+}
+
+impl Default for WriteStyle {
+ fn default() -> Self {
+ WriteStyle::Auto
+ }
+}
+
+/// A terminal target with color awareness.
+pub(crate) struct Writer {
+ inner: BufferWriter,
+ write_style: WriteStyle,
+}
+
+impl Writer {
+ pub fn write_style(&self) -> WriteStyle {
+ self.write_style
+ }
+
+ pub(super) fn buffer(&self) -> Buffer {
+ self.inner.buffer()
+ }
+
+ pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> {
+ self.inner.print(buf)
+ }
+}
+
+/// A builder for a terminal writer.
+///
+/// The target and style choice can be configured before building.
+#[derive(Debug)]
+pub(crate) struct Builder {
+ target: WritableTarget,
+ write_style: WriteStyle,
+ is_test: bool,
+ built: bool,
+}
+
+impl Builder {
+ /// Initialize the writer builder with defaults.
+ pub(crate) fn new() -> Self {
+ Builder {
+ target: Default::default(),
+ write_style: Default::default(),
+ is_test: false,
+ built: false,
+ }
+ }
+
+ /// Set the target to write to.
+ pub(crate) fn target(&mut self, target: Target) -> &mut Self {
+ self.target = target.into();
+ self
+ }
+
+ /// Parses a style choice string.
+ ///
+ /// See the [Disabling colors] section for more details.
+ ///
+ /// [Disabling colors]: ../index.html#disabling-colors
+ pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
+ self.write_style(parse_write_style(write_style))
+ }
+
+ /// Whether or not to print style characters when writing.
+ pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self {
+ self.write_style = write_style;
+ self
+ }
+
+ /// Whether or not to capture logs for `cargo test`.
+ #[allow(clippy::wrong_self_convention)]
+ pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
+ self.is_test = is_test;
+ self
+ }
+
+ /// Build a terminal writer.
+ pub(crate) fn build(&mut self) -> Writer {
+ assert!(!self.built, "attempt to re-use consumed builder");
+ self.built = true;
+
+ let color_choice = match self.write_style {
+ WriteStyle::Auto => {
+ if match &self.target {
+ WritableTarget::Stderr => is_stderr(),
+ WritableTarget::Stdout => is_stdout(),
+ WritableTarget::Pipe(_) => false,
+ } {
+ WriteStyle::Auto
+ } else {
+ WriteStyle::Never
+ }
+ }
+ color_choice => color_choice,
+ };
+
+ let writer = match mem::take(&mut self.target) {
+ WritableTarget::Stderr => BufferWriter::stderr(self.is_test, color_choice),
+ WritableTarget::Stdout => BufferWriter::stdout(self.is_test, color_choice),
+ WritableTarget::Pipe(pipe) => BufferWriter::pipe(color_choice, pipe),
+ };
+
+ Writer {
+ inner: writer,
+ write_style: self.write_style,
+ }
+ }
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Builder::new()
+ }
+}
+
+impl fmt::Debug for Writer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Writer").finish()
+ }
+}
+
+fn parse_write_style(spec: &str) -> WriteStyle {
+ match spec {
+ "auto" => WriteStyle::Auto,
+ "always" => WriteStyle::Always,
+ "never" => WriteStyle::Never,
+ _ => Default::default(),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_write_style_valid() {
+ let inputs = vec![
+ ("auto", WriteStyle::Auto),
+ ("always", WriteStyle::Always),
+ ("never", WriteStyle::Never),
+ ];
+
+ for (input, expected) in inputs {
+ assert_eq!(expected, parse_write_style(input));
+ }
+ }
+
+ #[test]
+ fn parse_write_style_invalid() {
+ let inputs = vec!["", "true", "false", "NEVER!!"];
+
+ for input in inputs {
+ assert_eq!(WriteStyle::Auto, parse_write_style(input));
+ }
+ }
+}
diff --git a/third_party/rust/env_logger/src/fmt/writer/termcolor/extern_impl.rs b/third_party/rust/env_logger/src/fmt/writer/termcolor/extern_impl.rs
new file mode 100644
index 0000000000..89c382239c
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/writer/termcolor/extern_impl.rs
@@ -0,0 +1,532 @@
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::fmt;
+use std::io::{self, Write};
+use std::rc::Rc;
+use std::sync::Mutex;
+
+use log::Level;
+use termcolor::{self, ColorChoice, ColorSpec, WriteColor};
+
+use crate::fmt::{Formatter, WritableTarget, WriteStyle};
+
+pub(in crate::fmt::writer) mod glob {
+ pub use super::*;
+}
+
+impl Formatter {
+ /// Begin a new [`Style`].
+ ///
+ /// # Examples
+ ///
+ /// Create a bold, red colored style and use it to print the log level:
+ ///
+ /// ```
+ /// use std::io::Write;
+ /// use env_logger::fmt::Color;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut level_style = buf.style();
+ ///
+ /// level_style.set_color(Color::Red).set_bold(true);
+ ///
+ /// writeln!(buf, "{}: {}",
+ /// level_style.value(record.level()),
+ /// record.args())
+ /// });
+ /// ```
+ ///
+ /// [`Style`]: struct.Style.html
+ pub fn style(&self) -> Style {
+ Style {
+ buf: self.buf.clone(),
+ spec: ColorSpec::new(),
+ }
+ }
+
+ /// Get the default [`Style`] for the given level.
+ ///
+ /// The style can be used to print other values besides the level.
+ pub fn default_level_style(&self, level: Level) -> Style {
+ let mut level_style = self.style();
+ match level {
+ Level::Trace => level_style.set_color(Color::Cyan),
+ Level::Debug => level_style.set_color(Color::Blue),
+ Level::Info => level_style.set_color(Color::Green),
+ Level::Warn => level_style.set_color(Color::Yellow),
+ Level::Error => level_style.set_color(Color::Red).set_bold(true),
+ };
+ level_style
+ }
+
+ /// Get a printable [`Style`] for the given level.
+ ///
+ /// The style can only be used to print the level.
+ pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
+ self.default_level_style(level).into_value(level)
+ }
+}
+
+pub(in crate::fmt::writer) struct BufferWriter {
+ inner: termcolor::BufferWriter,
+ uncolored_target: Option<WritableTarget>,
+}
+
+pub(in crate::fmt) struct Buffer {
+ inner: termcolor::Buffer,
+ has_uncolored_target: bool,
+}
+
+impl BufferWriter {
+ pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
+ BufferWriter {
+ inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
+ uncolored_target: if is_test {
+ Some(WritableTarget::Stderr)
+ } else {
+ None
+ },
+ }
+ }
+
+ pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
+ BufferWriter {
+ inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()),
+ uncolored_target: if is_test {
+ Some(WritableTarget::Stdout)
+ } else {
+ None
+ },
+ }
+ }
+
+ pub(in crate::fmt::writer) fn pipe(
+ write_style: WriteStyle,
+ pipe: Box<Mutex<dyn io::Write + Send + 'static>>,
+ ) -> Self {
+ BufferWriter {
+ // The inner Buffer is never printed from, but it is still needed to handle coloring and other formatting
+ inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
+ uncolored_target: Some(WritableTarget::Pipe(pipe)),
+ }
+ }
+
+ pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
+ Buffer {
+ inner: self.inner.buffer(),
+ has_uncolored_target: self.uncolored_target.is_some(),
+ }
+ }
+
+ pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
+ if let Some(target) = &self.uncolored_target {
+ // This impl uses the `eprint` and `print` macros
+ // instead of `termcolor`'s buffer.
+ // This is so their output can be captured by `cargo test`
+ let log = String::from_utf8_lossy(buf.bytes());
+
+ match target {
+ WritableTarget::Stderr => eprint!("{}", log),
+ WritableTarget::Stdout => print!("{}", log),
+ WritableTarget::Pipe(pipe) => write!(pipe.lock().unwrap(), "{}", log)?,
+ }
+
+ Ok(())
+ } else {
+ self.inner.print(&buf.inner)
+ }
+ }
+}
+
+impl Buffer {
+ pub(in crate::fmt) fn clear(&mut self) {
+ self.inner.clear()
+ }
+
+ pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.inner.write(buf)
+ }
+
+ pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
+ self.inner.flush()
+ }
+
+ pub(in crate::fmt) fn bytes(&self) -> &[u8] {
+ self.inner.as_slice()
+ }
+
+ fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
+ // Ignore styles for test captured logs because they can't be printed
+ if !self.has_uncolored_target {
+ self.inner.set_color(spec)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn reset(&mut self) -> io::Result<()> {
+ // Ignore styles for test captured logs because they can't be printed
+ if !self.has_uncolored_target {
+ self.inner.reset()
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl WriteStyle {
+ fn into_color_choice(self) -> ColorChoice {
+ match self {
+ WriteStyle::Always => ColorChoice::Always,
+ WriteStyle::Auto => ColorChoice::Auto,
+ WriteStyle::Never => ColorChoice::Never,
+ }
+ }
+}
+
+/// A set of styles to apply to the terminal output.
+///
+/// Call [`Formatter::style`] to get a `Style` and use the builder methods to
+/// set styling properties, like [color] and [weight].
+/// To print a value using the style, wrap it in a call to [`value`] when the log
+/// record is formatted.
+///
+/// # Examples
+///
+/// Create a bold, red colored style and use it to print the log level:
+///
+/// ```
+/// use std::io::Write;
+/// use env_logger::fmt::Color;
+///
+/// let mut builder = env_logger::Builder::new();
+///
+/// builder.format(|buf, record| {
+/// let mut level_style = buf.style();
+///
+/// level_style.set_color(Color::Red).set_bold(true);
+///
+/// writeln!(buf, "{}: {}",
+/// level_style.value(record.level()),
+/// record.args())
+/// });
+/// ```
+///
+/// Styles can be re-used to output multiple values:
+///
+/// ```
+/// use std::io::Write;
+/// use env_logger::fmt::Color;
+///
+/// let mut builder = env_logger::Builder::new();
+///
+/// builder.format(|buf, record| {
+/// let mut bold = buf.style();
+///
+/// bold.set_bold(true);
+///
+/// writeln!(buf, "{}: {} {}",
+/// bold.value(record.level()),
+/// bold.value("some bold text"),
+/// record.args())
+/// });
+/// ```
+///
+/// [`Formatter::style`]: struct.Formatter.html#method.style
+/// [color]: #method.set_color
+/// [weight]: #method.set_bold
+/// [`value`]: #method.value
+#[derive(Clone)]
+pub struct Style {
+ buf: Rc<RefCell<Buffer>>,
+ spec: ColorSpec,
+}
+
+/// A value that can be printed using the given styles.
+///
+/// It is the result of calling [`Style::value`].
+///
+/// [`Style::value`]: struct.Style.html#method.value
+pub struct StyledValue<'a, T> {
+ style: Cow<'a, Style>,
+ value: T,
+}
+
+impl Style {
+ /// Set the text color.
+ ///
+ /// # Examples
+ ///
+ /// Create a style with red text:
+ ///
+ /// ```
+ /// use std::io::Write;
+ /// use env_logger::fmt::Color;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut style = buf.style();
+ ///
+ /// style.set_color(Color::Red);
+ ///
+ /// writeln!(buf, "{}", style.value(record.args()))
+ /// });
+ /// ```
+ pub fn set_color(&mut self, color: Color) -> &mut Style {
+ self.spec.set_fg(Some(color.into_termcolor()));
+ self
+ }
+
+ /// Set the text weight.
+ ///
+ /// If `yes` is true then text will be written in bold.
+ /// If `yes` is false then text will be written in the default weight.
+ ///
+ /// # Examples
+ ///
+ /// Create a style with bold text:
+ ///
+ /// ```
+ /// use std::io::Write;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut style = buf.style();
+ ///
+ /// style.set_bold(true);
+ ///
+ /// writeln!(buf, "{}", style.value(record.args()))
+ /// });
+ /// ```
+ pub fn set_bold(&mut self, yes: bool) -> &mut Style {
+ self.spec.set_bold(yes);
+ self
+ }
+
+ /// Set the text intensity.
+ ///
+ /// If `yes` is true then text will be written in a brighter color.
+ /// If `yes` is false then text will be written in the default color.
+ ///
+ /// # Examples
+ ///
+ /// Create a style with intense text:
+ ///
+ /// ```
+ /// use std::io::Write;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut style = buf.style();
+ ///
+ /// style.set_intense(true);
+ ///
+ /// writeln!(buf, "{}", style.value(record.args()))
+ /// });
+ /// ```
+ pub fn set_intense(&mut self, yes: bool) -> &mut Style {
+ self.spec.set_intense(yes);
+ self
+ }
+
+ /// Set whether the text is dimmed.
+ ///
+ /// If `yes` is true then text will be written in a dimmer color.
+ /// If `yes` is false then text will be written in the default color.
+ ///
+ /// # Examples
+ ///
+ /// Create a style with dimmed text:
+ ///
+ /// ```
+ /// use std::io::Write;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut style = buf.style();
+ ///
+ /// style.set_dimmed(true);
+ ///
+ /// writeln!(buf, "{}", style.value(record.args()))
+ /// });
+ /// ```
+ pub fn set_dimmed(&mut self, yes: bool) -> &mut Style {
+ self.spec.set_dimmed(yes);
+ self
+ }
+
+ /// Set the background color.
+ ///
+ /// # Examples
+ ///
+ /// Create a style with a yellow background:
+ ///
+ /// ```
+ /// use std::io::Write;
+ /// use env_logger::fmt::Color;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut style = buf.style();
+ ///
+ /// style.set_bg(Color::Yellow);
+ ///
+ /// writeln!(buf, "{}", style.value(record.args()))
+ /// });
+ /// ```
+ pub fn set_bg(&mut self, color: Color) -> &mut Style {
+ self.spec.set_bg(Some(color.into_termcolor()));
+ self
+ }
+
+ /// Wrap a value in the style.
+ ///
+ /// The same `Style` can be used to print multiple different values.
+ ///
+ /// # Examples
+ ///
+ /// Create a bold, red colored style and use it to print the log level:
+ ///
+ /// ```
+ /// use std::io::Write;
+ /// use env_logger::fmt::Color;
+ ///
+ /// let mut builder = env_logger::Builder::new();
+ ///
+ /// builder.format(|buf, record| {
+ /// let mut style = buf.style();
+ ///
+ /// style.set_color(Color::Red).set_bold(true);
+ ///
+ /// writeln!(buf, "{}: {}",
+ /// style.value(record.level()),
+ /// record.args())
+ /// });
+ /// ```
+ pub fn value<T>(&self, value: T) -> StyledValue<T> {
+ StyledValue {
+ style: Cow::Borrowed(self),
+ value,
+ }
+ }
+
+ /// Wrap a value in the style by taking ownership of it.
+ pub(crate) fn into_value<T>(self, value: T) -> StyledValue<'static, T> {
+ StyledValue {
+ style: Cow::Owned(self),
+ value,
+ }
+ }
+}
+
+impl<'a, T> StyledValue<'a, T> {
+ fn write_fmt<F>(&self, f: F) -> fmt::Result
+ where
+ F: FnOnce() -> fmt::Result,
+ {
+ self.style
+ .buf
+ .borrow_mut()
+ .set_color(&self.style.spec)
+ .map_err(|_| fmt::Error)?;
+
+ // Always try to reset the terminal style, even if writing failed
+ let write = f();
+ let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
+
+ write.and(reset)
+ }
+}
+
+impl fmt::Debug for Style {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Style").field("spec", &self.spec).finish()
+ }
+}
+
+macro_rules! impl_styled_value_fmt {
+ ($($fmt_trait:path),*) => {
+ $(
+ impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> {
+ fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
+ self.write_fmt(|| T::fmt(&self.value, f))
+ }
+ }
+ )*
+ };
+}
+
+impl_styled_value_fmt!(
+ fmt::Debug,
+ fmt::Display,
+ fmt::Pointer,
+ fmt::Octal,
+ fmt::Binary,
+ fmt::UpperHex,
+ fmt::LowerHex,
+ fmt::UpperExp,
+ fmt::LowerExp
+);
+
+// The `Color` type is copied from https://github.com/BurntSushi/termcolor
+
+/// The set of available colors for the terminal foreground/background.
+///
+/// The `Ansi256` and `Rgb` colors will only output the correct codes when
+/// paired with the `Ansi` `WriteColor` implementation.
+///
+/// The `Ansi256` and `Rgb` color types are not supported when writing colors
+/// on Windows using the console. If they are used on Windows, then they are
+/// silently ignored and no colors will be emitted.
+///
+/// This set may expand over time.
+///
+/// This type has a `FromStr` impl that can parse colors from their human
+/// readable form. The format is as follows:
+///
+/// 1. Any of the explicitly listed colors in English. They are matched
+/// case insensitively.
+/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
+/// 3. A triple of 8-bit integers separated by a comma, where each integer is
+/// in decimal or hexadecimal format.
+///
+/// Hexadecimal numbers are written with a `0x` prefix.
+#[allow(missing_docs)]
+#[non_exhaustive]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Color {
+ Black,
+ Blue,
+ Green,
+ Red,
+ Cyan,
+ Magenta,
+ Yellow,
+ White,
+ Ansi256(u8),
+ Rgb(u8, u8, u8),
+}
+
+impl Color {
+ fn into_termcolor(self) -> termcolor::Color {
+ match self {
+ Color::Black => termcolor::Color::Black,
+ Color::Blue => termcolor::Color::Blue,
+ Color::Green => termcolor::Color::Green,
+ Color::Red => termcolor::Color::Red,
+ Color::Cyan => termcolor::Color::Cyan,
+ Color::Magenta => termcolor::Color::Magenta,
+ Color::Yellow => termcolor::Color::Yellow,
+ Color::White => termcolor::Color::White,
+ Color::Ansi256(value) => termcolor::Color::Ansi256(value),
+ Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b),
+ }
+ }
+}
diff --git a/third_party/rust/env_logger/src/fmt/writer/termcolor/mod.rs b/third_party/rust/env_logger/src/fmt/writer/termcolor/mod.rs
new file mode 100644
index 0000000000..20f01979e6
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/writer/termcolor/mod.rs
@@ -0,0 +1,12 @@
+/*
+This internal module contains the style and terminal writing implementation.
+
+Its public API is available when the `termcolor` crate is available.
+The terminal printing is shimmed when the `termcolor` crate is not available.
+*/
+
+#[cfg_attr(feature = "color", path = "extern_impl.rs")]
+#[cfg_attr(not(feature = "color"), path = "shim_impl.rs")]
+mod imp;
+
+pub(in crate::fmt) use self::imp::*;
diff --git a/third_party/rust/env_logger/src/fmt/writer/termcolor/shim_impl.rs b/third_party/rust/env_logger/src/fmt/writer/termcolor/shim_impl.rs
new file mode 100644
index 0000000000..0705770c95
--- /dev/null
+++ b/third_party/rust/env_logger/src/fmt/writer/termcolor/shim_impl.rs
@@ -0,0 +1,72 @@
+use std::{io, sync::Mutex};
+
+use crate::fmt::{WritableTarget, WriteStyle};
+
+pub(in crate::fmt::writer) mod glob {}
+
+pub(in crate::fmt::writer) struct BufferWriter {
+ target: WritableTarget,
+}
+
+pub(in crate::fmt) struct Buffer(Vec<u8>);
+
+impl BufferWriter {
+ pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self {
+ BufferWriter {
+ target: WritableTarget::Stderr,
+ }
+ }
+
+ pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self {
+ BufferWriter {
+ target: WritableTarget::Stdout,
+ }
+ }
+
+ pub(in crate::fmt::writer) fn pipe(
+ _write_style: WriteStyle,
+ pipe: Box<Mutex<dyn io::Write + Send + 'static>>,
+ ) -> Self {
+ BufferWriter {
+ target: WritableTarget::Pipe(pipe),
+ }
+ }
+
+ pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
+ Buffer(Vec::new())
+ }
+
+ pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
+ // This impl uses the `eprint` and `print` macros
+ // instead of using the streams directly.
+ // This is so their output can be captured by `cargo test`.
+ match &self.target {
+ // Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty.
+ WritableTarget::Pipe(pipe) => pipe.lock().unwrap().write_all(&buf.0)?,
+ WritableTarget::Stdout => print!("{}", String::from_utf8_lossy(&buf.0)),
+ WritableTarget::Stderr => eprint!("{}", String::from_utf8_lossy(&buf.0)),
+ }
+
+ Ok(())
+ }
+}
+
+impl Buffer {
+ pub(in crate::fmt) fn clear(&mut self) {
+ self.0.clear();
+ }
+
+ pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.0.extend(buf);
+ Ok(buf.len())
+ }
+
+ pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+
+ #[cfg(test)]
+ pub(in crate::fmt) fn bytes(&self) -> &[u8] {
+ &self.0
+ }
+}
diff --git a/third_party/rust/env_logger/src/lib.rs b/third_party/rust/env_logger/src/lib.rs
new file mode 100644
index 0000000000..59fa2a3e1d
--- /dev/null
+++ b/third_party/rust/env_logger/src/lib.rs
@@ -0,0 +1,1311 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A simple logger that can be configured via environment variables, for use
+//! with the logging facade exposed by the [`log` crate][log-crate-url].
+//!
+//! Despite having "env" in its name, **`env_logger`** can also be configured by
+//! other means besides environment variables. See [the examples][gh-repo-examples]
+//! in the source repository for more approaches.
+//!
+//! By default, `env_logger` writes logs to `stderr`, but can be configured to
+//! instead write them to `stdout`.
+//!
+//! ## Example
+//!
+//! ```
+//! use log::{debug, error, log_enabled, info, Level};
+//!
+//! env_logger::init();
+//!
+//! debug!("this is a debug {}", "message");
+//! error!("this is printed by default");
+//!
+//! if log_enabled!(Level::Info) {
+//! let x = 3 * 4; // expensive computation
+//! info!("the answer was: {}", x);
+//! }
+//! ```
+//!
+//! Assumes the binary is `main`:
+//!
+//! ```{.bash}
+//! $ RUST_LOG=error ./main
+//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
+//! ```
+//!
+//! ```{.bash}
+//! $ RUST_LOG=info ./main
+//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
+//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
+//! ```
+//!
+//! ```{.bash}
+//! $ RUST_LOG=debug ./main
+//! [2017-11-09T02:12:24Z DEBUG main] this is a debug message
+//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
+//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
+//! ```
+//!
+//! You can also set the log level on a per module basis:
+//!
+//! ```{.bash}
+//! $ RUST_LOG=main=info ./main
+//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
+//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
+//! ```
+//!
+//! And enable all logging:
+//!
+//! ```{.bash}
+//! $ RUST_LOG=main ./main
+//! [2017-11-09T02:12:24Z DEBUG main] this is a debug message
+//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
+//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
+//! ```
+//!
+//! If the binary name contains hyphens, you will need to replace
+//! them with underscores:
+//!
+//! ```{.bash}
+//! $ RUST_LOG=my_app ./my-app
+//! [2017-11-09T02:12:24Z DEBUG my_app] this is a debug message
+//! [2017-11-09T02:12:24Z ERROR my_app] this is printed by default
+//! [2017-11-09T02:12:24Z INFO my_app] the answer was: 12
+//! ```
+//!
+//! This is because Rust modules and crates cannot contain hyphens
+//! in their name, although `cargo` continues to accept them.
+//!
+//! See the documentation for the [`log` crate][log-crate-url] for more
+//! information about its API.
+//!
+//! ## Enabling logging
+//!
+//! Log levels are controlled on a per-module basis, and **by default all
+//! logging is disabled except for the `error` level**.
+//!
+//! Logging is controlled via the **`RUST_LOG`** environment variable. The
+//! value of this environment variable is a comma-separated list of *logging
+//! directives*. A logging directive is of the form:
+//!
+//! ```text
+//! example::log::target=level
+//! ```
+//!
+//! The log target is typically equal to the path of the module the message
+//! in question originated from, though it can be overridden.
+//!
+//! The path is rooted in the name of the crate it was compiled for, so if
+//! your program is in a file called, for example, `hello.rs`, the path would
+//! simply be be `hello`.
+//!
+//! Furthermore, the log can be filtered using prefix-search based on the
+//! specified log target. A value of, for example, `RUST_LOG=example`, would
+//! match all of the messages with targets:
+//!
+//! * `example`
+//! * `example::test`
+//! * `example::test::module::submodule`
+//! * `examples::and_more_examples`
+//!
+//! When providing the crate name or a module path, explicitly specifying the
+//! log level is optional. If omitted, all logging for the item will be
+//! enabled.
+//!
+//! The names of the log levels that may be specified correspond to the
+//! variations of the [`log::Level`][level-enum] enum from the `log`
+//! crate. They are:
+//!
+//! * `error`
+//! * `warn`
+//! * `info`
+//! * `debug`
+//! * `trace`
+//!
+//! There is also a pseudo logging level, `off`, which may be specified to
+//! disable all logging for a given module or for the entire application. As
+//! with the logging levels, the letter case is not significant[^fn-off].
+//!
+//! [^fn-off]: Similar to the universe of log level names, the `off` pseudo
+//! log level feature is also provided by the underlying `log` crate.
+//!
+//! The letter case is not significant for the logging level names; e.g.,
+//! `debug`, `DEBUG`, and `dEbuG` all represent the same logging level. For
+//! consistency, our convention is to use the lower case names. Where our docs
+//! do use other forms, they do so in the context of specific examples, so you
+//! won't be surprised if you see similar usage in the wild.
+//!
+//! As the log level for a module is optional, the module to enable logging for
+//! is also optional. **If only a level is provided, then the global log
+//! level for all modules is set to this value.**
+//!
+//! Some examples of valid values of `RUST_LOG` are:
+//!
+//! * `hello` turns on all logging for the 'hello' module
+//! * `trace` turns on all logging for the application, regardless of its name
+//! * `TRACE` turns on all logging for the application, regardless of its name (same as previous)
+//! * `info` turns on all info logging
+//! * `INFO` turns on all info logging (same as previous)
+//! * `hello=debug` turns on debug logging for 'hello'
+//! * `hello=DEBUG` turns on debug logging for 'hello' (same as previous)
+//! * `hello,std::option` turns on hello, and std's option logging
+//! * `error,hello=warn` turn on global error logging and also warn for hello
+//! * `error,hello=off` turn on global error logging, but turn off logging for hello
+//! * `off` turns off all logging for the application
+//! * `OFF` turns off all logging for the application (same as previous)
+//!
+//! ## Filtering results
+//!
+//! A `RUST_LOG` directive may include a regex filter. The syntax is to append `/`
+//! followed by a regex. Each message is checked against the regex, and is only
+//! logged if it matches. Note that the matching is done after formatting the
+//! log string but before adding any logging meta-data. There is a single filter
+//! for all modules.
+//!
+//! Some examples:
+//!
+//! * `hello/foo` turns on all logging for the 'hello' module where the log
+//! message includes 'foo'.
+//! * `info/f.o` turns on all info logging where the log message includes 'foo',
+//! 'f1o', 'fao', etc.
+//! * `hello=debug/foo*foo` turns on debug logging for 'hello' where the log
+//! message includes 'foofoo' or 'fofoo' or 'fooooooofoo', etc.
+//! * `error,hello=warn/[0-9]scopes` turn on global error logging and also
+//! warn for hello. In both cases the log message must include a single digit
+//! number followed by 'scopes'.
+//!
+//! ## Capturing logs in tests
+//!
+//! Records logged during `cargo test` will not be captured by the test harness by default.
+//! The [`Builder::is_test`] method can be used in unit tests to ensure logs will be captured:
+//!
+//! ```
+//! # #[macro_use] extern crate log;
+//! #[cfg(test)]
+//! mod tests {
+//! fn init() {
+//! let _ = env_logger::builder().is_test(true).try_init();
+//! }
+//!
+//! #[test]
+//! fn it_works() {
+//! init();
+//!
+//! info!("This record will be captured by `cargo test`");
+//!
+//! assert_eq!(2, 1 + 1);
+//! }
+//! }
+//! ```
+//!
+//! Enabling test capturing comes at the expense of color and other style support
+//! and may have performance implications.
+//!
+//! ## Disabling colors
+//!
+//! Colors and other styles can be configured with the `RUST_LOG_STYLE`
+//! environment variable. It accepts the following values:
+//!
+//! * `auto` (default) will attempt to print style characters, but don't force the issue.
+//! If the console isn't available on Windows, or if TERM=dumb, for example, then don't print colors.
+//! * `always` will always print style characters even if they aren't supported by the terminal.
+//! This includes emitting ANSI colors on Windows if the console API is unavailable.
+//! * `never` will never print style characters.
+//!
+//! ## Tweaking the default format
+//!
+//! Parts of the default format can be excluded from the log output using the [`Builder`].
+//! The following example excludes the timestamp from the log output:
+//!
+//! ```
+//! env_logger::builder()
+//! .format_timestamp(None)
+//! .init();
+//! ```
+//!
+//! ### Stability of the default format
+//!
+//! The default format won't optimise for long-term stability, and explicitly makes no
+//! guarantees about the stability of its output across major, minor or patch version
+//! bumps during `0.x`.
+//!
+//! If you want to capture or interpret the output of `env_logger` programmatically
+//! then you should use a custom format.
+//!
+//! ### Using a custom format
+//!
+//! Custom formats can be provided as closures to the [`Builder`].
+//! These closures take a [`Formatter`] and `log::Record` as arguments:
+//!
+//! ```
+//! use std::io::Write;
+//!
+//! env_logger::builder()
+//! .format(|buf, record| {
+//! writeln!(buf, "{}: {}", record.level(), record.args())
+//! })
+//! .init();
+//! ```
+//!
+//! See the [`fmt`] module for more details about custom formats.
+//!
+//! ## Specifying defaults for environment variables
+//!
+//! `env_logger` can read configuration from environment variables.
+//! If these variables aren't present, the default value to use can be tweaked with the [`Env`] type.
+//! The following example defaults to log `warn` and above if the `RUST_LOG` environment variable
+//! isn't set:
+//!
+//! ```
+//! use env_logger::Env;
+//!
+//! env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
+//! ```
+//!
+//! [gh-repo-examples]: https://github.com/env-logger-rs/env_logger/tree/main/examples
+//! [level-enum]: https://docs.rs/log/latest/log/enum.Level.html
+//! [log-crate-url]: https://docs.rs/log/
+//! [`Builder`]: struct.Builder.html
+//! [`Builder::is_test`]: struct.Builder.html#method.is_test
+//! [`Env`]: struct.Env.html
+//! [`fmt`]: fmt/index.html
+
+#![doc(
+ html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+ html_favicon_url = "https://www.rust-lang.org/static/images/favicon.ico"
+)]
+// When compiled for the rustc compiler itself we want to make sure that this is
+// an unstable crate
+#![cfg_attr(rustbuild, feature(staged_api, rustc_private))]
+#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))]
+#![deny(missing_debug_implementations, missing_docs)]
+
+use std::{borrow::Cow, cell::RefCell, env, io};
+
+use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
+
+pub mod filter;
+pub mod fmt;
+
+pub use self::fmt::glob::*;
+
+use self::filter::Filter;
+use self::fmt::writer::{self, Writer};
+use self::fmt::{FormatFn, Formatter};
+
+/// The default name for the environment variable to read filters from.
+pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG";
+
+/// The default name for the environment variable to read style preferences from.
+pub const DEFAULT_WRITE_STYLE_ENV: &str = "RUST_LOG_STYLE";
+
+/// Set of environment variables to configure from.
+///
+/// # Default environment variables
+///
+/// By default, the `Env` will read the following environment variables:
+///
+/// - `RUST_LOG`: the level filter
+/// - `RUST_LOG_STYLE`: whether or not to print styles with records.
+///
+/// These sources can be configured using the builder methods on `Env`.
+#[derive(Debug)]
+pub struct Env<'a> {
+ filter: Var<'a>,
+ write_style: Var<'a>,
+}
+
+#[derive(Debug)]
+struct Var<'a> {
+ name: Cow<'a, str>,
+ default: Option<Cow<'a, str>>,
+}
+
+/// The env logger.
+///
+/// This struct implements the `Log` trait from the [`log` crate][log-crate-url],
+/// which allows it to act as a logger.
+///
+/// The [`init()`], [`try_init()`], [`Builder::init()`] and [`Builder::try_init()`]
+/// methods will each construct a `Logger` and immediately initialize it as the
+/// default global logger.
+///
+/// If you'd instead need access to the constructed `Logger`, you can use
+/// the associated [`Builder`] and install it with the
+/// [`log` crate][log-crate-url] directly.
+///
+/// [log-crate-url]: https://docs.rs/log/
+/// [`init()`]: fn.init.html
+/// [`try_init()`]: fn.try_init.html
+/// [`Builder::init()`]: struct.Builder.html#method.init
+/// [`Builder::try_init()`]: struct.Builder.html#method.try_init
+/// [`Builder`]: struct.Builder.html
+pub struct Logger {
+ writer: Writer,
+ filter: Filter,
+ format: FormatFn,
+}
+
+/// `Builder` acts as builder for initializing a `Logger`.
+///
+/// It can be used to customize the log format, change the environment variable used
+/// to provide the logging directives and also set the default log level filter.
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate log;
+/// # use std::io::Write;
+/// use env_logger::Builder;
+/// use log::LevelFilter;
+///
+/// let mut builder = Builder::from_default_env();
+///
+/// builder
+/// .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args()))
+/// .filter(None, LevelFilter::Info)
+/// .init();
+///
+/// error!("error message");
+/// info!("info message");
+/// ```
+#[derive(Default)]
+pub struct Builder {
+ filter: filter::Builder,
+ writer: writer::Builder,
+ format: fmt::Builder,
+ built: bool,
+}
+
+impl Builder {
+ /// Initializes the log builder with defaults.
+ ///
+ /// **NOTE:** This method won't read from any environment variables.
+ /// Use the [`filter`] and [`write_style`] methods to configure the builder
+ /// or use [`from_env`] or [`from_default_env`] instead.
+ ///
+ /// # Examples
+ ///
+ /// Create a new builder and configure filters and style:
+ ///
+ /// ```
+ /// use log::LevelFilter;
+ /// use env_logger::{Builder, WriteStyle};
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder
+ /// .filter(None, LevelFilter::Info)
+ /// .write_style(WriteStyle::Always)
+ /// .init();
+ /// ```
+ ///
+ /// [`filter`]: #method.filter
+ /// [`write_style`]: #method.write_style
+ /// [`from_env`]: #method.from_env
+ /// [`from_default_env`]: #method.from_default_env
+ pub fn new() -> Builder {
+ Default::default()
+ }
+
+ /// Initializes the log builder from the environment.
+ ///
+ /// The variables used to read configuration from can be tweaked before
+ /// passing in.
+ ///
+ /// # Examples
+ ///
+ /// Initialise a logger reading the log filter from an environment variable
+ /// called `MY_LOG`:
+ ///
+ /// ```
+ /// use env_logger::Builder;
+ ///
+ /// let mut builder = Builder::from_env("MY_LOG");
+ /// builder.init();
+ /// ```
+ ///
+ /// Initialise a logger using the `MY_LOG` variable for filtering and
+ /// `MY_LOG_STYLE` for whether or not to write styles:
+ ///
+ /// ```
+ /// use env_logger::{Builder, Env};
+ ///
+ /// let env = Env::new().filter("MY_LOG").write_style("MY_LOG_STYLE");
+ ///
+ /// let mut builder = Builder::from_env(env);
+ /// builder.init();
+ /// ```
+ pub fn from_env<'a, E>(env: E) -> Self
+ where
+ E: Into<Env<'a>>,
+ {
+ 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<Env<'a>>,
+ {
+ let env = env.into();
+
+ if let Some(s) = env.get_filter() {
+ self.parse_filters(&s);
+ }
+
+ if let Some(s) = env.get_write_style() {
+ self.parse_write_style(&s);
+ }
+
+ self
+ }
+
+ /// Initializes the log builder from the environment using default variable names.
+ ///
+ /// This method is a convenient way to call `from_env(Env::default())` without
+ /// having to use the `Env` type explicitly. The builder will use the
+ /// [default environment variables].
+ ///
+ /// # Examples
+ ///
+ /// Initialise a logger using the default environment variables:
+ ///
+ /// ```
+ /// use env_logger::Builder;
+ ///
+ /// let mut builder = Builder::from_default_env();
+ /// builder.init();
+ /// ```
+ ///
+ /// [default environment variables]: struct.Env.html#default-environment-variables
+ pub fn from_default_env() -> Self {
+ Self::from_env(Env::default())
+ }
+
+ /// Applies the configuration from the environment using default variable names.
+ ///
+ /// This method is a convenient way to call `parse_env(Env::default())` without
+ /// having to use the `Env` type explicitly. The builder will use the
+ /// [default environment variables].
+ ///
+ /// # Examples
+ ///
+ /// Initialise a logger with filter level `Off`, then configure it using the
+ /// default environment variables:
+ ///
+ /// ```
+ /// use log::LevelFilter;
+ /// use env_logger::Builder;
+ ///
+ /// let mut builder = Builder::new();
+ /// builder.filter_level(LevelFilter::Off);
+ /// builder.parse_default_env();
+ /// builder.init();
+ /// ```
+ ///
+ /// [default environment variables]: struct.Env.html#default-environment-variables
+ pub fn parse_default_env(&mut self) -> &mut Self {
+ self.parse_env(Env::default())
+ }
+
+ /// Sets the format function for formatting the log output.
+ ///
+ /// This function is called on each record logged and should format the
+ /// log record and output it to the given [`Formatter`].
+ ///
+ /// The format function is expected to output the string directly to the
+ /// `Formatter` so that implementations can use the [`std::fmt`] macros
+ /// to format and output without intermediate heap allocations. The default
+ /// `env_logger` formatter takes advantage of this.
+ ///
+ /// # Examples
+ ///
+ /// Use a custom format to write only the log message:
+ ///
+ /// ```
+ /// use std::io::Write;
+ /// use env_logger::Builder;
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder.format(|buf, record| writeln!(buf, "{}", record.args()));
+ /// ```
+ ///
+ /// [`Formatter`]: fmt/struct.Formatter.html
+ /// [`String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
+ /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
+ pub fn format<F: 'static>(&mut self, format: F) -> &mut Self
+ where
+ F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send,
+ {
+ self.format.custom_format = Some(Box::new(format));
+ self
+ }
+
+ /// Use the default format.
+ ///
+ /// This method will clear any custom format set on the builder.
+ pub fn default_format(&mut self) -> &mut Self {
+ self.format = Default::default();
+ self
+ }
+
+ /// Whether or not to write the level in the default format.
+ pub fn format_level(&mut self, write: bool) -> &mut Self {
+ self.format.format_level = write;
+ self
+ }
+
+ /// Whether or not to write the module path in the default format.
+ pub fn format_module_path(&mut self, write: bool) -> &mut Self {
+ self.format.format_module_path = write;
+ self
+ }
+
+ /// Whether or not to write the target in the default format.
+ pub fn format_target(&mut self, write: bool) -> &mut Self {
+ self.format.format_target = write;
+ self
+ }
+
+ /// Configures the amount of spaces to use to indent multiline log records.
+ /// A value of `None` disables any kind of indentation.
+ pub fn format_indent(&mut self, indent: Option<usize>) -> &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<fmt::TimestampPrecision>) -> &mut Self {
+ self.format.format_timestamp = timestamp;
+ self
+ }
+
+ /// Configures the timestamp to use second precision.
+ pub fn format_timestamp_secs(&mut self) -> &mut Self {
+ self.format_timestamp(Some(fmt::TimestampPrecision::Seconds))
+ }
+
+ /// Configures the timestamp to use millisecond precision.
+ pub fn format_timestamp_millis(&mut self) -> &mut Self {
+ self.format_timestamp(Some(fmt::TimestampPrecision::Millis))
+ }
+
+ /// Configures the timestamp to use microsecond precision.
+ pub fn format_timestamp_micros(&mut self) -> &mut Self {
+ self.format_timestamp(Some(fmt::TimestampPrecision::Micros))
+ }
+
+ /// Configures the timestamp to use nanosecond precision.
+ pub fn format_timestamp_nanos(&mut self) -> &mut Self {
+ self.format_timestamp(Some(fmt::TimestampPrecision::Nanos))
+ }
+
+ /// Configures the end of line suffix.
+ pub fn format_suffix(&mut self, suffix: &'static str) -> &mut Self {
+ self.format.format_suffix = suffix;
+ self
+ }
+
+ /// Adds a directive to the filter for a specific module.
+ ///
+ /// # Examples
+ ///
+ /// Only include messages for info and above for logs in `path::to::module`:
+ ///
+ /// ```
+ /// use env_logger::Builder;
+ /// use log::LevelFilter;
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder.filter_module("path::to::module", LevelFilter::Info);
+ /// ```
+ pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
+ self.filter.filter_module(module, level);
+ self
+ }
+
+ /// Adds a directive to the filter for all modules.
+ ///
+ /// # Examples
+ ///
+ /// Only include messages for info and above for logs globally:
+ ///
+ /// ```
+ /// use env_logger::Builder;
+ /// use log::LevelFilter;
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder.filter_level(LevelFilter::Info);
+ /// ```
+ pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
+ self.filter.filter_level(level);
+ self
+ }
+
+ /// Adds filters to the logger.
+ ///
+ /// The given module (if any) will log at most the specified level provided.
+ /// If no module is provided then the filter will apply to all log messages.
+ ///
+ /// # Examples
+ ///
+ /// Only include messages for info and above for logs in `path::to::module`:
+ ///
+ /// ```
+ /// use env_logger::Builder;
+ /// use log::LevelFilter;
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder.filter(Some("path::to::module"), LevelFilter::Info);
+ /// ```
+ pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
+ self.filter.filter(module, level);
+ self
+ }
+
+ /// Parses the directives string in the same form as the `RUST_LOG`
+ /// environment variable.
+ ///
+ /// See the module documentation for more details.
+ pub fn parse_filters(&mut self, filters: &str) -> &mut Self {
+ self.filter.parse(filters);
+ self
+ }
+
+ /// Sets the target for the log output.
+ ///
+ /// Env logger can log to either stdout, stderr or a custom pipe. The default is stderr.
+ ///
+ /// The custom pipe can be used to send the log messages to a custom sink (for example a file).
+ /// Do note that direct writes to a file can become a bottleneck due to IO operation times.
+ ///
+ /// # Examples
+ ///
+ /// Write log message to `stdout`:
+ ///
+ /// ```
+ /// use env_logger::{Builder, Target};
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder.target(Target::Stdout);
+ /// ```
+ pub fn target(&mut self, target: fmt::Target) -> &mut Self {
+ self.writer.target(target);
+ self
+ }
+
+ /// Sets whether or not styles will be written.
+ ///
+ /// This can be useful in environments that don't support control characters
+ /// for setting colors.
+ ///
+ /// # Examples
+ ///
+ /// Never attempt to write styles:
+ ///
+ /// ```
+ /// use env_logger::{Builder, WriteStyle};
+ ///
+ /// let mut builder = Builder::new();
+ ///
+ /// builder.write_style(WriteStyle::Never);
+ /// ```
+ pub fn write_style(&mut self, write_style: fmt::WriteStyle) -> &mut Self {
+ self.writer.write_style(write_style);
+ self
+ }
+
+ /// Parses whether or not to write styles in the same form as the `RUST_LOG_STYLE`
+ /// environment variable.
+ ///
+ /// See the module documentation for more details.
+ pub fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
+ self.writer.parse_write_style(write_style);
+ self
+ }
+
+ /// Sets whether or not the logger will be used in unit tests.
+ ///
+ /// If `is_test` is `true` then the logger will allow the testing framework to
+ /// capture log records rather than printing them to the terminal directly.
+ pub fn is_test(&mut self, is_test: bool) -> &mut Self {
+ self.writer.is_test(is_test);
+ self
+ }
+
+ /// Initializes the global logger with the built env logger.
+ ///
+ /// This should be called early in the execution of a Rust program. Any log
+ /// events that occur before initialization will be ignored.
+ ///
+ /// # Errors
+ ///
+ /// This function will fail if it is called more than once, or if another
+ /// library has already initialized a global logger.
+ pub fn try_init(&mut self) -> Result<(), SetLoggerError> {
+ let logger = self.build();
+
+ let max_level = logger.filter();
+ let r = log::set_boxed_logger(Box::new(logger));
+
+ if r.is_ok() {
+ log::set_max_level(max_level);
+ }
+
+ r
+ }
+
+ /// Initializes the global logger with the built env logger.
+ ///
+ /// This should be called early in the execution of a Rust program. Any log
+ /// events that occur before initialization will be ignored.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if it is called more than once, or if another
+ /// library has already initialized a global logger.
+ pub fn init(&mut self) {
+ self.try_init()
+ .expect("Builder::init should not be called after logger initialized");
+ }
+
+ /// Build an env logger.
+ ///
+ /// The returned logger implements the `Log` trait and can be installed manually
+ /// or nested within another logger.
+ pub fn build(&mut self) -> Logger {
+ assert!(!self.built, "attempt to re-use consumed builder");
+ self.built = true;
+
+ Logger {
+ writer: self.writer.build(),
+ filter: self.filter.build(),
+ format: self.format.build(),
+ }
+ }
+}
+
+impl Logger {
+ /// Creates the logger from the environment.
+ ///
+ /// The variables used to read configuration from can be tweaked before
+ /// passing in.
+ ///
+ /// # Examples
+ ///
+ /// Create a logger reading the log filter from an environment variable
+ /// called `MY_LOG`:
+ ///
+ /// ```
+ /// use env_logger::Logger;
+ ///
+ /// let logger = Logger::from_env("MY_LOG");
+ /// ```
+ ///
+ /// Create a logger using the `MY_LOG` variable for filtering and
+ /// `MY_LOG_STYLE` for whether or not to write styles:
+ ///
+ /// ```
+ /// use env_logger::{Logger, Env};
+ ///
+ /// let env = Env::new().filter_or("MY_LOG", "info").write_style_or("MY_LOG_STYLE", "always");
+ ///
+ /// let logger = Logger::from_env(env);
+ /// ```
+ pub fn from_env<'a, E>(env: E) -> Self
+ where
+ E: Into<Env<'a>>,
+ {
+ 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<Option<Formatter>> = RefCell::new(None);
+ }
+
+ let print = |formatter: &mut Formatter, record: &Record| {
+ let _ =
+ (self.format)(formatter, record).and_then(|_| formatter.print(&self.writer));
+
+ // Always clear the buffer afterwards
+ formatter.clear();
+ };
+
+ let printed = FORMATTER
+ .try_with(|tl_buf| {
+ match tl_buf.try_borrow_mut() {
+ // There are no active borrows of the buffer
+ Ok(mut tl_buf) => match *tl_buf {
+ // We have a previously set formatter
+ Some(ref mut formatter) => {
+ // Check the buffer style. If it's different from the logger's
+ // style then drop the buffer and recreate it.
+ if formatter.write_style() != self.writer.write_style() {
+ *formatter = Formatter::new(&self.writer);
+ }
+
+ print(formatter, record);
+ }
+ // We don't have a previously set formatter
+ None => {
+ let mut formatter = Formatter::new(&self.writer);
+ print(&mut formatter, record);
+
+ *tl_buf = Some(formatter);
+ }
+ },
+ // There's already an active borrow of the buffer (due to re-entrancy)
+ Err(_) => {
+ print(&mut Formatter::new(&self.writer), record);
+ }
+ }
+ })
+ .is_ok();
+
+ if !printed {
+ // The thread-local storage was not available (because its
+ // destructor has already run). Create a new single-use
+ // Formatter on the stack for this call.
+ print(&mut Formatter::new(&self.writer), record);
+ }
+ }
+ }
+
+ fn flush(&self) {}
+}
+
+impl<'a> Env<'a> {
+ /// Get a default set of environment variables.
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Specify an environment variable to read the filter from.
+ pub fn filter<E>(mut self, filter_env: E) -> Self
+ where
+ E: Into<Cow<'a, str>>,
+ {
+ 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<E, V>(mut self, filter_env: E, default: V) -> Self
+ where
+ E: Into<Cow<'a, str>>,
+ V: Into<Cow<'a, str>>,
+ {
+ 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<V>(mut self, default: V) -> Self
+ where
+ V: Into<Cow<'a, str>>,
+ {
+ self.filter = Var::new_with_default(DEFAULT_FILTER_ENV, default);
+
+ self
+ }
+
+ fn get_filter(&self) -> Option<String> {
+ self.filter.get()
+ }
+
+ /// Specify an environment variable to read the style from.
+ pub fn write_style<E>(mut self, write_style_env: E) -> Self
+ where
+ E: Into<Cow<'a, str>>,
+ {
+ 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<E, V>(mut self, write_style_env: E, default: V) -> Self
+ where
+ E: Into<Cow<'a, str>>,
+ V: Into<Cow<'a, str>>,
+ {
+ 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<V>(mut self, default: V) -> Self
+ where
+ V: Into<Cow<'a, str>>,
+ {
+ self.write_style = Var::new_with_default(DEFAULT_WRITE_STYLE_ENV, default);
+
+ self
+ }
+
+ fn get_write_style(&self) -> Option<String> {
+ self.write_style.get()
+ }
+}
+
+impl<'a> Var<'a> {
+ fn new<E>(name: E) -> Self
+ where
+ E: Into<Cow<'a, str>>,
+ {
+ Var {
+ name: name.into(),
+ default: None,
+ }
+ }
+
+ fn new_with_default<E, V>(name: E, default: V) -> Self
+ where
+ E: Into<Cow<'a, str>>,
+ V: Into<Cow<'a, str>>,
+ {
+ Var {
+ name: name.into(),
+ default: Some(default.into()),
+ }
+ }
+
+ fn get(&self) -> Option<String> {
+ env::var(&*self.name)
+ .ok()
+ .or_else(|| self.default.to_owned().map(|v| v.into_owned()))
+ }
+}
+
+impl<'a, T> From<T> for Env<'a>
+where
+ T: Into<Cow<'a, str>>,
+{
+ fn from(filter_env: T) -> Self {
+ Env::default().filter(filter_env.into())
+ }
+}
+
+impl<'a> Default for Env<'a> {
+ fn default() -> Self {
+ Env {
+ filter: Var::new(DEFAULT_FILTER_ENV),
+ write_style: Var::new(DEFAULT_WRITE_STYLE_ENV),
+ }
+ }
+}
+
+mod std_fmt_impls {
+ use super::*;
+ use std::fmt;
+
+ impl fmt::Debug for Logger {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Logger")
+ .field("filter", &self.filter)
+ .finish()
+ }
+ }
+
+ impl fmt::Debug for Builder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.built {
+ f.debug_struct("Logger").field("built", &true).finish()
+ } else {
+ f.debug_struct("Logger")
+ .field("filter", &self.filter)
+ .field("writer", &self.writer)
+ .finish()
+ }
+ }
+ }
+}
+
+/// Attempts to initialize the global logger with an env logger.
+///
+/// This should be called early in the execution of a Rust program. Any log
+/// events that occur before initialization will be ignored.
+///
+/// # Errors
+///
+/// This function will fail if it is called more than once, or if another
+/// library has already initialized a global logger.
+pub fn try_init() -> Result<(), SetLoggerError> {
+ try_init_from_env(Env::default())
+}
+
+/// Initializes the global logger with an env logger.
+///
+/// This should be called early in the execution of a Rust program. Any log
+/// events that occur before initialization will be ignored.
+///
+/// # Panics
+///
+/// This function will panic if it is called more than once, or if another
+/// library has already initialized a global logger.
+pub fn init() {
+ try_init().expect("env_logger::init should not be called after logger initialized");
+}
+
+/// Attempts to initialize the global logger with an env logger from the given
+/// environment variables.
+///
+/// This should be called early in the execution of a Rust program. Any log
+/// events that occur before initialization will be ignored.
+///
+/// # Examples
+///
+/// Initialise a logger using the `MY_LOG` environment variable for filters
+/// and `MY_LOG_STYLE` for writing colors:
+///
+/// ```
+/// use env_logger::{Builder, Env};
+///
+/// # fn run() -> Result<(), Box<dyn ::std::error::Error>> {
+/// 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<Env<'a>>,
+{
+ 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<Env<'a>>,
+{
+ 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<Env<'a>>,
+{
+ 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);
+ }
+}
diff --git a/third_party/rust/env_logger/tests/init-twice-retains-filter.rs b/third_party/rust/env_logger/tests/init-twice-retains-filter.rs
new file mode 100644
index 0000000000..673da3fd28
--- /dev/null
+++ b/third_party/rust/env_logger/tests/init-twice-retains-filter.rs
@@ -0,0 +1,40 @@
+extern crate env_logger;
+extern crate log;
+
+use std::env;
+use std::process;
+use std::str;
+
+fn main() {
+ if env::var("YOU_ARE_TESTING_NOW").is_ok() {
+ // Init from the env (which should set the max level to `Debug`)
+ env_logger::init();
+
+ assert_eq!(log::LevelFilter::Debug, log::max_level());
+
+ // Init again using a different max level
+ // This shouldn't clobber the level that was previously set
+ env_logger::Builder::new()
+ .parse_filters("info")
+ .try_init()
+ .unwrap_err();
+
+ assert_eq!(log::LevelFilter::Debug, log::max_level());
+ return;
+ }
+
+ let exe = env::current_exe().unwrap();
+ let out = process::Command::new(exe)
+ .env("YOU_ARE_TESTING_NOW", "1")
+ .env("RUST_LOG", "debug")
+ .output()
+ .unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
+ if out.status.success() {
+ return;
+ }
+
+ println!("test failed: {}", out.status);
+ println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap());
+ println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap());
+ process::exit(1);
+}
diff --git a/third_party/rust/env_logger/tests/log-in-log.rs b/third_party/rust/env_logger/tests/log-in-log.rs
new file mode 100644
index 0000000000..89517ff362
--- /dev/null
+++ b/third_party/rust/env_logger/tests/log-in-log.rs
@@ -0,0 +1,39 @@
+#[macro_use]
+extern crate log;
+extern crate env_logger;
+
+use std::env;
+use std::fmt;
+use std::process;
+use std::str;
+
+struct Foo;
+
+impl fmt::Display for Foo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ info!("test");
+ f.write_str("bar")
+ }
+}
+
+fn main() {
+ env_logger::init();
+ if env::var("YOU_ARE_TESTING_NOW").is_ok() {
+ return info!("{}", Foo);
+ }
+
+ let exe = env::current_exe().unwrap();
+ let out = process::Command::new(exe)
+ .env("YOU_ARE_TESTING_NOW", "1")
+ .env("RUST_LOG", "debug")
+ .output()
+ .unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
+ if out.status.success() {
+ return;
+ }
+
+ println!("test failed: {}", out.status);
+ println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap());
+ println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap());
+ process::exit(1);
+}
diff --git a/third_party/rust/env_logger/tests/log_tls_dtors.rs b/third_party/rust/env_logger/tests/log_tls_dtors.rs
new file mode 100644
index 0000000000..5db87bd6c5
--- /dev/null
+++ b/third_party/rust/env_logger/tests/log_tls_dtors.rs
@@ -0,0 +1,66 @@
+#[macro_use]
+extern crate log;
+extern crate env_logger;
+
+use std::env;
+use std::process;
+use std::str;
+use std::thread;
+
+struct DropMe;
+
+impl Drop for DropMe {
+ fn drop(&mut self) {
+ debug!("Dropping now");
+ }
+}
+
+fn run() {
+ // Use multiple thread local values to increase the chance that our TLS
+ // value will get destroyed after the FORMATTER key in the library
+ thread_local! {
+ static DROP_ME_0: DropMe = DropMe;
+ static DROP_ME_1: DropMe = DropMe;
+ static DROP_ME_2: DropMe = DropMe;
+ static DROP_ME_3: DropMe = DropMe;
+ static DROP_ME_4: DropMe = DropMe;
+ static DROP_ME_5: DropMe = DropMe;
+ static DROP_ME_6: DropMe = DropMe;
+ static DROP_ME_7: DropMe = DropMe;
+ static DROP_ME_8: DropMe = DropMe;
+ static DROP_ME_9: DropMe = DropMe;
+ }
+ DROP_ME_0.with(|_| {});
+ DROP_ME_1.with(|_| {});
+ DROP_ME_2.with(|_| {});
+ DROP_ME_3.with(|_| {});
+ DROP_ME_4.with(|_| {});
+ DROP_ME_5.with(|_| {});
+ DROP_ME_6.with(|_| {});
+ DROP_ME_7.with(|_| {});
+ DROP_ME_8.with(|_| {});
+ DROP_ME_9.with(|_| {});
+}
+
+fn main() {
+ env_logger::init();
+ if env::var("YOU_ARE_TESTING_NOW").is_ok() {
+ // Run on a separate thread because TLS values on the main thread
+ // won't have their destructors run if pthread is used.
+ // https://doc.rust-lang.org/std/thread/struct.LocalKey.html#platform-specific-behavior
+ thread::spawn(run).join().unwrap();
+ } else {
+ let exe = env::current_exe().unwrap();
+ let out = process::Command::new(exe)
+ .env("YOU_ARE_TESTING_NOW", "1")
+ .env("RUST_LOG", "debug")
+ .output()
+ .unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
+ if !out.status.success() {
+ println!("test failed: {}", out.status);
+ println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap());
+ println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap());
+ process::exit(1);
+ }
+ }
+}
diff --git a/third_party/rust/env_logger/tests/regexp_filter.rs b/third_party/rust/env_logger/tests/regexp_filter.rs
new file mode 100644
index 0000000000..40178bac7f
--- /dev/null
+++ b/third_party/rust/env_logger/tests/regexp_filter.rs
@@ -0,0 +1,57 @@
+#[macro_use]
+extern crate log;
+extern crate env_logger;
+
+use std::env;
+use std::process;
+use std::str;
+
+fn main() {
+ if env::var("LOG_REGEXP_TEST").ok() == Some(String::from("1")) {
+ child_main();
+ } else {
+ parent_main()
+ }
+}
+
+fn child_main() {
+ env_logger::init();
+ info!("XYZ Message");
+}
+
+fn run_child(rust_log: String) -> bool {
+ let exe = env::current_exe().unwrap();
+ let out = process::Command::new(exe)
+ .env("LOG_REGEXP_TEST", "1")
+ .env("RUST_LOG", rust_log)
+ .output()
+ .unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
+ str::from_utf8(out.stderr.as_ref())
+ .unwrap()
+ .contains("XYZ Message")
+}
+
+fn assert_message_printed(rust_log: &str) {
+ if !run_child(rust_log.to_string()) {
+ panic!("RUST_LOG={} should allow the test log message", rust_log)
+ }
+}
+
+fn assert_message_not_printed(rust_log: &str) {
+ if run_child(rust_log.to_string()) {
+ panic!(
+ "RUST_LOG={} should not allow the test log message",
+ rust_log
+ )
+ }
+}
+
+fn parent_main() {
+ // test normal log severity levels
+ assert_message_printed("info");
+ assert_message_not_printed("warn");
+
+ // test of regular expression filters
+ assert_message_printed("info/XYZ");
+ assert_message_not_printed("info/XXX");
+}