summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config1
-rw-r--r--.cargo_vcs_info.json5
-rw-r--r--.editorconfig6
-rw-r--r--.github/ISSUE_TEMPLATE.md6
-rw-r--r--.github/pull_request_template.md3
-rw-r--r--.gitignore4
-rw-r--r--.rustfmt.toml2
-rw-r--r--.travis.yml32
-rw-r--r--.vimrc3
-rw-r--r--CHANGELOG.md289
-rw-r--r--Cargo.toml65
-rw-r--r--Cargo.toml.orig63
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT25
-rw-r--r--LICENSE-MPL2373
-rw-r--r--Makefile73
-rw-r--r--README.md117
-rw-r--r--benches.txt1
-rw-r--r--build.rs44
-rw-r--r--examples/README.md5
-rw-r--r--examples/common/mod.rs36
-rw-r--r--examples/named.rs23
-rw-r--r--examples/singlethread.rs29
-rw-r--r--examples/struct-log-self.rs90
-rw-r--r--src/key/dynamic.rs203
-rw-r--r--src/key/dynamic_nostd.rs168
-rw-r--r--src/key/mod.rs10
-rw-r--r--src/key/static.rs2
-rw-r--r--src/lib.rs3752
-rw-r--r--src/tests.rs419
30 files changed, 6050 insertions, 0 deletions
diff --git a/.cargo/config b/.cargo/config
new file mode 100644
index 0000000..a4081e0
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1 @@
+paths = [ "." ]
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..d3ca584
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+ "git": {
+ "sha1": "af366446d935193e0b6b650549084f2a2a8f1fbc"
+ }
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e3b5903
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,6 @@
+root = true
+
+[*.rs]
+charset = utf-8
+indent_style = space
+indent_size = 4
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..f9b7853
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,6 @@
+Have you checked [slog-rs gitter channel][slog-rs gitter] already? Especially
+if you have a question, you might get an answer much faster there.
+
+Feel free fill an issue anyway! It's just a suggestion.
+
+[slog-rs gitter]: https://gitter.im/slog-rs/slog
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..b860948
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,3 @@
+Make sure to:
+
+* [ ] Add an entry to CHANGELOG.md (if neccessary)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4c623b9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/target
+Cargo.lock
+tags
+rusty-tags.vi
diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 0000000..48b6f1b
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1,2 @@
+max_width = 80
+reorder_imports = true
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..79ca28e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,32 @@
+language: rust
+cache:
+ - cargo
+rust:
+ - stable
+ - 1.23.0
+ - beta
+ - nightly
+
+script:
+ # `patch` section is recent addition
+ - if [ "$TRAVIS_RUST_VERSION" != "1.23.0" ]; then make all ; fi
+ - if [ "$TRAVIS_RUST_VERSION" == "1.23.0" ]; then make build ; fi
+ - if [ "$TRAVIS_RUST_VERSION" != "1.23.0" ]; then make travistest ; fi
+ - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then make bench ; fi
+ - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo build --no-default-features; cargo test --no-default-features; fi
+ - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo test -p test_edition2018; fi
+
+env:
+ global:
+ - RUST_BACKTRACE=1
+ matrix:
+ -
+ - RELEASE=true
+
+notifications:
+ webhooks:
+ urls:
+ - https://webhooks.gitter.im/e/87a331e1a21456b6e2ad
+ on_success: change # options: [always|never|change] default: always
+ on_failure: change # options: [always|never|change] default: always
+ on_start: false # default: false
diff --git a/.vimrc b/.vimrc
new file mode 100644
index 0000000..3e2fb95
--- /dev/null
+++ b/.vimrc
@@ -0,0 +1,3 @@
+let g:rustfmt_autosave = 1
+
+autocmd BufEnter *.rs setlocal tabstop=4 shiftwidth=4 softtabstop=4 expandtab textwidth=80
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..73312c9
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,289 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+## 2.7.0 - 2020-11-29
+
+* Add #% for alternate display of the value part
+* Implement `Eq` for dynamic `Key`s
+* Add `emit_error` to `Serializer`, `#` for serializing foreign errors, and
+ `impl Value for std::io::Error`
+
+## 2.6.0 - 2019-10-28
+
+* Add #? for pretty-debug printing the value part
+
+## 2.5.3 - ????-??-??
+
+* Use fully qualified call syntax for `Logger::log` in macros
+
+## 2.5.2 - 2019-07-22
+
+* Restored parsing of `Level` and `FilterLevel` truncated names
+
+## 2.5.1 - 2019-07-11
+
+* Added parsing of `Level` and `FilterLevel` short names
+
+## 2.5.0 - 2019-07-11
+
+* Added `FilterLevel::accepts`
+* Added `as_str`, `as_short_str` and `Display` to `FilterLevel`
+
+## 2.4.1 - 2018-10-03
+
+* disable support for i128/u128 types if rustc is old
+
+## 2.4.0 - 2018-09-19
+
+* Implement Value for 128 bit integers
+* Add support 2018-style macro imports
+ * **WARNING**: This is a breaking change that we couldn't avoid. Users using
+ explicitly macro import (like `#[macro_use(slog_o)]`) must add
+ `__slog_builtin` to the import list.
+* Bump miminum supported Rust version to 1.26
+
+## 2.3.3 - 2018-07-20
+
+* `impl Value for SocketAddr`
+
+## 2.3.2 - 2018-07-20
+
+* Revert broken changes:
+ * Make `?` and `%` formatters in `kv!` more flexible
+ * Export local inner macros to help with Rust 2018 testing
+
+## 2.3.0 - 2018-07-20
+
+* Export local inner macros to help with Rust 2018 testing
+* Stabilize `Record::new`
+* Make `?` and `%` formatters in `kv!` more flexible
+
+## 2.2.3 - 2018-03-28
+
+* Fix (again) problems introduced by `2.2.1`
+
+## 2.2.2 - 2018-03-26
+
+* Fix problems introduced by `2.2.1`
+
+## 2.2.1 - 2018-03-24
+
+* Add `is_x_enabled()` for queering (imprecise) log-level
+
+## 2.2.0 - 2018-02-13
+### Added
+
+* Support for named format arguments in format messages. They will now become
+ respectively named key-value pairs.
+
+## 2.1.0 - 2017-12-10
+### Added
+
+* Support for nested-values through `emit_serde`, behind `nested-values` feature flag,
+ disabled by default for backward compatibility. **Note**: Consider unstable for the time
+ being.
+* Support for dynamic key (`String` vs `&'static str`), behind `dynamic-keys` feature
+ flag, disabled by default for backward compatibility. **Note**: Consider unstable for
+ the time being.
+
+## 2.0.12 - 2017-09-14
+### Changed
+
+* `#[allow(dead_code)` on unused log statements
+
+## 2.0.11 - 2017-09-13
+### Changed
+
+* Impl `Value` for `std::path::Display`
+
+## 2.0.10 - 2017-09-09
+### Changed
+
+* Remove unnecessary 'static bound on `FnValue`
+
+## 2.0.9 - 2017-08-23
+### Changed
+
+* Update README
+
+## 2.0.6 - 2017-05-27
+### Changed
+
+* Fix for https://github.com/rust-lang/rust/pull/42125
+
+## 2.0.5 - 2017-05-15
+### Changed
+
+* Relicense under MPL/Apache/MIT
+
+## 2.0.4 - 2017-05-05
+### Fixed
+
+* Documentation improvements
+
+## 2.0.3 - 2017-05-05
+### Fixed
+
+* Documentation fixes
+
+## 2.0.2 - 2017-04-12
+### Fixed
+
+* Compile time logging level filtering
+
+## 2.0.0 - 2017-04-11
+### Changed (since v1; bigger picture)
+
+* Unified and simplified logging macros structure and ordering.
+* Added logging Record `tags`.
+* Refactored key-value pairs traits and structures and overall handling.
+ * It's now possible to `impl KV for MyStruct`.
+ * `kv!` can be used to create on stack key-value list.
+ * `KV`-implementing data can appear on the key-value list directly.
+* Support chaining of `OwnedKVList`s. Existing `Logger` can be used as a `Drain`
+ to allow custom handling logic for a part of the logging hierarchy.
+* Added associated `Ok` type to `Drain`.
+* Support for `Drain`s unwind safety.
+* Refactored `Record` to optimize performance on async operations.
+* `slog-extra` has been renamed to `slog-async` since that's the only functionality it contained.
+* `slog-stream` is obsoleted and won't be used in `slog v2` at all. It was a wrong abstraction.
+ `Decorators` functionality was moved to `slog-term`.
+* `slog-term` provides open `Decorator` traits to allow multiple terminal / file writing backends.
+* `slog-term` default `Decorator`s use `term` crate and should work correctly on all supported OSes.
+* `DrainExt` has been removed and utility methods moved directly to `Drain`
+* `slog-stdlog` utilizes `slog-scope` directly.
+* Support for "typed" `Logger`s to allow squeezing last drops of performance possible,
+ at the cost of `T` in `Logger<T>`.
+
+## 2.0.0-3.1 - 2017-03-25
+### Added
+
+* Support for `fmt::Display` values with `%` in `kv!`
+
+
+## 2.0.0-3.0 - 2017-03-25
+### Changed
+
+* Added support for own `KV` and `Value` implementations
+* Streamlined the formatting syntax for `log!` and friends; **BREAKING**
+* Lazy values need explicit `FnValue` wrapper; **BREKING**
+
+### Added
+
+* `kv!` macro
+
+## 2.0.0-2.2 - 2017-03-19
+### Fixes
+
+* Bunch of trait-related fixes
+
+## 2.0.0-2.1 - 2017-03-11
+### Fixed
+
+* Require `MapErr` and `Filter` to be `UnwindSafe`
+
+## 2.0.0-2.0 - 2017-03-11
+### Changed
+
+* Make `Logger::root` return "erased" version
+* Introduce `Logger::root_typed` for "non-erased" `Logger` creation
+
+## 2.0.0-1.0 - 2017-02-23
+
+### Fixed
+
+* `fmt::Debug` for `MutexDrainError`
+
+### Changed
+* Parametrize `Logger` over the `Drain` it holds and introduce "erased" version
+* Enforcing `UnwindSafe` `Drain`s for `Logger`s
+* Refactored key-value pairs traits and structures
+* Renamed some types
+* Support chaining of `OwnedKVList`s
+* Added associated `Ok` type to `Drain`
+* Refactored `Record` to optimize performance on async
+ operations
+* Minimal rustc version required: `1.15.0`
+* `DrainExt` has been removed and utility methods moved directly to `Drain`
+
+### Added
+
+* Macros to create `OwnedKV` and `BorrowedKV`
+* `Logger` implements `Drain`
+
+## 1.5.0 - 2017-01-19
+### Changed
+
+* Order of key-value pairs is now strictly defined
+
+### Added
+
+* `Logger` implements `Drain`
+
+### Deprecated
+
+* Creation of `OwnedKeyValueList`
+
+## 1.4.1 - 2017-01-19
+### Fixed
+
+* Fix an invalid syntax exposed by nightly rust change (Issue #103)
+
+## 1.4.0 - 2016-12-27
+### Changed
+
+* Updated documentation
+
+### Deprecated
+
+* `OwnedKeyValueList::id`
+
+## 1.3.2 - 2016-11-19
+### Added
+
+* `slog_o` as an alternative name for `o`
+
+## 1.3.1 - 2016-11-19
+### Fixed
+
+* Cargo publishing mistake.
+
+## 1.3.0 - 2016-10-31
+### Changed
+
+* **BREAKING**: Removed default `Send+Sync` from `Drain`
+
+## 1.2.1 - 2016-10-27
+### Added
+
+* `OwnedKeyValueList::id` for owned key value unique identification
+
+## 1.2.0 - 2016-10-21
+### Changed
+
+* **BREAKING**: `Serializer` takes `key : &'static str` now
+
+### Fixed
+
+* Corner cases in `info!(...)` and other macros
+
+## 1.1.0 - 2016-10-17
+### Changed
+
+* **BREAKING**: Rewrite handling of owned values.
+
+## 1.0.1
+### Fixed
+
+* `use std` in `o!`
+
+### Added
+
+* Implement `fmt::Debug` for `Logger`
+
+## 1.0.0 - 2016-09-21
+
+First stable release.
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..1e27df1
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,65 @@
+# 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 believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "slog"
+version = "2.7.0"
+authors = ["Dawid Ciężarkiewicz <dpc@dpc.pw>"]
+build = "build.rs"
+autoexamples = true
+description = "Structured, extensible, composable logging for Rust"
+homepage = "https://github.com/slog-rs/slog"
+documentation = "https://docs.rs/slog"
+readme = "README.md"
+keywords = ["log", "logging", "structured", "hierarchical"]
+categories = ["development-tools::debugging"]
+license = "MPL-2.0 OR MIT OR Apache-2.0"
+repository = "https://github.com/slog-rs/slog"
+[package.metadata.docs.rs]
+features = ["std", "nested-values", "dynamic-keys"]
+[profile.bench]
+opt-level = 3
+lto = true
+debug = false
+debug-assertions = false
+
+[profile.release]
+opt-level = 3
+lto = true
+debug = false
+debug-assertions = false
+
+[[example]]
+name = "singlethread"
+required-features = ["nothreads"]
+[dependencies.erased-serde]
+version = "0.3"
+optional = true
+
+[features]
+default = ["std"]
+dynamic-keys = []
+max_level_debug = []
+max_level_error = []
+max_level_info = []
+max_level_off = []
+max_level_trace = []
+max_level_warn = []
+nested-values = ["erased-serde"]
+nothreads = []
+release_max_level_debug = []
+release_max_level_error = []
+release_max_level_info = []
+release_max_level_off = []
+release_max_level_trace = []
+release_max_level_warn = []
+std = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..9fd4d6f
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,63 @@
+[package]
+name = "slog"
+version = "2.7.0"
+authors = ["Dawid Ciężarkiewicz <dpc@dpc.pw>"]
+description = "Structured, extensible, composable logging for Rust"
+keywords = ["log", "logging", "structured", "hierarchical"]
+categories = ["development-tools::debugging"]
+license = "MPL-2.0 OR MIT OR Apache-2.0"
+documentation = "https://docs.rs/slog"
+homepage = "https://github.com/slog-rs/slog"
+repository = "https://github.com/slog-rs/slog"
+readme = "README.md"
+autoexamples = true
+
+build = "build.rs"
+
+[profile.release]
+opt-level = 3
+debug = false
+lto = true
+debug-assertions = false
+
+[profile.bench]
+opt-level = 3
+debug = false
+lto = true
+debug-assertions = false
+
+[features]
+nested-values = ["erased-serde"]
+dynamic-keys = []
+std = []
+nothreads = []
+default = ["std"]
+
+max_level_off = []
+max_level_error = []
+max_level_warn = []
+max_level_info = []
+max_level_debug = []
+max_level_trace = []
+
+release_max_level_off = []
+release_max_level_error = []
+release_max_level_warn = []
+release_max_level_info = []
+release_max_level_debug = []
+release_max_level_trace = []
+
+[dependencies]
+erased-serde = { version = "0.3", optional = true }
+
+[[example]]
+name = "singlethread"
+required-features = ["nothreads"]
+
+[package.metadata.docs.rs]
+features = ["std", "nested-values", "dynamic-keys"]
+
+[workspace]
+members = [
+ "crates/test_edition2018",
+]
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/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/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..39d4bdb
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2014 The Rust Project Developers
+
+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/LICENSE-MPL2 b/LICENSE-MPL2
new file mode 100644
index 0000000..14e2f77
--- /dev/null
+++ b/LICENSE-MPL2
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c95eeee
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,73 @@
+PKG_NAME=$(shell grep name Cargo.toml | head -n 1 | awk -F \" '{print $$2}')
+DOCS_DEFAULT_MODULE=$(subst -,_,$(PKG_NAME))
+ifeq (, $(shell which cargo-check 2> /dev/null))
+DEFAULT_TARGET=build
+else
+DEFAULT_TARGET=build
+endif
+
+default: $(DEFAULT_TARGET)
+
+CARGO_FLAGS += -v
+
+ALL_TARGETS += build $(EXAMPLES) test doc
+ifneq ($(RELEASE),)
+$(info RELEASE BUILD: $(PKG_NAME))
+CARGO_FLAGS += --release
+else
+$(info DEBUG BUILD: $(PKG_NAME); use `RELEASE=true make [args]` for release build)
+endif
+
+EXAMPLES = $(shell cd examples 2>/dev/null && ls *.rs 2>/dev/null | sed -e 's/.rs$$//g' )
+
+all: $(ALL_TARGETS)
+
+
+.PHONY: run test build doc clean clippy
+run test build clean:
+ cargo $@ $(CARGO_FLAGS)
+
+check:
+ $(info Running check; use `make build` to actually build)
+ cargo $@ $(CARGO_FLAGS)
+
+clippy:
+ cargo build --features clippy
+
+.PHONY: bench
+bench:
+ cargo $@ $(filter-out --release,$(CARGO_FLAGS))
+
+.PHONY: travistest
+travistest: test
+
+.PHONY: longtest
+longtest:
+ @echo "Running longtest. Press Ctrl+C to stop at any time"
+ @sleep 2
+ @i=0; while i=$$((i + 1)) && echo "Iteration $$i" && make test ; do :; done
+
+.PHONY: $(EXAMPLES)
+named struct-log-self:
+ cargo build --example $@ $(CARGO_FLAGS)
+singlethread:
+ cargo build --features nothreads --example $@ $(CARGO_FLAGS)
+
+.PHONY: doc
+doc: FORCE
+ cargo doc
+
+.PHONY: publishdoc
+publishdoc:
+ rm -rf target/doc
+ make doc
+ echo '<meta http-equiv="refresh" content="0;url='${DOCS_DEFAULT_MODULE}'/index.html">' > target/doc/index.html
+ ghp-import -n target/doc
+ git push -f origin gh-pages
+
+.PHONY: docview
+docview: doc
+ xdg-open target/doc/$(PKG_NAME)/index.html
+
+.PHONY: FORCE
+FORCE:
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cdae195
--- /dev/null
+++ b/README.md
@@ -0,0 +1,117 @@
+<p align="center">
+
+ <a href="https://github.com/slog-rs/slog">
+ <img src="https://cdn.rawgit.com/slog-rs/misc/master/media/slog.svg" alt="slog-rs logo">
+ </a>
+ <br>
+
+ <a href="https://travis-ci.org/slog-rs/slog">
+ <img src="https://img.shields.io/travis/slog-rs/slog/master.svg" alt="Travis CI Build Status">
+ </a>
+
+ <a href="https://crates.io/crates/slog">
+ <img src="https://img.shields.io/crates/d/slog.svg" alt="slog-rs on crates.io">
+ </a>
+
+ <a href="https://gitter.im/slog-rs/slog">
+ <img src="https://img.shields.io/gitter/room/slog-rs/slog.svg" alt="slog-rs Gitter Chat">
+ </a>
+
+ <a href="https://docs.rs/releases/search?query=slog-">
+ <img src="https://docs.rs/slog/badge.svg" alt="docs-rs: release versions documentation">
+ </a>
+ <br>
+ <strong><a href="https://github.com/slog-rs/slog/wiki/Getting-started">Getting started</a></strong>
+ <a href="//github.com/slog-rs/slog/wiki/Introduction-to-structured-logging-with-slog">Introduction</a>
+ <a href="//github.com/slog-rs/slog/wiki/FAQ">FAQ</a>
+ <br>
+ <a href="https://crates.io/search?q=slog">Crate list</a>
+</p>
+
+# slog-rs - The Logging for [Rust][rust]
+
+### Introduction (please read)
+
+`slog` is an ecosystem of reusable components for structured, extensible,
+composable and contextual logging for [Rust][rust].
+
+The ambition is to be The Logging Library for Rust. `slog` should accommodate a
+variety of logging features and requirements. If there is a feature that you
+need and standard `log` crate is missing, `slog` should have it.
+
+This power comes with a little steeper learning curve, so if you experience any
+problems, **please join [slog-rs gitter] channel** to get up to speed. If you'd
+like to take a quick, convenient route, consider using
+[sloggers](https://docs.rs/sloggers/) wrapper library.
+
+While the code is reliable, the documentation sometimes could use an improvement.
+Please report all issues and ideas.
+
+### Features & technical documentation
+
+Most of the interesting documentation is auto-generated and hosted on [https://docs.rs](https://docs.rs/slog/).
+
+Go to [docs.rs/slog](https://docs.rs/slog/) to read about features and APIs
+(examples included).
+
+**Note**: `slog` is just a core, and the actual functionality is inside
+many feature crates. To name a few:
+
+* [slog-term](https://docs.rs/slog-term/) for terminal output
+* [slog-async](https://docs.rs/slog-async/) for asynchronous logging
+* [slog-json](https://docs.rs/slog-json/) for logging JSON
+* [slog-syslog](https://docs.rs/slog-syslog/) for logging to syslog
+* [sloggers](https://docs.rs/sloggers/) for convenience methods (note: [3rd-party library](https://github.com/sile/sloggers))
+
+There are many more slog feature crates. Search for [more slog features on
+crates.io](https://crates.io/search?q=slog). It is easy to write and publish
+new ones. Look through all the [existing crates using
+slog](https://crates.io/crates/slog/reverse_dependencies) for examples and ideas.
+
+### Terminal output example
+
+`slog-term` is only one of many `slog` features - useful showcase,
+multi-platform, and featuring eg. automatic TTY detection and colors.
+
+See following screenshot: same output in both compact and full output mode.
+
+![slog-rs terminal example output](http://i.imgur.com/mqrG8yL.png)
+
+## Using & help
+
+Please use [slog-rs gitter] channel to ask for help or discuss
+slog features.
+
+See
+[examples/features.rs](https://github.com/slog-rs/misc/blob/master/examples/features.rs)
+for full quick code example overview.
+
+Read [Documentation](https://docs.rs/slog/) for details and features.
+
+To report a bug or ask for features use [github issues][issues].
+
+[faq]: https://github.com/slog-rs/slog/wiki/FAQ
+[wiki]: https://github.com/slog-rs/slog/wiki/
+[rust]: http://rust-lang.org
+[slog-rs gitter]: https://gitter.im/slog-rs/slog
+[issues]: //github.com/slog-rs/slog/issues
+
+## Slog community
+
+Slog related crates are hosted under [slog github
+organization](https://github.com/slog-rs).
+
+Dawid Ciężarkiewicz is the original author and current maintainer of `slog` and
+therefore self-appointed benevolent dictator over the project. When working on
+slog Dawid follows and expects everyone to follow his [Code of
+Conduct](https://github.com/dpc/public/blob/master/COC.md).
+
+Any particular repositories under slog ecosystem might be created, controlled,
+maintained by other entities with various levels of autonomy. Lets work together
+toward a common goal in a respectful and welcoming atmosphere!
+
+## Verification Recommendation
+
+To help with the maintaince, the ownership of this crate is potentially shared between multiple developers.
+It is recommended to always use [cargo-crev](https://github.com/crev-dev/cargo-crev)
+to verify the trustworthiness of each of your dependencies, including this one.
diff --git a/benches.txt b/benches.txt
new file mode 100644
index 0000000..aacf05a
--- /dev/null
+++ b/benches.txt
@@ -0,0 +1 @@
+Benchmarks have been moved to: https://github.com/slog-rs/misc/tree/master/benches
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..14bf916
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,44 @@
+use std::env;
+use std::process::Command;
+use std::str;
+
+// FIXME: replace it with '?' operator
+macro_rules! try_opt {
+ ($e:expr) => {
+ match { $e } {
+ Some(e) => e,
+ None => return None,
+ }
+ };
+}
+
+fn rustc_minor_version() -> Option<u32> {
+ let rustc = try_opt!(env::var_os("RUSTC"));
+ let output = try_opt!(Command::new(rustc).arg("--version").output().ok());
+ let version_str = try_opt!(str::from_utf8(&output.stdout).ok());
+ let minor_str = try_opt!(version_str.split('.').nth(1));
+
+ minor_str.parse().ok()
+}
+
+fn main() {
+ let minor = match rustc_minor_version() {
+ Some(m) => m,
+ None => return,
+ };
+
+ let target = env::var("TARGET").unwrap();
+ let is_emscripten = target == "asmjs-unknown-emscripten"
+ || target == "wasm32-unknown-emscripten";
+
+ if minor >= 26 && !is_emscripten {
+ println!("cargo:rustc-cfg=integer128");
+ }
+
+ // workaround on macro bugs fixed in 1.20
+ //
+ // https://github.com/rust-lang/rust/pull/42913
+ if minor < 20 {
+ println!("cargo:rustc-cfg=macro_workaround");
+ }
+}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..9079cf0
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,5 @@
+This directory contains some general examples on how to use `slog`
+
+More examples can be found in: https://github.com/slog-rs/misc/tree/master/examples
+
+Also, some functionality-specific examples can be found in respective crates implementing them.
diff --git a/examples/common/mod.rs b/examples/common/mod.rs
new file mode 100644
index 0000000..b83755d
--- /dev/null
+++ b/examples/common/mod.rs
@@ -0,0 +1,36 @@
+use slog::*;
+use std::{fmt, result};
+
+pub struct PrintlnSerializer;
+
+impl Serializer for PrintlnSerializer {
+ fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> Result {
+ print!(", {}={}", key, val);
+ Ok(())
+ }
+}
+
+pub struct PrintlnDrain;
+
+impl Drain for PrintlnDrain {
+ type Ok = ();
+ type Err = ();
+
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+
+ print!("{}", record.msg());
+
+ record
+ .kv()
+ .serialize(record, &mut PrintlnSerializer)
+ .unwrap();
+ values.serialize(record, &mut PrintlnSerializer).unwrap();
+
+ println!("");
+ Ok(())
+ }
+}
diff --git a/examples/named.rs b/examples/named.rs
new file mode 100644
index 0000000..15a12be
--- /dev/null
+++ b/examples/named.rs
@@ -0,0 +1,23 @@
+//#![feature(trace_macros)]
+#[macro_use]
+extern crate slog;
+use slog::{Fuse, Logger};
+
+mod common;
+
+fn main() {
+ let log = Logger::root(
+ Fuse(common::PrintlnDrain),
+ o!("version" => "2")
+ );
+
+ //trace_macros!(true);
+ info!(log, "foo is {foo}", foo = 2; "a" => "b");
+ info!(log, "foo is {foo} {bar}", bar=3, foo = 2; "a" => "b");
+ info!(log, "foo is {foo} {bar} {baz}", bar=3, foo = 2, baz=4; "a" => "b");
+ info!(log, "foo is {foo} {bar} {baz}", bar = 3, foo = 2, baz = 4;);
+ info!(log, "foo is {foo} {bar} {baz}", bar=3, foo = 2, baz=4);
+ info!(log, "foo is {foo} {bar} {baz}", bar=3, foo = 2, baz=4,);
+ info!(log, "formatted {num_entries} entries of {}", "something", num_entries = 2; "log-key" => true);
+ info!(log, "{first} {third} {second}", first = 1, second = 2, third=3; "forth" => 4, "fifth" => 5);
+}
diff --git a/examples/singlethread.rs b/examples/singlethread.rs
new file mode 100644
index 0000000..1ca5689
--- /dev/null
+++ b/examples/singlethread.rs
@@ -0,0 +1,29 @@
+//#![feature(nothreads)]
+#[macro_use]
+extern crate slog;
+use slog::{Fuse, Logger};
+use std::cell::RefCell;
+use std::rc::Rc;
+
+mod common;
+
+#[derive(Clone)]
+struct NoThreadSafeObject {
+ val: Rc<RefCell<usize>>,
+}
+
+fn main() {
+ let log = Logger::root(Fuse(common::PrintlnDrain), o!("version" => "2"));
+ let obj = NoThreadSafeObject {
+ val: Rc::new(RefCell::new(4)),
+ };
+
+ // Move obj2 into a closure. Since it's !Send, this only works
+ // with nothreads feature.
+ let obj2 = obj.clone();
+ let sublog = log.new(o!("obj.val" => slog::FnValue(move |_| {
+ format!("{}", obj2.val.borrow())
+ })));
+
+ info!(sublog, "test");
+}
diff --git a/examples/struct-log-self.rs b/examples/struct-log-self.rs
new file mode 100644
index 0000000..f533766
--- /dev/null
+++ b/examples/struct-log-self.rs
@@ -0,0 +1,90 @@
+//! Example of how to implement `KV` for a struct
+//! to conveniently log data associated with it.
+#[macro_use]
+extern crate slog;
+use slog::*;
+
+mod common;
+
+struct Peer {
+ host: String,
+ port: u32,
+}
+
+impl Peer {
+ fn new(host: String, port: u32) -> Self {
+ Peer {
+ host: host,
+ port: port,
+ }
+ }
+}
+
+// `KV` can be implemented for a struct
+impl KV for Peer {
+ fn serialize(&self, _record: &Record, serializer: &mut Serializer) -> Result {
+ serializer.emit_u32(Key::from("peer-port"), self.port)?;
+ serializer.emit_str(Key::from("peer-host"), &self.host)?;
+ Ok(())
+ }
+}
+
+struct Server {
+ _host: String,
+ _port: u32,
+ // One approach is to create new `Logger` with struct data
+ // and embedded it into struct itself. This works when struct is mostly
+ // immutable.
+ log: Logger,
+}
+
+impl Server {
+ fn new(host: String, port: u32, log: Logger) -> Server {
+ let log = log.new(o!("server-host" => host.clone(), "server-port" => port));
+ Server {
+ _host: host,
+ _port: port,
+ log: log,
+ }
+ }
+
+ fn connection(&self, peer: &Peer) {
+ // Another approach is to add struct to a logging message when it's
+ // necessary. This might be necessary when struct data can change
+ // between different logging statements (not the case here for `Peer`).
+ info!(self.log, "new connection"; peer);
+ }
+}
+
+struct PeerCounter {
+ count: usize,
+ log: Logger,
+}
+
+impl PeerCounter {
+ fn new(log: Logger) -> Self {
+ PeerCounter { count: 0, log: log }
+ }
+
+ // A hybrid approach with `Logger` with parent logging-context embedded into
+ // a `struct` and a helper function adding mutable fields.
+ fn log_info(&self, msg: &str, kv: BorrowedKV) {
+ info!(self.log, "{}", msg; "current-count" => self.count, kv);
+ }
+
+ fn count(&mut self, peer: &Peer) {
+ self.count += 1;
+ self.log_info("counted peer", b!(peer));
+ }
+}
+
+fn main() {
+ let log = Logger::root(Fuse(common::PrintlnDrain), o!("build-id" => "7.3.3-abcdef"));
+
+ let server = Server::new("localhost".into(), 12345, log.clone());
+
+ let peer = Peer::new("1.2.3.4".into(), 999);
+ server.connection(&peer);
+ let mut counter = PeerCounter::new(log);
+ counter.count(&peer);
+}
diff --git a/src/key/dynamic.rs b/src/key/dynamic.rs
new file mode 100644
index 0000000..fe9ba7c
--- /dev/null
+++ b/src/key/dynamic.rs
@@ -0,0 +1,203 @@
+#[cfg(not(feature = "std"))]
+use alloc::borrow::Cow;
+#[cfg(not(feature = "std"))]
+use alloc::string::{String, ToString};
+#[cfg(not(feature = "std"))]
+use core::cmp::PartialEq;
+#[cfg(not(feature = "std"))]
+use core::convert::{AsRef, From, Into};
+#[cfg(not(feature = "std"))]
+use core::fmt;
+#[cfg(not(feature = "std"))]
+use core::hash::{Hash, Hasher};
+#[cfg(not(feature = "std"))]
+use core::iter::{FromIterator, IntoIterator};
+#[cfg(not(feature = "std"))]
+use core::ops::Deref;
+#[cfg(not(feature = "std"))]
+use core::str::FromStr;
+#[cfg(feature = "std")]
+use std::borrow::Cow;
+#[cfg(feature = "std")]
+use std::cmp::PartialEq;
+#[cfg(feature = "std")]
+use std::convert::{AsRef, From};
+#[cfg(feature = "std")]
+use std::fmt;
+#[cfg(feature = "std")]
+use std::hash::{Hash, Hasher};
+#[cfg(feature = "std")]
+use std::iter::{FromIterator, IntoIterator};
+#[cfg(feature = "std")]
+use std::string::String;
+#[cfg(feature = "std")]
+use std::string::ToString;
+
+/// Opaque Key is a representation of a key.
+///
+/// It is owned, and largely forms a contract for
+/// key to follow.
+#[derive(Clone, Eq)]
+pub struct Key {
+ data: Cow<'static, str>,
+}
+
+impl Key {
+ /// Returns the length of self.
+ pub fn len(&self) -> usize {
+ self.data.len()
+ }
+
+ /// Returns the length of self.
+ pub fn is_empty(&self) -> bool {
+ self.data.is_empty()
+ }
+
+ /// Take as a `str`
+ pub fn as_str(&self) -> &str {
+ self.data.as_ref()
+ }
+
+ /// Take as a reference
+ pub fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl Default for Key {
+ fn default() -> Key {
+ Key {
+ data: Cow::Borrowed(""),
+ }
+ }
+}
+
+impl Hash for Key {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ match self.data {
+ Cow::Borrowed(ref ptr) => ptr.hash(state),
+ Cow::Owned(ref ptr) => ptr.hash(state),
+ }
+ }
+}
+
+impl From<&'static str> for Key {
+ #[inline(always)]
+ fn from(data: &'static str) -> Key {
+ Key {
+ data: Cow::Borrowed(data),
+ }
+ }
+}
+
+impl From<String> for Key {
+ fn from(data: String) -> Key {
+ Key {
+ data: Cow::Owned(data),
+ }
+ }
+}
+
+impl From<Key> for String {
+ fn from(s: Key) -> String {
+ s.to_string()
+ }
+}
+
+impl FromIterator<char> for Key {
+ fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>()),
+ }
+ }
+}
+
+impl<'a> FromIterator<&'a char> for Key {
+ fn from_iter<I: IntoIterator<Item = &'a char>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>()),
+ }
+ }
+}
+
+impl FromIterator<String> for Key {
+ fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>()),
+ }
+ }
+}
+
+impl<'a> FromIterator<&'a String> for Key {
+ fn from_iter<I: IntoIterator<Item = &'a String>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(
+ iter.into_iter().map(|x| x.as_str()).collect::<String>(),
+ ),
+ }
+ }
+}
+
+impl<'a> FromIterator<&'a str> for Key {
+ fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>()),
+ }
+ }
+}
+
+impl<'a> FromIterator<Cow<'a, str>> for Key {
+ fn from_iter<I: IntoIterator<Item = Cow<'a, str>>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>()),
+ }
+ }
+}
+
+impl PartialEq<str> for Key {
+ fn eq(&self, other: &str) -> bool {
+ self.as_ref().eq(other)
+ }
+}
+
+impl PartialEq<&str> for Key {
+ fn eq(&self, other: &&str) -> bool {
+ self.as_ref().eq(*other)
+ }
+}
+
+impl PartialEq<String> for Key {
+ fn eq(&self, other: &String) -> bool {
+ self.as_ref().eq(other.as_str())
+ }
+}
+
+impl PartialEq<Self> for Key {
+ fn eq(&self, other: &Self) -> bool {
+ self.as_ref().eq(other.as_ref())
+ }
+}
+
+impl AsRef<str> for Key {
+ fn as_ref<'a>(&'a self) -> &'a str {
+ self.data.as_ref()
+ }
+}
+
+impl fmt::Display for Key {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.data {
+ Cow::Borrowed(ref ptr) => write!(f, "{}", ptr),
+ Cow::Owned(ref ptr) => write!(f, "{}", ptr),
+ }
+ }
+}
+
+impl fmt::Debug for Key {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.data {
+ Cow::Borrowed(ref ptr) => write!(f, "{:?}", ptr),
+ Cow::Owned(ref ptr) => write!(f, "{:?}", ptr),
+ }
+ }
+}
diff --git a/src/key/dynamic_nostd.rs b/src/key/dynamic_nostd.rs
new file mode 100644
index 0000000..97af971
--- /dev/null
+++ b/src/key/dynamic_nostd.rs
@@ -0,0 +1,168 @@
+use alloc::borrow::Cow;
+use alloc::string::{String, ToString};
+
+use core::convert::{From,Into,AsRef};
+use core::ops::Deref;
+use core::str::FromStr;
+use core::cmp::PartialEq;
+use core::hash::{Hash,Hasher};
+use core::iter::{FromIterator, IntoIterator};
+use core::fmt;
+
+/// Opaque Key is a representation of a key.
+///
+/// It is owned, and largely forms a contract for
+/// key to follow.
+#[derive(Clone, Eq)]
+pub struct Key {
+ data: Cow<'static, str>
+}
+
+impl Key {
+
+ pub fn len(&self) -> usize {
+ self.data.len()
+ }
+
+ pub fn as_str(&self) -> &str {
+ self.data.as_ref()
+ }
+
+ pub fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl Default for Key {
+ fn default() -> Key {
+ Key {
+ data: Cow::Borrowed("")
+ }
+ }
+}
+
+impl Hash for Key {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ match self.data {
+ Cow::Borrowed(ref ptr) => ptr.hash(state),
+ Cow::Owned(ref ptr) => ptr.hash(state)
+ }
+ }
+}
+
+impl From<&'static str> for Key {
+ #[inline(always)]
+ fn from(data: &'static str) -> Key {
+ Key {
+ data: Cow::Borrowed(data)
+ }
+ }
+}
+
+impl From<String> for Key {
+ #[inline(always)]
+ fn from(data: String) -> Key {
+ Key{
+ data: Cow::Owned(data)
+ }
+ }
+}
+
+impl FromIterator<char> for Key {
+ fn from_iter<I: IntoIterator<Item=char>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>())
+ }
+ }
+}
+
+impl<'a> FromIterator<&'a char> for Key {
+ fn from_iter<I: IntoIterator<Item=&'a char>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>())
+ }
+ }
+}
+
+impl FromIterator<String> for Key {
+ fn from_iter<I: IntoIterator<Item=String>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>())
+ }
+ }
+}
+
+impl<'a> FromIterator<&'a String> for Key {
+ fn from_iter<I: IntoIterator<Item=&'a String>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().map(|x| x.as_str()).collect::<String>())
+ }
+ }
+}
+
+impl<'a> FromIterator<&'a str> for Key {
+ fn from_iter<I: IntoIterator<Item=&'a str>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>())
+ }
+ }
+}
+
+impl<'a> FromIterator<Cow<'a,str>> for Key {
+ fn from_iter<I: IntoIterator<Item=Cow<'a,str>>>(iter: I) -> Key {
+ Key {
+ data: Cow::Owned(iter.into_iter().collect::<String>())
+ }
+ }
+}
+
+impl PartialEq<str> for Key {
+ #[inline(always)]
+ fn eq(&self, other: &str) -> bool {
+ self.as_ref().eq(other)
+ }
+}
+
+impl PartialEq<&str> for Key {
+ #[inline(always)]
+ fn eq(&self, other: &&str) -> bool {
+ self.as_ref().eq(*other)
+ }
+}
+
+impl PartialEq<String> for Key {
+ #[inline(always)]
+ fn eq(&self, other: &String) -> bool {
+ self.as_ref().eq(other.as_str())
+ }
+}
+
+impl PartialEq<Self> for Key {
+ #[inline(always)]
+ fn eq(&self, other: &Self) -> bool {
+ self.as_ref().eq(other.as_ref())
+ }
+}
+impl AsRef<str> for Key {
+ #[inline(always)]
+ fn as_ref<'a>(&'a self) -> &'a str {
+ self.data.as_ref()
+ }
+}
+impl fmt::Display for Key {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.data {
+ Cow::Borrowed(ref ptr) => write!(f, "{}", ptr),
+ Cow::Owned(ref ptr) => write!(f, "{}", ptr)
+ }
+ }
+}
+impl fmt::Debug for Key {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.data {
+ Cow::Borrowed(ref ptr) => write!(f, "{:?}", ptr),
+ Cow::Owned(ref ptr) => write!(f, "{:?}", ptr)
+ }
+ }
+}
+
diff --git a/src/key/mod.rs b/src/key/mod.rs
new file mode 100644
index 0000000..3f54ed7
--- /dev/null
+++ b/src/key/mod.rs
@@ -0,0 +1,10 @@
+#[cfg(feature = "dynamic-keys")]
+mod dynamic;
+#[cfg(feature = "dynamic-keys")]
+pub use self::dynamic::Key;
+
+#[cfg(not(feature = "dynamic-keys"))]
+#[path = "static.rs"]
+mod static_;
+#[cfg(not(feature = "dynamic-keys"))]
+pub use self::static_::Key;
diff --git a/src/key/static.rs b/src/key/static.rs
new file mode 100644
index 0000000..0465ed5
--- /dev/null
+++ b/src/key/static.rs
@@ -0,0 +1,2 @@
+/// Key type
+pub type Key = &'static str;
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..904589e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,3752 @@
+//! # Slog - Structured, extensible, composable logging for Rust
+//!
+//! `slog-rs` is an ecosystem of reusable components for structured, extensible,
+//! composable logging for Rust.
+//!
+//! `slog` is `slog-rs`'s main crate providing core components shared between
+//! all other parts of `slog-rs` ecosystem.
+//!
+//! This is auto-generated technical documentation of `slog`. For information
+//! about project organization, development, help, etc. please see
+//! [slog github page](https://github.com/slog-rs/slog)
+//!
+//! ## Core advantages over `log` crate
+//!
+//! * **extensible** - `slog` crate provides core functionality: a very basic
+//! and portable standard feature-set based on open `trait`s. This allows
+//! implementing new features that can be independently published.
+//! * **composable** - `trait`s that `slog` exposes to provide extensibility
+//! are designed to be easy to efficiently reuse and combine. By combining
+//! different functionalities, each application can specify precisely when,
+//! where, and how to process logging data from an application and its
+//! dependencies.
+//! * **flexible** - `slog` does not constrain logging to just one globally
+//! registered backend. Parts of your application can handle logging
+//! in a customized way, or completely independently.
+//! * **structured** and both **human and machine readable** - By using the
+//! key-value data format and retaining its type information, the meaning of logging
+//! data is preserved. Data can be serialized to machine readable formats like
+//! JSON and sent to data-mining systems for further analysis, etc. On the
+//! other hand, when presenting on screen, logging information can be presented
+//! in aesthetically pleasing and easy to understand ways.
+//! * **contextual** - `slog`'s `Logger` objects carry set of key-value data
+//! pairs that contain the context of logging - information that otherwise
+//! would have to be repeated in every logging statement.
+//!
+//! ## `slog` features
+//!
+//! * performance oriented; read [what makes slog
+//! fast](https://github.com/slog-rs/slog/wiki/What-makes-slog-fast) and see:
+//! [slog bench log](https://github.com/dpc/slog-rs/wiki/Bench-log)
+//! * lazily evaluation through closure values
+//! * async IO support included: see [`slog-async`
+//! crate](https://docs.rs/slog-async)
+//! * `#![no_std]` support (with opt-out `std` cargo feature flag)
+//! * support for named format arguments (e.g. `info!(logger, "printed {line_count} lines", line_count = 2);`)
+//! for easy bridging between the human readable and machine-readable outputs
+//! * tree-structured loggers
+//! * modular, lightweight and very extensible
+//! * tiny core crate that does not pull any dependencies
+//! * feature-crates for specific functionality
+//! * using `slog` in library does not force users of the library to use slog
+//! (but provides additional functionality); see [example how to use
+//! `slog` in library](https://github.com/slog-rs/example-lib)
+//! * backwards and forwards compatibility with `log` crate:
+//! see [`slog-stdlog` crate](https://docs.rs/slog-stdlog)
+//! * convenience crates:
+//! * logging-scopes for implicit `Logger` passing: see
+//! [slog-scope crate](https://docs.rs/slog-scope)
+//! * many existing core & community provided features:
+//! * multiple outputs
+//! * filtering control
+//! * compile-time log level filter using cargo features (same as in `log`
+//! crate)
+//! * by level, msg, and any other meta-data
+//! * [`slog-envlogger`](https://github.com/slog-rs/envlogger) - port of
+//! `env_logger`
+//! * terminal output, with color support: see [`slog-term`
+//! crate](https://docs.rs/slog-term)
+//! * [json](https://docs.rs/slog-json)
+//! * [bunyan](https://docs.rs/slog-bunyan)
+//! * [syslog](https://docs.rs/slog-syslog)
+//! and [journald](https://docs.rs/slog-journald) support
+//! * run-time configuration:
+//! * run-time behavior change;
+//! see [slog-atomic](https://docs.rs/slog-atomic)
+//! * run-time configuration; see
+//! [slog-config crate](https://docs.rs/slog-config)
+//!
+//!
+//! [env_logger]: https://crates.io/crates/env_logger
+//!
+//! ## Notable details
+//!
+//! **Note:** At compile time `slog` by default removes trace and debug level
+//! statements in release builds, and trace level records in debug builds. This
+//! makes `trace` and `debug` level logging records practically free, which
+//! should encourage using them freely. If you want to enable trace/debug
+//! messages or raise the compile time logging level limit, use the following in
+//! your `Cargo.toml`:
+//!
+//! ```norust
+//! slog = { version = ... ,
+//! features = ["max_level_trace", "release_max_level_warn"] }
+//! ```
+//!
+//! Root drain (passed to `Logger::root`) must be one that does not ever return
+//! errors. This forces user to pick error handing strategy.
+//! `Drain::fuse()` or `Drain::ignore_res()`.
+//!
+//! [env_logger]: https://crates.io/crates/env_logger
+//! [fn-overv]: https://github.com/dpc/slog-rs/wiki/Functional-overview
+//! [atomic-switch]: https://docs.rs/slog-atomic/
+//!
+//! ## Where to start
+//!
+//! [`Drain`](trait.Drain.html), [`Logger`](struct.Logger.html) and
+//! [`log` macro](macro.log.html) are the most important elements of
+//! slog. Make sure to read their respective documentation
+//!
+//! Typically the biggest problem is creating a `Drain`
+//!
+//!
+//! ### Logging to the terminal
+//!
+//! ```ignore
+//! #[macro_use]
+//! extern crate slog;
+//! extern crate slog_term;
+//! extern crate slog_async;
+//!
+//! use slog::Drain;
+//!
+//! fn main() {
+//! let decorator = slog_term::TermDecorator::new().build();
+//! let drain = slog_term::FullFormat::new(decorator).build().fuse();
+//! let drain = slog_async::Async::new(drain).build().fuse();
+//!
+//! let _log = slog::Logger::root(drain, o!());
+//! }
+//! ```
+//!
+//! ### Logging to a file
+//!
+//! ```ignore
+//! #[macro_use]
+//! extern crate slog;
+//! extern crate slog_term;
+//! extern crate slog_async;
+//!
+//! use std::fs::OpenOptions;
+//! use slog::Drain;
+//!
+//! fn main() {
+//! let log_path = "target/your_log_file_path.log";
+//! let file = OpenOptions::new()
+//! .create(true)
+//! .write(true)
+//! .truncate(true)
+//! .open(log_path)
+//! .unwrap();
+//!
+//! let decorator = slog_term::PlainDecorator::new(file);
+//! let drain = slog_term::FullFormat::new(decorator).build().fuse();
+//! let drain = slog_async::Async::new(drain).build().fuse();
+//!
+//! let _log = slog::Logger::root(drain, o!());
+//! }
+//! ```
+//!
+//! You can consider using `slog-json` instead of `slog-term`.
+//! `slog-term` only coincidently fits the role of a file output format. A
+//! proper `slog-file` crate with suitable format, log file rotation and other
+//! file-logging related features would be awesome. Contributions are welcome!
+//!
+//! ### Change logging level at runtime
+//!
+//! ```ignore
+//! #[macro_use]
+//! extern crate slog;
+//! extern crate slog_term;
+//! extern crate slog_async;
+//!
+//! use slog::Drain;
+//!
+//! use std::sync::{Arc, atomic};
+//! use std::sync::atomic::Ordering;
+//! use std::result;
+//!
+//! /// Custom Drain logic
+//! struct RuntimeLevelFilter<D>{
+//! drain: D,
+//! on: Arc<atomic::AtomicBool>,
+//! }
+//!
+//! impl<D> Drain for RuntimeLevelFilter<D>
+//! where D : Drain {
+//! type Ok = Option<D::Ok>;
+//! type Err = Option<D::Err>;
+//!
+//! fn log(&self,
+//! record: &slog::Record,
+//! values: &slog::OwnedKVList)
+//! -> result::Result<Self::Ok, Self::Err> {
+//! let current_level = if self.on.load(Ordering::Relaxed) {
+//! slog::Level::Trace
+//! } else {
+//! slog::Level::Info
+//! };
+//!
+//! if record.level().is_at_least(current_level) {
+//! self.drain.log(
+//! record,
+//! values
+//! )
+//! .map(Some)
+//! .map_err(Some)
+//! } else {
+//! Ok(None)
+//! }
+//! }
+//! }
+//!
+//! fn main() {
+//! // atomic variable controlling logging level
+//! let on = Arc::new(atomic::AtomicBool::new(false));
+//!
+//! let decorator = slog_term::TermDecorator::new().build();
+//! let drain = slog_term::FullFormat::new(decorator).build();
+//! let drain = RuntimeLevelFilter {
+//! drain: drain,
+//! on: on.clone(),
+//! }.fuse();
+//! let drain = slog_async::Async::new(drain).build().fuse();
+//!
+//! let _log = slog::Logger::root(drain, o!());
+//!
+//! // switch level in your code
+//! on.store(true, Ordering::Relaxed);
+//! }
+//! ```
+//!
+//! Why is this not an existing crate? Because there are multiple ways to
+//! achieve the same result, and each application might come with its own
+//! variation. Supporting a more general solution is a maintenance effort.
+//! There is also nothing stopping anyone from publishing their own crate
+//! implementing it.
+//!
+//! Alternative to the above approach is `slog-atomic` crate. It implements
+//! swapping whole parts of `Drain` logging hierarchy.
+//!
+//! ## Examples & help
+//!
+//! Basic examples that are kept up-to-date are typically stored in
+//! respective git repository, under `examples/` subdirectory. Eg.
+//! [slog-term examples](https://github.com/slog-rs/term/tree/master/examples).
+//!
+//! [slog-rs wiki pages](https://github.com/slog-rs/slog/wiki) contain
+//! some pages about `slog-rs` technical details.
+//!
+//! Source code of other [software using
+//! slog-rs](https://crates.io/crates/slog/reverse_dependencies) can
+//! be an useful reference.
+//!
+//! Visit [slog-rs gitter channel](https://gitter.im/slog-rs/slog) for immediate
+//! help.
+//!
+//! ## Migrating from slog v1 to slog v2
+//!
+//! ### Key-value pairs come now after format string
+//!
+//! ```
+//! #[macro_use]
+//! extern crate slog;
+//!
+//! fn main() {
+//! let drain = slog::Discard;
+//! let root = slog::Logger::root(drain, o!());
+//! info!(root, "formatted: {}", 1; "log-key" => true);
+//! }
+//! ```
+//!
+//! See more information about format at [`log`](macro.log.html).
+//!
+//! ### `slog-streamer` is gone
+//!
+//! Create simple terminal logger like this:
+//!
+//! ```ignore
+//! #[macro_use]
+//! extern crate slog;
+//! extern crate slog_term;
+//! extern crate slog_async;
+//!
+//! use slog::Drain;
+//!
+//! fn main() {
+//! let decorator = slog_term::TermDecorator::new().build();
+//! let drain = slog_term::FullFormat::new(decorator).build().fuse();
+//! let drain = slog_async::Async::new(drain).build().fuse();
+//!
+//! let _log = slog::Logger::root(drain, o!());
+//! }
+//! ```
+//!
+//!
+//! ### Logging macros now takes ownership of values.
+//!
+//! Pass them by reference: `&x`.
+//!
+// }}}
+
+// {{{ Imports & meta
+#![warn(missing_docs)]
+#![no_std]
+
+#[cfg(not(feature = "std"))]
+extern crate alloc;
+#[macro_use]
+#[cfg(feature = "std")]
+extern crate std;
+
+mod key;
+pub use self::key::Key;
+#[cfg(not(feature = "std"))]
+use alloc::sync::Arc;
+#[cfg(not(feature = "std"))]
+use alloc::boxed::Box;
+#[cfg(not(feature = "std"))]
+use alloc::rc::Rc;
+#[cfg(not(feature = "std"))]
+use alloc::string::String;
+
+#[cfg(feature = "nested-values")]
+extern crate erased_serde;
+
+use core::{convert, fmt, result};
+use core::str::FromStr;
+#[cfg(feature = "std")]
+use std::boxed::Box;
+#[cfg(feature = "std")]
+use std::panic::{RefUnwindSafe, UnwindSafe};
+#[cfg(feature = "std")]
+use std::rc::Rc;
+#[cfg(feature = "std")]
+use std::string::String;
+#[cfg(feature = "std")]
+use std::sync::Arc;
+// }}}
+
+// {{{ Macros
+/// Macro for building group of key-value pairs:
+/// [`OwnedKV`](struct.OwnedKV.html)
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let _root = slog::Logger::root(
+/// drain,
+/// o!("key1" => "value1", "key2" => "value2")
+/// );
+/// }
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! o(
+ ($($args:tt)*) => {
+ $crate::OwnedKV(kv!($($args)*))
+ };
+);
+
+/// Macro for building group of key-value pairs (alias)
+///
+/// Use in case of macro name collisions
+#[macro_export(local_inner_macros)]
+macro_rules! slog_o(
+ ($($args:tt)*) => {
+ $crate::OwnedKV(slog_kv!($($args)*))
+ };
+);
+
+/// Macro for building group of key-value pairs in
+/// [`BorrowedKV`](struct.BorrowedKV.html)
+///
+/// In most circumstances using this macro directly is unnecessary and `info!`
+/// and other wrappers over `log!` should be used instead.
+#[macro_export(local_inner_macros)]
+macro_rules! b(
+ ($($args:tt)*) => {
+ $crate::BorrowedKV(&kv!($($args)*))
+ };
+);
+
+/// Alias of `b`
+#[macro_export(local_inner_macros)]
+macro_rules! slog_b(
+ ($($args:tt)*) => {
+ $crate::BorrowedKV(&slog_kv!($($args)*))
+ };
+);
+
+/// Macro for build `KV` implementing type
+///
+/// You probably want to use `o!` or `b!` instead.
+#[macro_export(local_inner_macros)]
+macro_rules! kv(
+ (@ $args_ready:expr; $k:expr => %$v:expr) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => %$v:expr, $($args:tt)* ) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => #%$v:expr) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => #%$v:expr, $($args:tt)* ) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => ?$v:expr) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => ?$v:expr, $($args:tt)* ) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => #?$v:expr) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => #?$v:expr, $($args:tt)* ) => {
+ kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => #$v:expr) => {
+ kv!(@ ($crate::SingleKV::from(($k, $crate::ErrorValue($v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => $v:expr) => {
+ kv!(@ ($crate::SingleKV::from(($k, $v)), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => $v:expr, $($args:tt)* ) => {
+ kv!(@ ($crate::SingleKV::from(($k, $v)), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $kv:expr) => {
+ kv!(@ ($kv, $args_ready); )
+ };
+ (@ $args_ready:expr; $kv:expr, $($args:tt)* ) => {
+ kv!(@ ($kv, $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; ) => {
+ $args_ready
+ };
+ (@ $args_ready:expr;, ) => {
+ $args_ready
+ };
+ ($($args:tt)*) => {
+ kv!(@ (); $($args)*)
+ };
+);
+
+/// Alias of `kv`
+#[macro_export(local_inner_macros)]
+macro_rules! slog_kv(
+ (@ $args_ready:expr; $k:expr => %$v:expr) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => %$v:expr, $($args:tt)* ) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => #%$v:expr) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => #%$v:expr, $($args:tt)* ) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => ?$v:expr) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => ?$v:expr, $($args:tt)* ) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => #?$v:expr) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => #?$v:expr, $($args:tt)* ) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $k:expr => $v:expr) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, $v)), $args_ready); )
+ };
+ (@ $args_ready:expr; $k:expr => $v:expr, $($args:tt)* ) => {
+ slog_kv!(@ ($crate::SingleKV::from(($k, $v)), $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; $slog_kv:expr) => {
+ slog_kv!(@ ($slog_kv, $args_ready); )
+ };
+ (@ $args_ready:expr; $slog_kv:expr, $($args:tt)* ) => {
+ slog_kv!(@ ($slog_kv, $args_ready); $($args)* )
+ };
+ (@ $args_ready:expr; ) => {
+ $args_ready
+ };
+ (@ $args_ready:expr;, ) => {
+ $args_ready
+ };
+ ($($args:tt)*) => {
+ slog_kv!(@ (); $($args)*)
+ };
+);
+
+#[macro_export(local_inner_macros)]
+/// Create `RecordStatic` at the given code location
+macro_rules! record_static(
+ ($lvl:expr, $tag:expr,) => { record_static!($lvl, $tag) };
+ ($lvl:expr, $tag:expr) => {{
+ static LOC : $crate::RecordLocation = $crate::RecordLocation {
+ file: __slog_builtin!(@file),
+ line: __slog_builtin!(@line),
+ column: __slog_builtin!(@column),
+ function: "",
+ module: __slog_builtin!(@module_path),
+ };
+ $crate::RecordStatic {
+ location : &LOC,
+ level: $lvl,
+ tag : $tag,
+ }
+ }};
+);
+
+#[macro_export(local_inner_macros)]
+/// Create `RecordStatic` at the given code location (alias)
+macro_rules! slog_record_static(
+ ($lvl:expr, $tag:expr,) => { slog_record_static!($lvl, $tag) };
+ ($lvl:expr, $tag:expr) => {{
+ static LOC : $crate::RecordLocation = $crate::RecordLocation {
+ file: __slog_builtin!(@file),
+ line: __slog_builtin!(@line),
+ column: __slog_builtin!(@column),
+ function: "",
+ module: __slog_builtin!(@module_path),
+ };
+ $crate::RecordStatic {
+ location : &LOC,
+ level: $lvl,
+ tag: $tag,
+ }
+ }};
+);
+
+#[macro_export(local_inner_macros)]
+/// Create `Record` at the given code location
+///
+/// Note that this requires that `lvl` and `tag` are compile-time constants. If
+/// you need them to *not* be compile-time, such as when recreating a `Record`
+/// from a serialized version, use `Record::new` instead.
+macro_rules! record(
+ ($lvl:expr, $tag:expr, $args:expr, $b:expr,) => {
+ record!($lvl, $tag, $args, $b)
+ };
+ ($lvl:expr, $tag:expr, $args:expr, $b:expr) => {{
+ #[allow(dead_code)]
+ static RS : $crate::RecordStatic<'static> = record_static!($lvl, $tag);
+ $crate::Record::new(&RS, $args, $b)
+ }};
+);
+
+#[macro_export(local_inner_macros)]
+/// Create `Record` at the given code location (alias)
+macro_rules! slog_record(
+ ($lvl:expr, $tag:expr, $args:expr, $b:expr,) => {
+ slog_record!($lvl, $tag, $args, $b)
+ };
+ ($lvl:expr, $tag:expr, $args:expr, $b:expr) => {{
+ static RS : $crate::RecordStatic<'static> = slog_record_static!($lvl,
+ $tag);
+ $crate::Record::new(&RS, $args, $b)
+ }};
+);
+
+/// Log message a logging record
+///
+/// Use wrappers `error!`, `warn!` etc. instead
+///
+/// The `max_level_*` and `release_max_level*` cargo features can be used to
+/// statically disable logging at various levels. See [slog notable
+/// details](index.html#notable-details)
+///
+/// Use [version with longer name](macro.slog_log.html) if you want to prevent
+/// clash with legacy `log` crate macro names.
+///
+/// ## Supported invocations
+///
+/// ### Simple
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let root = slog::Logger::root(
+/// drain,
+/// o!("key1" => "value1", "key2" => "value2")
+/// );
+/// info!(root, "test info log"; "log-key" => true);
+/// }
+/// ```
+///
+/// Note that `"key" => value` part is optional:
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let root = slog::Logger::root(
+/// drain, o!("key1" => "value1", "key2" => "value2")
+/// );
+/// info!(root, "test info log");
+/// }
+/// ```
+///
+/// ### Formatting support:
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let root = slog::Logger::root(drain,
+/// o!("key1" => "value1", "key2" => "value2")
+/// );
+/// info!(root, "formatted {num_entries} entries of {}", "something", num_entries = 2; "log-key" => true);
+/// }
+/// ```
+///
+/// Note:
+///
+/// * `;` is used to separate message arguments and key value pairs.
+/// * message behaves like `format!`/`format_args!`
+/// * Named arguments to messages will be added to key-value pairs as well!
+///
+/// `"key" => value` part is optional:
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let root = slog::Logger::root(
+/// drain, o!("key1" => "value1", "key2" => "value2")
+/// );
+/// info!(root, "formatted: {}", 1);
+/// }
+/// ```
+///
+/// Use formatting support wisely. Prefer named arguments, so the associated
+/// data is not "lost" by becoming an untyped string in the message.
+///
+/// ### Tags
+///
+/// All above versions can be supplemented with a tag - string literal prefixed
+/// with `#`.
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let root = slog::Logger::root(drain,
+/// o!("key1" => "value1", "key2" => "value2")
+/// );
+/// let ops = 3;
+/// info!(
+/// root,
+/// #"performance-metric", "thread speed"; "ops_per_sec" => ops
+/// );
+/// }
+/// ```
+///
+/// See `Record::tag()` for more information about tags.
+///
+/// ### Own implementations of `KV` and `Value`
+///
+/// List of key value pairs is a comma separated list of key-values. Typically,
+/// a designed syntax is used in form of `k => v` where `k` can be any type
+/// that implements `Value` type.
+///
+/// It's possible to directly specify type that implements `KV` trait without
+/// `=>` syntax.
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+///
+/// use slog::*;
+///
+/// fn main() {
+/// struct MyKV;
+/// struct MyV;
+///
+/// impl KV for MyKV {
+/// fn serialize(&self,
+/// _record: &Record,
+/// serializer: &mut Serializer)
+/// -> Result {
+/// serializer.emit_u32("MyK", 16)
+/// }
+/// }
+///
+/// impl Value for MyV {
+/// fn serialize(&self,
+/// _record: &Record,
+/// key : Key,
+/// serializer: &mut Serializer)
+/// -> Result {
+/// serializer.emit_u32("MyKV", 16)
+/// }
+/// }
+///
+/// let drain = slog::Discard;
+///
+/// let root = slog::Logger::root(drain, o!(MyKV));
+///
+/// info!(
+/// root,
+/// "testing MyV"; "MyV" => MyV
+/// );
+/// }
+/// ```
+///
+/// ### `fmt::Display` and `fmt::Debug` values
+///
+/// Value of any type that implements `std::fmt::Display` can be prefixed with
+/// `%` in `k => v` expression to use its text representation returned by
+/// `format_args!("{}", v)`. This is especially useful for errors. Not that
+/// this does not allocate any `String` since it operates on `fmt::Arguments`.
+/// You can also use the `#%` prefix to use the "alternate" form of formatting,
+/// represented by the `{:#}` formatting specifier.
+///
+/// Similarly to use `std::fmt::Debug` value can be prefixed with `?`,
+/// or pretty-printed with `#?`.
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+/// use std::fmt::Write;
+///
+/// fn main() {
+/// let drain = slog::Discard;
+/// let log = slog::Logger::root(drain, o!());
+///
+/// let mut output = String::new();
+///
+/// if let Err(e) = write!(&mut output, "write to string") {
+/// error!(log, "write failed"; "err" => %e);
+/// }
+/// }
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! log(
+ // `2` means that `;` was already found
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr) => {
+ $crate::Logger::log(&$l, &record!($lvl, $tag, &__slog_builtin!(@format_args $msg_fmt, $($fmt)*), b!($($kv)*)))
+ };
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr,) => {
+ log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr;) => {
+ log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $($args:tt)*) => {
+ log!(2 @ { $($fmt)* }, { $($kv)* $($args)*}, $l, $lvl, $tag, $msg_fmt)
+ };
+ // `1` means that we are still looking for `;`
+ // -- handle named arguments to format string
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr) => {
+ log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr;) => {
+ log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr,) => {
+ log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr; $($args:tt)*) => {
+ log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr, $($args:tt)*) => {
+ log!(1 @ { $($fmt)* $k = $v, }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ // -- look for `;` termination
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr,) => {
+ log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr) => {
+ log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, ; $($args:tt)*) => {
+ log!(1 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt; $($args)*)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr; $($args:tt)*) => {
+ log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ // -- must be normal argument to format string
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $f:tt $($args:tt)*) => {
+ log!(1 @ { $($fmt)* $f }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ ($l:expr, $lvl:expr, $tag:expr, $($args:tt)*) => {
+ if $lvl.as_usize() <= $crate::__slog_static_max_level().as_usize() {
+ log!(1 @ { }, { }, $l, $lvl, $tag, $($args)*)
+ }
+ };
+);
+
+/// Log message a logging record (alias)
+///
+/// Prefer [shorter version](macro.log.html), unless it clashes with
+/// existing `log` crate macro.
+///
+/// See [`log`](macro.log.html) for documentation.
+///
+/// ```
+/// #[macro_use(slog_o,slog_b,slog_record,slog_record_static,slog_log,slog_info,slog_kv,__slog_builtin)]
+/// extern crate slog;
+///
+/// fn main() {
+/// let log = slog::Logger::root(slog::Discard, slog_o!());
+///
+/// slog_info!(log, "some interesting info"; "where" => "right here");
+/// }
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! slog_log(
+ // `2` means that `;` was already found
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr) => {
+ $crate::Logger::log(&$l, &slog_record!($lvl, $tag, &__slog_builtin!(@format_args $msg_fmt, $($fmt)*), slog_b!($($kv)*)))
+ };
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr,) => {
+ slog_log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr;) => {
+ slog_log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (2 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $($args:tt)*) => {
+ slog_log!(2 @ { $($fmt)* }, { $($kv)* $($args)*}, $l, $lvl, $tag, $msg_fmt)
+ };
+ // `1` means that we are still looking for `;`
+ // -- handle named arguments to format string
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr) => {
+ slog_log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr;) => {
+ slog_log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr,) => {
+ slog_log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr; $($args:tt)*) => {
+ slog_log!(2 @ { $($fmt)* $k = $v }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $k:ident = $v:expr, $($args:tt)*) => {
+ slog_log!(1 @ { $($fmt)* $k = $v, }, { $($kv)* __slog_builtin!(@stringify $k) => $v, }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ // -- look for `;` termination
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr,) => {
+ slog_log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr) => {
+ slog_log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, ; $($args:tt)*) => {
+ slog_log!(1 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt; $($args)*)
+ };
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr; $($args:tt)*) => {
+ slog_log!(2 @ { $($fmt)* }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ // -- must be normal argument to format string
+ (1 @ { $($fmt:tt)* }, { $($kv:tt)* }, $l:expr, $lvl:expr, $tag:expr, $msg_fmt:expr, $f:tt $($args:tt)*) => {
+ slog_log!(1 @ { $($fmt)* $f }, { $($kv)* }, $l, $lvl, $tag, $msg_fmt, $($args)*)
+ };
+ ($l:expr, $lvl:expr, $tag:expr, $($args:tt)*) => {
+ if $lvl.as_usize() <= $crate::__slog_static_max_level().as_usize() {
+ slog_log!(1 @ { }, { }, $l, $lvl, $tag, $($args)*)
+ }
+ };
+);
+/// Log critical level record
+///
+/// See `log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! crit(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Critical, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Critical, "", $($args)+)
+ };
+);
+
+/// Log critical level record (alias)
+///
+/// Prefer shorter version, unless it clashes with
+/// existing `log` crate macro.
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! slog_crit(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Critical, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Critical, "", $($args)+)
+ };
+);
+
+/// Log error level record
+///
+/// See `log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! error(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Error, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Error, "", $($args)+)
+ };
+);
+
+/// Log error level record
+///
+/// Prefer shorter version, unless it clashes with
+/// existing `log` crate macro.
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! slog_error(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Error, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Error, "", $($args)+)
+ };
+);
+
+/// Log warning level record
+///
+/// See `log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! warn(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Warning, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Warning, "", $($args)+)
+ };
+);
+
+/// Log warning level record (alias)
+///
+/// Prefer shorter version, unless it clashes with
+/// existing `log` crate macro.
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! slog_warn(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Warning, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Warning, "", $($args)+)
+ };
+);
+
+/// Log info level record
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! info(
+ ($l:expr, #$tag:expr, $($args:tt)*) => {
+ log!($l, $crate::Level::Info, $tag, $($args)*)
+ };
+ ($l:expr, $($args:tt)*) => {
+ log!($l, $crate::Level::Info, "", $($args)*)
+ };
+);
+
+/// Log info level record (alias)
+///
+/// Prefer shorter version, unless it clashes with
+/// existing `log` crate macro.
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! slog_info(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Info, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Info, "", $($args)+)
+ };
+);
+
+/// Log debug level record
+///
+/// See `log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! debug(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Debug, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Debug, "", $($args)+)
+ };
+);
+
+/// Log debug level record (alias)
+///
+/// Prefer shorter version, unless it clashes with
+/// existing `log` crate macro.
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! slog_debug(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Debug, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Debug, "", $($args)+)
+ };
+);
+
+/// Log trace level record
+///
+/// See `log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! trace(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Trace, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ log!($l, $crate::Level::Trace, "", $($args)+)
+ };
+);
+
+/// Log trace level record (alias)
+///
+/// Prefer shorter version, unless it clashes with
+/// existing `log` crate macro.
+///
+/// See `slog_log` for documentation.
+#[macro_export(local_inner_macros)]
+macro_rules! slog_trace(
+ ($l:expr, #$tag:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Trace, $tag, $($args)+)
+ };
+ ($l:expr, $($args:tt)+) => {
+ slog_log!($l, $crate::Level::Trace, "", $($args)+)
+ };
+ ($($args:tt)+) => {
+ slog_log!($crate::Level::Trace, $($args)+)
+ };
+);
+
+/// Helper macro for using the built-in macros inside of
+/// exposed macros with `local_inner_macros` attribute.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __slog_builtin {
+ (@format_args $($t:tt)*) => ( format_args!($($t)*) );
+ (@stringify $($t:tt)*) => ( stringify!($($t)*) );
+ (@file) => ( file!() );
+ (@line) => ( line!() );
+ (@column) => ( column!() );
+ (@module_path) => ( module_path!() );
+}
+
+// }}}
+
+// {{{ Logger
+/// Logging handle used to execute logging statements
+///
+/// In an essence `Logger` instance holds two pieces of information:
+///
+/// * drain - destination where to forward logging `Record`s for
+/// processing.
+/// * context - list of key-value pairs associated with it.
+///
+/// The root `Logger` is created with a `Drain` that will be cloned to every
+/// member of its hierarchy.
+///
+/// Child `Logger`s are built from existing ones, and inherit their key-value
+/// pairs, which can be supplemented with additional pairs.
+///
+/// Cloning existing loggers and creating new ones is cheap. Loggers can be
+/// freely passed around the code and between threads.
+///
+/// `Logger`s are `Sync+Send` - there's no need to synchronize accesses to them,
+/// as they can accept logging records from multiple threads at once. They can
+/// be sent to any thread. Because of that they require the `Drain` to be
+/// `Sync+Send` as well. Not all `Drain`s are `Sync` or `Send` but they can
+/// often be made so by wrapping in a `Mutex` and/or `Arc`.
+///
+/// `Logger` implements `Drain` trait. Any logging `Record` delivered to
+/// a `Logger` functioning as a `Drain` will be delivered to its `Drain`
+/// with existing key-value pairs appended to the `Logger`'s key-value pairs.
+/// By itself, it is effectively very similar to `Logger` being an ancestor
+/// of `Logger` that originated the logging `Record`. Combined with other
+/// `Drain`s, this allows custom processing logic for a sub-tree of a whole logging
+/// tree.
+///
+/// Logger is parametrized over type of a `Drain` associated with it (`D`). It
+/// default to type-erased version so `Logger` without any type annotation
+/// means `Logger<Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>>`. See
+/// `Logger::root_typed` and `Logger::to_erased` for more information.
+#[derive(Clone)]
+pub struct Logger<D = Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>>
+where
+ D: SendSyncUnwindSafeDrain<Ok = (), Err = Never>,
+{
+ drain: D,
+ list: OwnedKVList,
+}
+
+impl<D> Logger<D>
+where
+ D: SendSyncUnwindSafeDrain<Ok = (), Err = Never>,
+{
+ /// Build a root `Logger`
+ ///
+ /// Root logger starts a new tree associated with a given `Drain`. Root
+ /// logger drain must return no errors. See `Drain::ignore_res()` and
+ /// `Drain::fuse()`.
+ ///
+ /// All children and their children (and so on), form one logging tree
+ /// sharing a common drain. See `Logger::new`.
+ ///
+ /// This version (as opposed to `Logger:root_typed`) will take `drain` and
+ /// made it into `Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>`.
+ /// This is typically the most convenient way to work with `Logger`s.
+ ///
+ /// Use `o!` macro to build `OwnedKV` object.
+ ///
+ /// ```
+ /// #[macro_use]
+ /// extern crate slog;
+ ///
+ /// fn main() {
+ /// let _root = slog::Logger::root(
+ /// slog::Discard,
+ /// o!("key1" => "value1", "key2" => "value2"),
+ /// );
+ /// }
+ /// ```
+ pub fn root<T>(drain: D, values: OwnedKV<T>) -> Logger
+ where
+ D: 'static + SendSyncRefUnwindSafeDrain<Err = Never, Ok = ()>,
+ T: SendSyncRefUnwindSafeKV + 'static,
+ {
+ Logger {
+ drain: Arc::new(drain)
+ as Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>,
+ list: OwnedKVList::root(values),
+ }
+ }
+
+ /// Build a root `Logger` that retains `drain` type
+ ///
+ /// Unlike `Logger::root`, this constructor retains the type of a `drain`,
+ /// which allows highest performance possible by eliminating indirect call
+ /// on `Drain::log`, and allowing monomorphization of `Logger` and `Drain`
+ /// objects.
+ ///
+ /// If you don't understand the implications, you should probably just
+ /// ignore it.
+ ///
+ /// See `Logger:into_erased` and `Logger::to_erased` for conversion from
+ /// type returned by this function to version that would be returned by
+ /// `Logger::root`.
+ pub fn root_typed<T>(drain: D, values: OwnedKV<T>) -> Logger<D>
+ where
+ D: 'static + SendSyncUnwindSafeDrain<Err = Never, Ok = ()> + Sized,
+ T: SendSyncRefUnwindSafeKV + 'static,
+ {
+ Logger {
+ drain: drain,
+ list: OwnedKVList::root(values),
+ }
+ }
+
+ /// Build a child logger
+ ///
+ /// Child logger inherits all existing key-value pairs from its parent and
+ /// supplements them with additional ones.
+ ///
+ /// Use `o!` macro to build `OwnedKV` object.
+ ///
+ /// ### Drain cloning (`D : Clone` requirement)
+ ///
+ /// All children, their children and so on, form one tree sharing a
+ /// common drain. This drain, will be `Clone`d when this method is called.
+ /// That is why `Clone` must be implemented for `D` in `Logger<D>::new`.
+ ///
+ /// For some `Drain` types `Clone` is cheap or even free (a no-op). This is
+ /// the case for any `Logger` returned by `Logger::root` and its children.
+ ///
+ /// When using `Logger::root_typed`, it's possible that cloning might be
+ /// expensive, or even impossible.
+ ///
+ /// The reason why wrapping in an `Arc` is not done internally, and exposed
+ /// to the user is performance. Calling `Drain::log` through an `Arc` is
+ /// tiny bit slower than doing it directly.
+ ///
+ /// ```
+ /// #[macro_use]
+ /// extern crate slog;
+ ///
+ /// fn main() {
+ /// let root = slog::Logger::root(slog::Discard,
+ /// o!("key1" => "value1", "key2" => "value2"));
+ /// let _log = root.new(o!("key" => "value"));
+ /// }
+ #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))]
+ pub fn new<T>(&self, values: OwnedKV<T>) -> Logger<D>
+ where
+ T: SendSyncRefUnwindSafeKV + 'static,
+ D: Clone,
+ {
+ Logger {
+ drain: self.drain.clone(),
+ list: OwnedKVList::new(values, self.list.node.clone()),
+ }
+ }
+
+ /// Log one logging `Record`
+ ///
+ /// Use specific logging functions instead. See `log!` macro
+ /// documentation.
+ #[inline]
+ pub fn log(&self, record: &Record) {
+ let _ = self.drain.log(record, &self.list);
+ }
+
+ /// Get list of key-value pairs assigned to this `Logger`
+ pub fn list(&self) -> &OwnedKVList {
+ &self.list
+ }
+
+ /// Convert to default, "erased" type:
+ /// `Logger<Arc<SendSyncUnwindSafeDrain>>`
+ ///
+ /// Useful to adapt `Logger<D : Clone>` to an interface expecting
+ /// `Logger<Arc<...>>`.
+ ///
+ /// Note that calling on a `Logger<Arc<...>>` will convert it to
+ /// `Logger<Arc<Arc<...>>>` which is not optimal. This might be fixed when
+ /// Rust gains trait implementation specialization.
+ pub fn into_erased(
+ self,
+ ) -> Logger<Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>>
+ where
+ D: SendRefUnwindSafeDrain + 'static,
+ {
+ Logger {
+ drain: Arc::new(self.drain)
+ as Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>,
+ list: self.list,
+ }
+ }
+
+ /// Create a copy with "erased" type
+ ///
+ /// See `into_erased`
+ pub fn to_erased(
+ &self,
+ ) -> Logger<Arc<SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>>
+ where
+ D: SendRefUnwindSafeDrain + 'static + Clone,
+ {
+ self.clone().into_erased()
+ }
+}
+
+impl<D> fmt::Debug for Logger<D>
+where
+ D: SendSyncUnwindSafeDrain<Ok = (), Err = Never>,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(write!(f, "Logger{:?}", self.list));
+ Ok(())
+ }
+}
+
+impl<D> Drain for Logger<D>
+where
+ D: SendSyncUnwindSafeDrain<Ok = (), Err = Never>,
+{
+ type Ok = ();
+ type Err = Never;
+
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ let chained = OwnedKVList {
+ node: Arc::new(MultiListNode {
+ next_node: values.node.clone(),
+ node: self.list.node.clone(),
+ }),
+ };
+ self.drain.log(record, &chained)
+ }
+
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ self.drain.is_enabled(level)
+ }
+}
+
+// {{{ Drain
+/// Logging drain
+///
+/// `Drain`s typically mean destination for logs, but `slog` generalizes the
+/// term.
+///
+/// `Drain`s are responsible for handling logging statements (`Record`s) from
+/// `Logger`s associated with them: filtering, modifying, formatting
+/// and writing the log records into given destination(s).
+///
+/// It's a typical pattern to parametrize `Drain`s over `Drain` traits to allow
+/// composing `Drain`s.
+///
+/// Implementing this trait allows writing custom `Drain`s. Slog users should
+/// not be afraid of implementing their own `Drain`s. Any custom log handling
+/// logic should be implemented as a `Drain`.
+pub trait Drain {
+ /// Type returned by this drain
+ ///
+ /// It can be useful in some circumstances, but rarely. It will probably
+ /// default to `()` once https://github.com/rust-lang/rust/issues/29661 is
+ /// stable.
+ type Ok;
+ /// Type of potential errors that can be returned by this `Drain`
+ type Err;
+ /// Handle one logging statement (`Record`)
+ ///
+ /// Every logging `Record` built from a logging statement (eg.
+ /// `info!(...)`), and key-value lists of a `Logger` it was executed on
+ /// will be passed to the root drain registered during `Logger::root`.
+ ///
+ /// Typically `Drain`s:
+ ///
+ /// * pass this information (or not) to the sub-logger(s) (filters)
+ /// * format and write the information to a destination (writers)
+ /// * deal with the errors returned from the sub-logger(s)
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err>;
+
+ /// **Avoid**: Check if messages at the specified log level are **maybe**
+ /// enabled for this logger.
+ ///
+ /// The purpose of it so to allow **imprecise** detection if a given logging
+ /// level has any chance of actually being logged. This might be used
+ /// to explicitly skip needless computation.
+ ///
+ /// **It is best effort, can return false positives, but not false negatives.**
+ ///
+ /// The logger is still free to ignore records even if the level is enabled,
+ /// so an enabled level doesn't necessarily guarantee that the record will
+ /// actually be logged.
+ ///
+ /// This function is somewhat needless, and is better expressed by using
+ /// lazy values (see `FnValue`). A `FnValue` is more precise and does not
+ /// require additional (potentially recursive) calls to do something that
+ /// `log` will already do anyways (making decision if something should be
+ /// logged or not).
+ ///
+ /// ```
+ /// # #[macro_use]
+ /// # extern crate slog;
+ /// # use slog::*;
+ /// # fn main() {
+ /// let logger = Logger::root(Discard, o!());
+ /// if logger.is_enabled(Level::Debug) {
+ /// let num = 5.0f64;
+ /// let sqrt = num.sqrt();
+ /// debug!(logger, "Sqrt"; "num" => num, "sqrt" => sqrt);
+ /// }
+ /// # }
+ /// ```
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ level.as_usize() <= ::__slog_static_max_level().as_usize()
+ }
+
+ /// **Avoid**: See `is_enabled`
+ #[inline]
+ fn is_critical_enabled(&self) -> bool {
+ self.is_enabled(Level::Critical)
+ }
+
+ /// **Avoid**: See `is_enabled`
+ #[inline]
+ fn is_error_enabled(&self) -> bool {
+ self.is_enabled(Level::Error)
+ }
+
+ /// **Avoid**: See `is_enabled`
+ #[inline]
+ fn is_warning_enabled(&self) -> bool {
+ self.is_enabled(Level::Warning)
+ }
+
+ /// **Avoid**: See `is_enabled`
+ #[inline]
+ fn is_info_enabled(&self) -> bool {
+ self.is_enabled(Level::Info)
+ }
+
+ /// **Avoid**: See `is_enabled`
+ #[inline]
+ fn is_debug_enabled(&self) -> bool {
+ self.is_enabled(Level::Debug)
+ }
+
+ /// **Avoid**: See `is_enabled`
+ #[inline]
+ fn is_trace_enabled(&self) -> bool {
+ self.is_enabled(Level::Trace)
+ }
+
+ /// Pass `Drain` through a closure, eg. to wrap
+ /// into another `Drain`.
+ ///
+ /// ```
+ /// #[macro_use]
+ /// extern crate slog;
+ /// use slog::*;
+ ///
+ /// fn main() {
+ /// let _drain = Discard.map(Fuse);
+ /// }
+ /// ```
+ fn map<F, R>(self, f: F) -> R
+ where
+ Self: Sized,
+ F: FnOnce(Self) -> R,
+ {
+ f(self)
+ }
+
+ /// Filter logging records passed to `Drain`
+ ///
+ /// Wrap `Self` in `Filter`
+ ///
+ /// This will convert `self` to a `Drain` that ignores `Record`s
+ /// for which `f` returns false.
+ fn filter<F>(self, f: F) -> Filter<Self, F>
+ where
+ Self: Sized,
+ F: FilterFn,
+ {
+ Filter::new(self, f)
+ }
+
+ /// Filter logging records passed to `Drain` (by level)
+ ///
+ /// Wrap `Self` in `LevelFilter`
+ ///
+ /// This will convert `self` to a `Drain` that ignores `Record`s of
+ /// logging lever smaller than `level`.
+ fn filter_level(self, level: Level) -> LevelFilter<Self>
+ where
+ Self: Sized,
+ {
+ LevelFilter(self, level)
+ }
+
+ /// Map logging errors returned by this drain
+ ///
+ /// `f` is a closure that takes `Drain::Err` returned by a given
+ /// drain, and returns new error of potentially different type
+ fn map_err<F, E>(self, f: F) -> MapError<Self, E>
+ where
+ Self: Sized,
+ F: MapErrFn<Self::Err, E>,
+ {
+ MapError::new(self, f)
+ }
+
+ /// Ignore results returned by this drain
+ ///
+ /// Wrap `Self` in `IgnoreResult`
+ fn ignore_res(self) -> IgnoreResult<Self>
+ where
+ Self: Sized,
+ {
+ IgnoreResult::new(self)
+ }
+
+ /// Make `Self` panic when returning any errors
+ ///
+ /// Wrap `Self` in `Map`
+ fn fuse(self) -> Fuse<Self>
+ where
+ Self::Err: fmt::Debug,
+ Self: Sized,
+ {
+ self.map(Fuse)
+ }
+}
+
+impl<'a, D: Drain + 'a> Drain for &'a D {
+ type Ok = D::Ok;
+ type Err = D::Err;
+ #[inline]
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ (**self).log(record, values)
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ (**self).is_enabled(level)
+ }
+}
+
+impl<'a, D: Drain + 'a> Drain for &'a mut D {
+ type Ok = D::Ok;
+ type Err = D::Err;
+ #[inline]
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ (**self).log(record, values)
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ (**self).is_enabled(level)
+ }
+}
+
+#[cfg(feature = "std")]
+/// `Send + Sync + UnwindSafe` bound
+///
+/// This type is used to enforce `Drain`s associated with `Logger`s
+/// are thread-safe.
+pub trait SendSyncUnwindSafe: Send + Sync + UnwindSafe {}
+
+#[cfg(feature = "std")]
+impl<T> SendSyncUnwindSafe for T
+where
+ T: Send + Sync + UnwindSafe + ?Sized,
+{
+}
+
+#[cfg(feature = "std")]
+/// `Drain + Send + Sync + UnwindSafe` bound
+///
+/// This type is used to enforce `Drain`s associated with `Logger`s
+/// are thread-safe.
+pub trait SendSyncUnwindSafeDrain: Drain + Send + Sync + UnwindSafe {}
+
+#[cfg(feature = "std")]
+impl<T> SendSyncUnwindSafeDrain for T
+where
+ T: Drain + Send + Sync + UnwindSafe + ?Sized,
+{
+}
+
+#[cfg(feature = "std")]
+/// `Drain + Send + Sync + RefUnwindSafe` bound
+///
+/// This type is used to enforce `Drain`s associated with `Logger`s
+/// are thread-safe.
+pub trait SendSyncRefUnwindSafeDrain: Drain + Send + Sync + RefUnwindSafe {}
+
+#[cfg(feature = "std")]
+impl<T> SendSyncRefUnwindSafeDrain for T
+where
+ T: Drain + Send + Sync + RefUnwindSafe + ?Sized,
+{
+}
+
+#[cfg(feature = "std")]
+/// Function that can be used in `MapErr` drain
+pub trait MapErrFn<EI, EO>
+ : 'static + Sync + Send + UnwindSafe + RefUnwindSafe + Fn(EI) -> EO {
+}
+
+#[cfg(feature = "std")]
+impl<T, EI, EO> MapErrFn<EI, EO> for T
+where
+ T: 'static
+ + Sync
+ + Send
+ + ?Sized
+ + UnwindSafe
+ + RefUnwindSafe
+ + Fn(EI) -> EO,
+{
+}
+
+#[cfg(feature = "std")]
+/// Function that can be used in `Filter` drain
+pub trait FilterFn
+ : 'static + Sync + Send + UnwindSafe + RefUnwindSafe + Fn(&Record) -> bool {
+}
+
+#[cfg(feature = "std")]
+impl<T> FilterFn for T
+where
+ T: 'static
+ + Sync
+ + Send
+ + ?Sized
+ + UnwindSafe
+ + RefUnwindSafe
+ + Fn(&Record) -> bool,
+{
+}
+
+#[cfg(not(feature = "std"))]
+/// `Drain + Send + Sync + UnwindSafe` bound
+///
+/// This type is used to enforce `Drain`s associated with `Logger`s
+/// are thread-safe.
+pub trait SendSyncUnwindSafeDrain: Drain + Send + Sync {}
+
+#[cfg(not(feature = "std"))]
+impl<T> SendSyncUnwindSafeDrain for T
+where
+ T: Drain + Send + Sync + ?Sized,
+{
+}
+
+#[cfg(not(feature = "std"))]
+/// `Drain + Send + Sync + RefUnwindSafe` bound
+///
+/// This type is used to enforce `Drain`s associated with `Logger`s
+/// are thread-safe.
+pub trait SendSyncRefUnwindSafeDrain: Drain + Send + Sync {}
+
+#[cfg(not(feature = "std"))]
+impl<T> SendSyncRefUnwindSafeDrain for T
+where
+ T: Drain + Send + Sync + ?Sized,
+{
+}
+
+#[cfg(feature = "std")]
+/// `Drain + Send + RefUnwindSafe` bound
+pub trait SendRefUnwindSafeDrain: Drain + Send + RefUnwindSafe {}
+
+#[cfg(feature = "std")]
+impl<T> SendRefUnwindSafeDrain for T
+where
+ T: Drain + Send + RefUnwindSafe + ?Sized,
+{
+}
+
+#[cfg(not(feature = "std"))]
+/// `Drain + Send + RefUnwindSafe` bound
+pub trait SendRefUnwindSafeDrain: Drain + Send {}
+
+#[cfg(not(feature = "std"))]
+impl<T> SendRefUnwindSafeDrain for T
+where
+ T: Drain + Send + ?Sized,
+{
+}
+
+#[cfg(not(feature = "std"))]
+/// Function that can be used in `MapErr` drain
+pub trait MapErrFn<EI, EO>: 'static + Sync + Send + Fn(EI) -> EO {}
+
+#[cfg(not(feature = "std"))]
+impl<T, EI, EO> MapErrFn<EI, EO> for T
+where
+ T: 'static + Sync + Send + ?Sized + Fn(EI) -> EO,
+{
+}
+
+#[cfg(not(feature = "std"))]
+/// Function that can be used in `Filter` drain
+pub trait FilterFn: 'static + Sync + Send + Fn(&Record) -> bool {}
+
+#[cfg(not(feature = "std"))]
+impl<T> FilterFn for T
+where
+ T: 'static + Sync + Send + ?Sized + Fn(&Record) -> bool,
+{
+}
+
+impl<D: Drain + ?Sized> Drain for Box<D> {
+ type Ok = D::Ok;
+ type Err = D::Err;
+ fn log(
+ &self,
+ record: &Record,
+ o: &OwnedKVList,
+ ) -> result::Result<Self::Ok, D::Err> {
+ (**self).log(record, o)
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ (**self).is_enabled(level)
+ }
+}
+
+impl<D: Drain + ?Sized> Drain for Arc<D> {
+ type Ok = D::Ok;
+ type Err = D::Err;
+ fn log(
+ &self,
+ record: &Record,
+ o: &OwnedKVList,
+ ) -> result::Result<Self::Ok, D::Err> {
+ (**self).log(record, o)
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ (**self).is_enabled(level)
+ }
+}
+
+/// `Drain` discarding everything
+///
+/// `/dev/null` of `Drain`s
+#[derive(Debug, Copy, Clone)]
+pub struct Discard;
+
+impl Drain for Discard {
+ type Ok = ();
+ type Err = Never;
+ fn log(&self, _: &Record, _: &OwnedKVList) -> result::Result<(), Never> {
+ Ok(())
+ }
+ #[inline]
+ fn is_enabled(&self, _1: Level) -> bool {
+ false
+ }
+}
+
+/// `Drain` filtering records
+///
+/// Wraps another `Drain` and passes `Record`s to it, only if they satisfy a
+/// given condition.
+#[derive(Debug, Clone)]
+pub struct Filter<D: Drain, F>(pub D, pub F)
+where
+ F: Fn(&Record) -> bool + 'static + Send + Sync;
+
+impl<D: Drain, F> Filter<D, F>
+where
+ F: FilterFn,
+{
+ /// Create `Filter` wrapping given `drain`
+ pub fn new(drain: D, cond: F) -> Self {
+ Filter(drain, cond)
+ }
+}
+
+impl<D: Drain, F> Drain for Filter<D, F>
+where
+ F: FilterFn,
+{
+ type Ok = Option<D::Ok>;
+ type Err = D::Err;
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ if (self.1)(record) {
+ Ok(Some(self.0.log(record, logger_values)?))
+ } else {
+ Ok(None)
+ }
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ /*
+ * This is one of the reasons we can't guarantee the value is actually logged.
+ * The filter function is given dynamic control over whether or not the record is logged
+ * and could filter stuff out even if the log level is supposed to be enabled
+ */
+ self.0.is_enabled(level)
+ }
+}
+
+/// `Drain` filtering records by `Record` logging level
+///
+/// Wraps a drain and passes records to it, only
+/// if their level is at least given level.
+///
+/// TODO: Remove this type. This drain is a special case of `Filter`, but
+/// because `Filter` can not use static dispatch ATM due to Rust limitations
+/// that will be lifted in the future, it is a standalone type.
+/// Reference: https://github.com/rust-lang/rust/issues/34511
+#[derive(Debug, Clone)]
+pub struct LevelFilter<D: Drain>(pub D, pub Level);
+
+impl<D: Drain> LevelFilter<D> {
+ /// Create `LevelFilter`
+ pub fn new(drain: D, level: Level) -> Self {
+ LevelFilter(drain, level)
+ }
+}
+
+impl<D: Drain> Drain for LevelFilter<D> {
+ type Ok = Option<D::Ok>;
+ type Err = D::Err;
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ if record.level().is_at_least(self.1) {
+ Ok(Some(self.0.log(record, logger_values)?))
+ } else {
+ Ok(None)
+ }
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ level.is_at_least(self.1) && self.0.is_enabled(level)
+ }
+}
+
+/// `Drain` mapping error returned by another `Drain`
+///
+/// See `Drain::map_err` for convenience function.
+pub struct MapError<D: Drain, E> {
+ drain: D,
+ // eliminated dynamic dispatch, after rust learns `-> impl Trait`
+ map_fn: Box<MapErrFn<D::Err, E, Output = E>>,
+}
+
+impl<D: Drain, E> MapError<D, E> {
+ /// Create `Filter` wrapping given `drain`
+ pub fn new<F>(drain: D, map_fn: F) -> Self
+ where
+ F: MapErrFn<<D as Drain>::Err, E>,
+ {
+ MapError {
+ drain: drain,
+ map_fn: Box::new(map_fn),
+ }
+ }
+}
+
+impl<D: Drain, E> Drain for MapError<D, E> {
+ type Ok = D::Ok;
+ type Err = E;
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ self.drain
+ .log(record, logger_values)
+ .map_err(|e| (self.map_fn)(e))
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ self.drain.is_enabled(level)
+ }
+}
+
+/// `Drain` duplicating records into two other `Drain`s
+///
+/// Can be nested for more than two outputs.
+#[derive(Debug, Clone)]
+pub struct Duplicate<D1: Drain, D2: Drain>(pub D1, pub D2);
+
+impl<D1: Drain, D2: Drain> Duplicate<D1, D2> {
+ /// Create `Duplicate`
+ pub fn new(drain1: D1, drain2: D2) -> Self {
+ Duplicate(drain1, drain2)
+ }
+}
+
+impl<D1: Drain, D2: Drain> Drain for Duplicate<D1, D2> {
+ type Ok = (D1::Ok, D2::Ok);
+ type Err = (
+ result::Result<D1::Ok, D1::Err>,
+ result::Result<D2::Ok, D2::Err>,
+ );
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ let res1 = self.0.log(record, logger_values);
+ let res2 = self.1.log(record, logger_values);
+
+ match (res1, res2) {
+ (Ok(o1), Ok(o2)) => Ok((o1, o2)),
+ (r1, r2) => Err((r1, r2)),
+ }
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ self.0.is_enabled(level) || self.1.is_enabled(level)
+ }
+}
+
+/// `Drain` panicking on error
+///
+/// `Logger` requires a root drain to handle all errors (`Drain::Error == ()`),
+/// `Fuse` will wrap a `Drain` and panic if it returns any errors.
+///
+/// Note: `Drain::Err` must implement `Display` (for displaying on panic). It's
+/// easy to create your own `Fuse` drain if this requirement can't be fulfilled.
+#[derive(Debug, Clone)]
+pub struct Fuse<D: Drain>(pub D)
+where
+ D::Err: fmt::Debug;
+
+impl<D: Drain> Fuse<D>
+where
+ D::Err: fmt::Debug,
+{
+ /// Create `Fuse` wrapping given `drain`
+ pub fn new(drain: D) -> Self {
+ Fuse(drain)
+ }
+}
+
+impl<D: Drain> Drain for Fuse<D>
+where
+ D::Err: fmt::Debug,
+{
+ type Ok = ();
+ type Err = Never;
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Never> {
+ let _ = self.0
+ .log(record, logger_values)
+ .unwrap_or_else(|e| panic!("slog::Fuse Drain: {:?}", e));
+ Ok(())
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ self.0.is_enabled(level)
+ }
+}
+
+/// `Drain` ignoring result
+///
+/// `Logger` requires a root drain to handle all errors (`Drain::Err=()`), and
+/// returns nothing (`Drain::Ok=()`) `IgnoreResult` will ignore any result
+/// returned by the `Drain` it wraps.
+#[derive(Clone)]
+pub struct IgnoreResult<D: Drain> {
+ drain: D,
+}
+
+impl<D: Drain> IgnoreResult<D> {
+ /// Create `IgnoreResult` wrapping `drain`
+ pub fn new(drain: D) -> Self {
+ IgnoreResult { drain: drain }
+ }
+}
+
+impl<D: Drain> Drain for IgnoreResult<D> {
+ type Ok = ();
+ type Err = Never;
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<(), Never> {
+ let _ = self.drain.log(record, logger_values);
+ Ok(())
+ }
+
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ self.drain.is_enabled(level)
+ }
+}
+
+/// Error returned by `Mutex<D : Drain>`
+#[cfg(feature = "std")]
+#[derive(Clone)]
+pub enum MutexDrainError<D: Drain> {
+ /// Error acquiring mutex
+ Mutex,
+ /// Error returned by drain
+ Drain(D::Err),
+}
+
+#[cfg(feature = "std")]
+impl<D> fmt::Debug for MutexDrainError<D>
+where
+ D: Drain,
+ D::Err: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
+ match *self {
+ MutexDrainError::Mutex => write!(f, "MutexDrainError::Mutex"),
+ MutexDrainError::Drain(ref e) => e.fmt(f),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<D> std::error::Error for MutexDrainError<D>
+where
+ D: Drain,
+ D::Err: fmt::Debug + fmt::Display + std::error::Error,
+{
+ fn description(&self) -> &str {
+ match *self {
+ MutexDrainError::Mutex => "Mutex acquire failed",
+ MutexDrainError::Drain(ref e) => e.description(),
+ }
+ }
+
+ fn cause(&self) -> Option<&std::error::Error> {
+ match *self {
+ MutexDrainError::Mutex => None,
+ MutexDrainError::Drain(ref e) => Some(e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a, D: Drain> From<std::sync::PoisonError<std::sync::MutexGuard<'a, D>>>
+ for MutexDrainError<D> {
+ fn from(
+ _: std::sync::PoisonError<std::sync::MutexGuard<'a, D>>,
+ ) -> MutexDrainError<D> {
+ MutexDrainError::Mutex
+ }
+}
+
+#[cfg(feature = "std")]
+impl<D: Drain> fmt::Display for MutexDrainError<D>
+where
+ D::Err: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
+ match *self {
+ MutexDrainError::Mutex => write!(f, "MutexError"),
+ MutexDrainError::Drain(ref e) => write!(f, "{}", e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<D: Drain> Drain for std::sync::Mutex<D> {
+ type Ok = D::Ok;
+ type Err = MutexDrainError<D>;
+ fn log(
+ &self,
+ record: &Record,
+ logger_values: &OwnedKVList,
+ ) -> result::Result<Self::Ok, Self::Err> {
+ let d = self.lock()?;
+ d.log(record, logger_values).map_err(MutexDrainError::Drain)
+ }
+ #[inline]
+ fn is_enabled(&self, level: Level) -> bool {
+ self.lock().ok().map_or(true, |lock| lock.is_enabled(level))
+ }
+}
+// }}}
+
+// {{{ Level & FilterLevel
+/// Official capitalized logging (and logging filtering) level names
+///
+/// In order of `as_usize()`.
+pub static LOG_LEVEL_NAMES: [&'static str; 7] =
+ ["OFF", "CRITICAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
+
+/// Official capitalized logging (and logging filtering) short level names
+///
+/// In order of `as_usize()`.
+pub static LOG_LEVEL_SHORT_NAMES: [&'static str; 7] =
+ ["OFF", "CRIT", "ERRO", "WARN", "INFO", "DEBG", "TRCE"];
+
+/// Logging level associated with a logging `Record`
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub enum Level {
+ /// Critical
+ Critical,
+ /// Error
+ Error,
+ /// Warning
+ Warning,
+ /// Info
+ Info,
+ /// Debug
+ Debug,
+ /// Trace
+ Trace,
+}
+
+/// Logging filtering level
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub enum FilterLevel {
+ /// Log nothing
+ Off,
+ /// Log critical level only
+ Critical,
+ /// Log only error level and above
+ Error,
+ /// Log only warning level and above
+ Warning,
+ /// Log only info level and above
+ Info,
+ /// Log only debug level and above
+ Debug,
+ /// Log everything
+ Trace,
+}
+
+impl Level {
+ /// Convert to `str` from `LOG_LEVEL_SHORT_NAMES`
+ pub fn as_short_str(&self) -> &'static str {
+ LOG_LEVEL_SHORT_NAMES[self.as_usize()]
+ }
+
+ /// Convert to `str` from `LOG_LEVEL_NAMES`
+ pub fn as_str(&self) -> &'static str {
+ LOG_LEVEL_NAMES[self.as_usize()]
+ }
+
+ /// Cast `Level` to ordering integer
+ ///
+ /// `Critical` is the smallest and `Trace` the biggest value
+ #[inline]
+ pub fn as_usize(&self) -> usize {
+ match *self {
+ Level::Critical => 1,
+ Level::Error => 2,
+ Level::Warning => 3,
+ Level::Info => 4,
+ Level::Debug => 5,
+ Level::Trace => 6,
+ }
+ }
+
+ /// Get a `Level` from an `usize`
+ ///
+ /// This complements `as_usize`
+ #[inline]
+ pub fn from_usize(u: usize) -> Option<Level> {
+ match u {
+ 1 => Some(Level::Critical),
+ 2 => Some(Level::Error),
+ 3 => Some(Level::Warning),
+ 4 => Some(Level::Info),
+ 5 => Some(Level::Debug),
+ 6 => Some(Level::Trace),
+ _ => None,
+ }
+ }
+}
+
+impl FilterLevel {
+ /// Convert to `str` from `LOG_LEVEL_SHORT_NAMES`
+ pub fn as_short_str(&self) -> &'static str {
+ LOG_LEVEL_SHORT_NAMES[self.as_usize()]
+ }
+
+ /// Convert to `str` from `LOG_LEVEL_NAMES`
+ pub fn as_str(&self) -> &'static str {
+ LOG_LEVEL_NAMES[self.as_usize()]
+ }
+
+ /// Convert to `usize` value
+ ///
+ /// `Off` is 0, and `Trace` 6
+ #[inline]
+ pub fn as_usize(&self) -> usize {
+ match *self {
+ FilterLevel::Off => 0,
+ FilterLevel::Critical => 1,
+ FilterLevel::Error => 2,
+ FilterLevel::Warning => 3,
+ FilterLevel::Info => 4,
+ FilterLevel::Debug => 5,
+ FilterLevel::Trace => 6,
+ }
+ }
+
+ /// Get a `FilterLevel` from an `usize`
+ ///
+ /// This complements `as_usize`
+ #[inline]
+ pub fn from_usize(u: usize) -> Option<FilterLevel> {
+ match u {
+ 0 => Some(FilterLevel::Off),
+ 1 => Some(FilterLevel::Critical),
+ 2 => Some(FilterLevel::Error),
+ 3 => Some(FilterLevel::Warning),
+ 4 => Some(FilterLevel::Info),
+ 5 => Some(FilterLevel::Debug),
+ 6 => Some(FilterLevel::Trace),
+ _ => None,
+ }
+ }
+
+ /// Maximum logging level (log everything)
+ #[inline]
+ pub fn max() -> Self {
+ FilterLevel::Trace
+ }
+
+ /// Minimum logging level (log nothing)
+ #[inline]
+ pub fn min() -> Self {
+ FilterLevel::Off
+ }
+
+ /// Check if message with given level should be logged
+ pub fn accepts(self, level: Level) -> bool {
+ self.as_usize() >= level.as_usize()
+ }
+}
+
+impl FromStr for Level {
+ type Err = ();
+ fn from_str(name: &str) -> core::result::Result<Level, ()> {
+ index_of_log_level_name(name)
+ .and_then(|idx| Level::from_usize(idx))
+ .ok_or(())
+ }
+}
+
+impl FromStr for FilterLevel {
+ type Err = ();
+ fn from_str(name: &str) -> core::result::Result<FilterLevel, ()> {
+ index_of_log_level_name(name)
+ .and_then(|idx| FilterLevel::from_usize(idx))
+ .ok_or(())
+ }
+}
+
+fn index_of_log_level_name(name: &str) -> Option<usize> {
+ index_of_str_ignore_case(&LOG_LEVEL_NAMES, name)
+ .or_else(|| index_of_str_ignore_case(&LOG_LEVEL_SHORT_NAMES, name))
+}
+
+fn index_of_str_ignore_case(haystack: &[&str], needle: &str) -> Option<usize> {
+ if needle.is_empty() {
+ return None;
+ }
+ haystack.iter()
+ // This will never panic because haystack has only ASCII characters
+ .map(|hay| &hay[..needle.len().min(hay.len())])
+ .position(|hay| hay.eq_ignore_ascii_case(needle))
+}
+
+impl fmt::Display for Level {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_short_str())
+ }
+}
+
+impl fmt::Display for FilterLevel {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_short_str())
+ }
+}
+
+impl Level {
+ /// Returns true if `self` is at least `level` logging level
+ #[inline]
+ pub fn is_at_least(&self, level: Self) -> bool {
+ self.as_usize() <= level.as_usize()
+ }
+}
+
+#[test]
+fn level_at_least() {
+ assert!(Level::Debug.is_at_least(Level::Debug));
+ assert!(Level::Debug.is_at_least(Level::Trace));
+ assert!(!Level::Debug.is_at_least(Level::Info));
+}
+
+#[test]
+fn filter_level_sanity() {
+ assert!(Level::Critical.as_usize() > FilterLevel::Off.as_usize());
+ assert!(Level::Critical.as_usize() == FilterLevel::Critical.as_usize());
+ assert!(Level::Trace.as_usize() == FilterLevel::Trace.as_usize());
+}
+
+#[test]
+fn level_from_str() {
+ refute_from_str::<Level>("off");
+ assert_from_str(Level::Critical, "critical");
+ assert_from_str(Level::Critical, "crit");
+ assert_from_str(Level::Error, "error");
+ assert_from_str(Level::Error, "erro");
+ assert_from_str(Level::Warning, "warn");
+ assert_from_str(Level::Info, "info");
+ assert_from_str(Level::Debug, "debug");
+ assert_from_str(Level::Debug, "debg");
+ assert_from_str(Level::Trace, "trace");
+ assert_from_str(Level::Trace, "trce");
+
+ assert_from_str(Level::Info, "Info");
+ assert_from_str(Level::Info, "INFO");
+ assert_from_str(Level::Info, "iNfO");
+
+ refute_from_str::<Level>("");
+ assert_from_str(Level::Info, "i");
+ assert_from_str(Level::Info, "in");
+ assert_from_str(Level::Info, "inf");
+ refute_from_str::<Level>("infor");
+
+ refute_from_str::<Level>("?");
+ refute_from_str::<Level>("info ");
+ refute_from_str::<Level>(" info");
+ refute_from_str::<Level>("desinfo");
+}
+
+#[test]
+fn filter_level_from_str() {
+ assert_from_str(FilterLevel::Off, "off");
+ assert_from_str(FilterLevel::Critical, "critical");
+ assert_from_str(FilterLevel::Critical, "crit");
+ assert_from_str(FilterLevel::Error, "error");
+ assert_from_str(FilterLevel::Error, "erro");
+ assert_from_str(FilterLevel::Warning, "warn");
+ assert_from_str(FilterLevel::Info, "info");
+ assert_from_str(FilterLevel::Debug, "debug");
+ assert_from_str(FilterLevel::Debug, "debg");
+ assert_from_str(FilterLevel::Trace, "trace");
+ assert_from_str(FilterLevel::Trace, "trce");
+
+ assert_from_str(FilterLevel::Info, "Info");
+ assert_from_str(FilterLevel::Info, "INFO");
+ assert_from_str(FilterLevel::Info, "iNfO");
+
+ refute_from_str::<FilterLevel>("");
+ assert_from_str(FilterLevel::Info, "i");
+ assert_from_str(FilterLevel::Info, "in");
+ assert_from_str(FilterLevel::Info, "inf");
+ refute_from_str::<FilterLevel>("infor");
+
+ refute_from_str::<FilterLevel>("?");
+ refute_from_str::<FilterLevel>("info ");
+ refute_from_str::<FilterLevel>(" info");
+ refute_from_str::<FilterLevel>("desinfo");
+}
+
+#[cfg(test)]
+fn assert_from_str<T>(expected: T, level_str: &str)
+ where
+ T: FromStr + fmt::Debug + PartialEq,
+ T::Err: fmt::Debug {
+ let result = T::from_str(level_str);
+
+ let actual = result.unwrap_or_else(|e| {
+ panic!("Failed to parse filter level '{}': {:?}", level_str, e)
+ });
+ assert_eq!(expected, actual, "Invalid filter level parsed from '{}'", level_str);
+}
+
+#[cfg(test)]
+fn refute_from_str<T>(level_str: &str)
+ where
+ T: FromStr + fmt::Debug {
+ let result = T::from_str(level_str);
+
+ if let Ok(level) = result {
+ panic!("Parsing filter level '{}' succeeded: {:?}", level_str, level)
+ }
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn level_to_string_and_from_str_are_compatible() {
+ assert_to_string_from_str(Level::Critical);
+ assert_to_string_from_str(Level::Error);
+ assert_to_string_from_str(Level::Warning);
+ assert_to_string_from_str(Level::Info);
+ assert_to_string_from_str(Level::Debug);
+ assert_to_string_from_str(Level::Trace);
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn filter_level_to_string_and_from_str_are_compatible() {
+ assert_to_string_from_str(FilterLevel::Off);
+ assert_to_string_from_str(FilterLevel::Critical);
+ assert_to_string_from_str(FilterLevel::Error);
+ assert_to_string_from_str(FilterLevel::Warning);
+ assert_to_string_from_str(FilterLevel::Info);
+ assert_to_string_from_str(FilterLevel::Debug);
+ assert_to_string_from_str(FilterLevel::Trace);
+}
+
+#[cfg(all(test, feature = "std"))]
+fn assert_to_string_from_str<T>(expected: T)
+ where
+ T: std::string::ToString + FromStr + PartialEq + fmt::Debug,
+ <T as FromStr>::Err: fmt::Debug {
+ let string = expected.to_string();
+
+ let actual = T::from_str(&string)
+ .expect(&format!("Failed to parse string representation of {:?}", expected));
+
+ assert_eq!(expected, actual, "Invalid value parsed from string representation of {:?}", actual);
+}
+
+#[test]
+fn filter_level_accepts_tests() {
+ assert_eq!(true, FilterLevel::Warning.accepts(Level::Error));
+ assert_eq!(true, FilterLevel::Warning.accepts(Level::Warning));
+ assert_eq!(false, FilterLevel::Warning.accepts(Level::Info));
+ assert_eq!(false, FilterLevel::Off.accepts(Level::Critical));
+}
+// }}}
+
+// {{{ Record
+#[doc(hidden)]
+#[derive(Clone, Copy)]
+pub struct RecordLocation {
+ /// File
+ pub file: &'static str,
+ /// Line
+ pub line: u32,
+ /// Column (currently not implemented)
+ pub column: u32,
+ /// Function (currently not implemented)
+ pub function: &'static str,
+ /// Module
+ pub module: &'static str,
+}
+/// Information that can be static in the given record thus allowing to optimize
+/// record creation to be done mostly at compile-time.
+///
+/// This should be constructed via the `record_static!` macro.
+pub struct RecordStatic<'a> {
+ /// Code location
+ #[doc(hidden)]
+ pub location: &'a RecordLocation,
+ /// Tag
+ #[doc(hidden)]
+ pub tag: &'a str,
+ /// Logging level
+ #[doc(hidden)]
+ pub level: Level,
+}
+
+/// One logging record
+///
+/// Corresponds to one logging statement like `info!(...)` and carries all its
+/// data: eg. message, immediate key-value pairs and key-value pairs of `Logger`
+/// used to execute it.
+///
+/// Record is passed to a `Logger`, which delivers it to its own `Drain`,
+/// where actual logging processing is implemented.
+pub struct Record<'a> {
+ rstatic: &'a RecordStatic<'a>,
+ msg: &'a fmt::Arguments<'a>,
+ kv: BorrowedKV<'a>,
+}
+
+impl<'a> Record<'a> {
+ /// Create a new `Record`
+ ///
+ /// Most of the time, it is slightly more performant to construct a `Record`
+ /// via the `record!` macro because it enforces that the *entire*
+ /// `RecordStatic` is built at compile-time.
+ ///
+ /// Use this if runtime record creation is a requirement, as is the case with
+ /// [slog-async](https://docs.rs/slog-async/latest/slog_async/struct.Async.html),
+ /// for example.
+ #[inline]
+ pub fn new(
+ s: &'a RecordStatic<'a>,
+ msg: &'a fmt::Arguments<'a>,
+ kv: BorrowedKV<'a>,
+ ) -> Self {
+ Record {
+ rstatic: s,
+ msg: msg,
+ kv: kv,
+ }
+ }
+
+ /// Get a log record message
+ pub fn msg(&self) -> &fmt::Arguments {
+ self.msg
+ }
+
+ /// Get record logging level
+ pub fn level(&self) -> Level {
+ self.rstatic.level
+ }
+
+ /// Get line number
+ pub fn line(&self) -> u32 {
+ self.rstatic.location.line
+ }
+
+ /// Get line number
+ pub fn location(&self) -> &RecordLocation {
+ self.rstatic.location
+ }
+
+ /// Get error column
+ pub fn column(&self) -> u32 {
+ self.rstatic.location.column
+ }
+
+ /// Get file path
+ pub fn file(&self) -> &'static str {
+ self.rstatic.location.file
+ }
+
+ /// Get tag
+ ///
+ /// Tag is information that can be attached to `Record` that is not meant
+ /// to be part of the normal key-value pairs, but only as an ad-hoc control
+ /// flag for quick lookup in the `Drain`s. As such should be used carefully
+ /// and mostly in application code (as opposed to libraries) - where tag
+ /// meaning across the system can be coordinated. When used in libraries,
+ /// make sure to prefix it with something reasonably distinct, like create
+ /// name.
+ pub fn tag(&self) -> &str {
+ self.rstatic.tag
+ }
+
+ /// Get module
+ pub fn module(&self) -> &'static str {
+ self.rstatic.location.module
+ }
+
+ /// Get function (placeholder)
+ ///
+ /// There's currently no way to obtain that information
+ /// in Rust at compile time, so it is not implemented.
+ ///
+ /// It will be implemented at first opportunity, and
+ /// it will not be considered a breaking change.
+ pub fn function(&self) -> &'static str {
+ self.rstatic.location.function
+ }
+
+ /// Get key-value pairs
+ pub fn kv(&self) -> BorrowedKV {
+ BorrowedKV(self.kv.0)
+ }
+}
+// }}}
+
+// {{{ Serializer
+
+#[cfg(macro_workaround)]
+macro_rules! impl_default_as_fmt{
+ (#[$m:meta] $($t:tt)+) => {
+ #[$m]
+ impl_default_as_fmt!($($t)*);
+ };
+ ($t:ty => $f:ident) => {
+ #[allow(missing_docs)]
+ fn $f(&mut self, key : Key, val : $t)
+ -> Result {
+ self.emit_arguments(key, &format_args!("{}", val))
+ }
+ };
+}
+
+#[cfg(not(macro_workaround))]
+macro_rules! impl_default_as_fmt{
+ ($(#[$m:meta])* $t:ty => $f:ident) => {
+ $(#[$m])*
+ fn $f(&mut self, key : Key, val : $t)
+ -> Result {
+ self.emit_arguments(key, &format_args!("{}", val))
+ }
+ };
+}
+
+/// This is a workaround to be able to pass &mut Serializer, from
+/// `Serializer::emit_serde` default implementation. `&Self` can't be casted to
+/// `&Serializer` (without : Sized, which break object safety), but it can be
+/// used as <T: Serializer>.
+#[cfg(feature = "nested-values")]
+struct SerializerForward<'a, T: 'a + ?Sized>(&'a mut T);
+
+#[cfg(feature = "nested-values")]
+impl<'a, T: Serializer + 'a + ?Sized> Serializer for SerializerForward<'a, T> {
+ fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> Result {
+ self.0.emit_arguments(key, val)
+ }
+
+ #[cfg(feature = "nested-values")]
+ fn emit_serde(&mut self, _key: Key, _value: &SerdeValue) -> Result {
+ panic!();
+ }
+}
+
+/// Serializer
+///
+/// Drains using `Format` will internally use
+/// types implementing this trait.
+pub trait Serializer {
+ impl_default_as_fmt! {
+ /// Emit `usize`
+ usize => emit_usize
+ }
+ impl_default_as_fmt! {
+ /// Emit `isize`
+ isize => emit_isize
+ }
+ impl_default_as_fmt! {
+ /// Emit `bool`
+ bool => emit_bool
+ }
+ impl_default_as_fmt! {
+ /// Emit `char`
+ char => emit_char
+ }
+ impl_default_as_fmt! {
+ /// Emit `u8`
+ u8 => emit_u8
+ }
+ impl_default_as_fmt! {
+ /// Emit `i8`
+ i8 => emit_i8
+ }
+ impl_default_as_fmt! {
+ /// Emit `u16`
+ u16 => emit_u16
+ }
+ impl_default_as_fmt! {
+ /// Emit `i16`
+ i16 => emit_i16
+ }
+ impl_default_as_fmt! {
+ /// Emit `u32`
+ u32 => emit_u32
+ }
+ impl_default_as_fmt! {
+ /// Emit `i32`
+ i32 => emit_i32
+ }
+ impl_default_as_fmt! {
+ /// Emit `f32`
+ f32 => emit_f32
+ }
+ impl_default_as_fmt! {
+ /// Emit `u64`
+ u64 => emit_u64
+ }
+ impl_default_as_fmt! {
+ /// Emit `i64`
+ i64 => emit_i64
+ }
+ impl_default_as_fmt! {
+ /// Emit `f64`
+ f64 => emit_f64
+ }
+ impl_default_as_fmt! {
+ /// Emit `u128`
+ #[cfg(integer128)]
+ u128 => emit_u128
+ }
+ impl_default_as_fmt! {
+ /// Emit `i128`
+ #[cfg(integer128)]
+ i128 => emit_i128
+ }
+ impl_default_as_fmt! {
+ /// Emit `&str`
+ &str => emit_str
+ }
+
+ /// Emit `()`
+ fn emit_unit(&mut self, key: Key) -> Result {
+ self.emit_arguments(key, &format_args!("()"))
+ }
+
+ /// Emit `None`
+ fn emit_none(&mut self, key: Key) -> Result {
+ self.emit_arguments(key, &format_args!(""))
+ }
+
+ /// Emit `fmt::Arguments`
+ ///
+ /// This is the only method that has to implemented, but for performance and
+ /// to retain type information most serious `Serializer`s will want to
+ /// implement all other methods as well.
+ fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> Result;
+
+ /// Emit a value implementing
+ /// [`serde::Serialize`](https://docs.rs/serde/1/serde/trait.Serialize.html)
+ ///
+ /// This is especially useful for composite values, eg. structs as Json values, or sequences.
+ ///
+ /// To prevent pulling-in `serde` dependency, this is an extension behind a
+ /// `serde` feature flag.
+ ///
+ /// The value needs to implement `SerdeValue`.
+ #[cfg(feature = "nested-values")]
+ fn emit_serde(&mut self, key: Key, value: &SerdeValue) -> Result {
+ value.serialize_fallback(key, &mut SerializerForward(self))
+ }
+
+ /// Emit a type implementing `std::error::Error`
+ ///
+ /// Error values are a bit special as their `Display` implementation doesn't show full
+ /// information about the type but must be retrieved using `source()`. This can be used
+ /// for formatting sources of errors differntly.
+ ///
+ /// The default implementation of this method formats the sources separated with `: `.
+ /// Serializers are encouraged to take advantage of the type information and format it as
+ /// appropriate.
+ ///
+ /// This method is only available in `std` because the `Error` trait is not available
+ /// without `std`.
+ #[cfg(feature = "std")]
+ fn emit_error(&mut self, key: Key, error: &(std::error::Error + 'static)) -> Result {
+ self.emit_arguments(key, &format_args!("{}", ErrorAsFmt(error)))
+ }
+}
+
+/// Serializer to closure adapter.
+///
+/// Formats all arguments as `fmt::Arguments` and passes them to a given closure.
+struct AsFmtSerializer<F>(pub F)
+where
+ F: for<'a> FnMut(Key, fmt::Arguments<'a>) -> Result;
+
+impl<F> Serializer for AsFmtSerializer<F>
+where
+ F: for<'a> FnMut(Key, fmt::Arguments<'a>) -> Result,
+{
+ fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> Result {
+ (self.0)(key, *val)
+ }
+}
+
+/// A helper for formatting std::error::Error types by joining sources with `: `
+///
+/// This avoids allocation in the default implementation of `Serializer::emit_error()`.
+/// This is only enabled with `std` as the trait is only available there.
+#[cfg(feature = "std")]
+struct ErrorAsFmt<'a>(pub &'a (std::error::Error + 'static));
+
+#[cfg(feature = "std")]
+impl<'a> fmt::Display for ErrorAsFmt<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // For backwards compatibility
+ // This is fine because we don't need downcasting
+ #![allow(deprecated)]
+ write!(f, "{}", self.0)?;
+ let mut error = self.0.cause();
+ while let Some(source) = error {
+ write!(f, ": {}", source)?;
+ error = source.cause();
+ }
+ Ok(())
+ }
+}
+
+// }}}
+
+// {{{ serde
+/// A value that can be serialized via serde
+///
+/// This is useful for implementing nested values, like sequences or structures.
+#[cfg(feature = "nested-values")]
+pub trait SerdeValue: erased_serde::Serialize + Value {
+ /// Serialize the value in a way that is compatible with `slog::Serializer`s
+ /// that do not support serde.
+ ///
+ /// The implementation should *not* call `slog::Serialize::serialize`
+ /// on itself, as it will lead to infinite recursion.
+ ///
+ /// Default implementation is provided, but it returns error, so use it
+ /// only for internal types in systems and libraries where `serde` is always
+ /// enabled.
+ fn serialize_fallback(
+ &self,
+ _key: Key,
+ _serializer: &mut Serializer,
+ ) -> Result<()> {
+ Err(Error::Other)
+ }
+
+ /// Convert to `erased_serialize::Serialize` of the underlying value,
+ /// so `slog::Serializer`s can use it to serialize via `serde`.
+ fn as_serde(&self) -> &erased_serde::Serialize;
+
+ /// Convert to a boxed value that can be sent across threads
+ ///
+ /// This enables functionality like `slog-async` and similar.
+ fn to_sendable(&self) -> Box<SerdeValue + Send + 'static>;
+}
+
+// }}}
+
+// {{{ Value
+/// Value that can be serialized
+///
+/// Types that implement this type implement custom serialization in the
+/// structured part of the log macros. Without an implementation of `Value` for
+/// your type you must emit using either the `?` "debug", `#?` "pretty-debug",
+/// `%` "display", `#%` "alternate display" or [`SerdeValue`](trait.SerdeValue.html)
+/// (if you have the `nested-values` feature enabled) formatters.
+///
+/// # Example
+///
+/// ```
+/// use slog::{Key, Value, Record, Result, Serializer};
+/// struct MyNewType(i64);
+///
+/// impl Value for MyNewType {
+/// fn serialize(&self, _rec: &Record, key: Key, serializer: &mut Serializer) -> Result {
+/// serializer.emit_i64(key, self.0)
+/// }
+/// }
+/// ```
+///
+/// See also [`KV`](trait.KV.html) for formatting both the key and value.
+pub trait Value {
+ /// Serialize self into `Serializer`
+ ///
+ /// Structs implementing this trait should generally
+ /// only call respective methods of `serializer`.
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result;
+}
+
+impl<'a, V> Value for &'a V
+where
+ V: Value + ?Sized,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (*self).serialize(record, key, serializer)
+ }
+}
+
+macro_rules! impl_value_for{
+ ($t:ty, $f:ident) => {
+ impl Value for $t {
+ fn serialize(&self,
+ _record : &Record,
+ key : Key,
+ serializer : &mut Serializer
+ ) -> Result {
+ serializer.$f(key, *self)
+ }
+ }
+ };
+}
+
+impl_value_for!(usize, emit_usize);
+impl_value_for!(isize, emit_isize);
+impl_value_for!(bool, emit_bool);
+impl_value_for!(char, emit_char);
+impl_value_for!(u8, emit_u8);
+impl_value_for!(i8, emit_i8);
+impl_value_for!(u16, emit_u16);
+impl_value_for!(i16, emit_i16);
+impl_value_for!(u32, emit_u32);
+impl_value_for!(i32, emit_i32);
+impl_value_for!(f32, emit_f32);
+impl_value_for!(u64, emit_u64);
+impl_value_for!(i64, emit_i64);
+impl_value_for!(f64, emit_f64);
+#[cfg(integer128)]
+impl_value_for!(u128, emit_u128);
+#[cfg(integer128)]
+impl_value_for!(i128, emit_i128);
+
+impl Value for () {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_unit(key)
+ }
+}
+
+impl Value for str {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_str(key, self)
+ }
+}
+
+impl<'a> Value for fmt::Arguments<'a> {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_arguments(key, self)
+ }
+}
+
+impl Value for String {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_str(key, self.as_str())
+ }
+}
+
+impl<T: Value> Value for Option<T> {
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ match *self {
+ Some(ref s) => s.serialize(record, key, serializer),
+ None => serializer.emit_none(key),
+ }
+ }
+}
+
+impl<T> Value for Box<T>
+where
+ T: Value + ?Sized,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (**self).serialize(record, key, serializer)
+ }
+}
+impl<T> Value for Arc<T>
+where
+ T: Value + ?Sized,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (**self).serialize(record, key, serializer)
+ }
+}
+
+impl<T> Value for Rc<T>
+where
+ T: Value,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (**self).serialize(record, key, serializer)
+ }
+}
+
+impl<T> Value for core::num::Wrapping<T>
+where
+ T: Value,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ self.0.serialize(record, key, serializer)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<'a> Value for std::path::Display<'a> {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_arguments(key, &format_args!("{}", *self))
+ }
+}
+
+#[cfg(feature = "std")]
+impl Value for std::net::SocketAddr {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_arguments(key, &format_args!("{}", self))
+ }
+}
+
+#[cfg(feature = "std")]
+impl Value for std::io::Error {
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_error(key, self)
+ }
+}
+
+/// Explicit lazy-closure `Value`
+pub struct FnValue<V: Value, F>(pub F)
+where
+ F: for<'c, 'd> Fn(&'c Record<'d>) -> V;
+
+impl<'a, V: 'a + Value, F> Value for FnValue<V, F>
+where
+ F: 'a + for<'c, 'd> Fn(&'c Record<'d>) -> V,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (self.0)(record).serialize(record, key, serializer)
+ }
+}
+
+#[deprecated(note = "Renamed to `PushFnValueSerializer`")]
+/// Old name of `PushFnValueSerializer`
+pub type PushFnSerializer<'a> = PushFnValueSerializer<'a>;
+
+/// Handle passed to `PushFnValue` closure
+///
+/// It makes sure only one value is serialized, and will automatically emit
+/// `()` if nothing else was serialized.
+pub struct PushFnValueSerializer<'a> {
+ record: &'a Record<'a>,
+ key: Key,
+ serializer: &'a mut Serializer,
+ done: bool,
+}
+
+impl<'a> PushFnValueSerializer<'a> {
+ #[deprecated(note = "Renamed to `emit`")]
+ /// Emit a value
+ pub fn serialize<'b, S: 'b + Value>(self, s: S) -> Result {
+ self.emit(s)
+ }
+
+ /// Emit a value
+ ///
+ /// This consumes `self` to prevent serializing one value multiple times
+ pub fn emit<'b, S: 'b + Value>(mut self, s: S) -> Result {
+ self.done = true;
+ s.serialize(self.record, self.key.clone(), self.serializer)
+ }
+}
+
+impl<'a> Drop for PushFnValueSerializer<'a> {
+ fn drop(&mut self) {
+ if !self.done {
+ // unfortunately this gives no change to return serialization errors
+ let _ = self.serializer.emit_unit(self.key.clone());
+ }
+ }
+}
+
+/// Lazy `Value` that writes to Serializer
+///
+/// It's more ergonomic for closures used as lazy values to return type
+/// implementing `Serialize`, but sometimes that forces an allocation (eg.
+/// `String`s)
+///
+/// In some cases it might make sense for another closure form to be used - one
+/// taking a serializer as an argument, which avoids lifetimes / allocation
+/// issues.
+///
+/// Generally this method should be used if it avoids a big allocation of
+/// `Serialize`-implementing type in performance-critical logging statement.
+///
+/// ```
+/// #[macro_use]
+/// extern crate slog;
+/// use slog::{PushFnValue, Logger, Discard};
+///
+/// fn main() {
+/// // Create a logger with a key-value printing
+/// // `file:line` string value for every logging statement.
+/// // `Discard` `Drain` used for brevity.
+/// let root = Logger::root(Discard, o!(
+/// "source_location" => PushFnValue(|record , s| {
+/// s.serialize(
+/// format_args!(
+/// "{}:{}",
+/// record.file(),
+/// record.line(),
+/// )
+/// )
+/// })
+/// ));
+/// }
+/// ```
+pub struct PushFnValue<F>(pub F)
+where
+ F: 'static
+ + for<'c, 'd> Fn(&'c Record<'d>, PushFnValueSerializer<'c>) -> Result;
+
+impl<F> Value for PushFnValue<F>
+where
+ F: 'static
+ + for<'c, 'd> Fn(&'c Record<'d>, PushFnValueSerializer<'c>) -> Result,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ let ser = PushFnValueSerializer {
+ record: record,
+ key: key,
+ serializer: serializer,
+ done: false,
+ };
+ (self.0)(record, ser)
+ }
+}
+
+/// A wrapper struct for serializing errors
+///
+/// This struct can be used to wrap types that don't implement `slog::Value` but
+/// do implement `std::error::Error` so that they can be logged.
+/// This is usually not used directly but using `#error` in the macros.
+///
+/// This struct is only available in `std` because the `Error` trait is not available
+/// without `std`.
+#[cfg(feature = "std")]
+pub struct ErrorValue<E: std::error::Error>(pub E);
+
+#[cfg(feature = "std")]
+impl<E> Value for ErrorValue<E>
+where
+ E: 'static + std::error::Error,
+{
+ fn serialize(
+ &self,
+ _record: &Record,
+ key: Key,
+ serializer: &mut Serializer,
+ ) -> Result {
+ serializer.emit_error(key, &self.0)
+ }
+}
+
+// }}}
+
+// {{{ KV
+/// Key-value pair(s) for log events
+///
+/// Zero, one or more key value pairs chained together
+///
+/// Any logging data must implement this trait for slog to be able to use it,
+/// although slog comes with default implementations within its macros (the
+/// `=>` and `kv!` portions of the log macros).
+///
+/// If you don't use this trait, you must emit your structured data by
+/// specifying both key and value in each log event:
+///
+/// ```ignore
+/// info!(logger, "my event"; "type_key" => %my_val);
+/// ```
+///
+/// If you implement this trait, that can become:
+///
+/// ```ignore
+/// info!(logger, "my event"; my_val);
+/// ```
+///
+/// Types implementing this trait can emit multiple key-value pairs, and can
+/// customize their structured representation. The order of emitting them
+/// should be consistent with the way key-value pair hierarchy is traversed:
+/// from data most specific to the logging context to the most general one. Or
+/// in other words: from newest to oldest.
+///
+/// Implementers are are responsible for calling the `emit_*` methods on the
+/// `Serializer` passed in, the `Record` can be used to make display decisions
+/// based on context, but for most plain-value structs you will just call
+/// `emit_*`.
+///
+/// # Example
+///
+/// ```
+/// use slog::{KV, Record, Result, Serializer};
+///
+/// struct MyNewType(i64);
+///
+/// impl KV for MyNewType {
+/// fn serialize(&self, _rec: &Record, serializer: &mut Serializer) -> Result {
+/// serializer.emit_i64("my_new_type", self.0)
+/// }
+/// }
+/// ```
+///
+/// See also [`Value`](trait.Value.html), which allows you to customize just
+/// the right hand side of the `=>` structure macro, and (if you have the
+/// `nested-values` feature enabled) [`SerdeValue`](trait.SerdeValue.html)
+/// which allows emitting anything serde can emit.
+pub trait KV {
+ /// Serialize self into `Serializer`
+ ///
+ /// `KV` should call respective `Serializer` methods
+ /// for each key-value pair it contains.
+ fn serialize(&self, record: &Record, serializer: &mut Serializer)
+ -> Result;
+}
+
+impl<'a, T> KV for &'a T
+where
+ T: KV,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (**self).serialize(record, serializer)
+ }
+}
+
+#[cfg(feature = "nothreads")]
+/// Thread-local safety bound for `KV`
+///
+/// This type is used to enforce `KV`s stored in `Logger`s are thread-safe.
+pub trait SendSyncRefUnwindSafeKV: KV {}
+
+#[cfg(feature = "nothreads")]
+impl<T> SendSyncRefUnwindSafeKV for T where T: KV + ?Sized {}
+
+#[cfg(all(not(feature = "nothreads"), feature = "std"))]
+/// This type is used to enforce `KV`s stored in `Logger`s are thread-safe.
+pub trait SendSyncRefUnwindSafeKV: KV + Send + Sync + RefUnwindSafe {}
+
+#[cfg(all(not(feature = "nothreads"), feature = "std"))]
+impl<T> SendSyncRefUnwindSafeKV for T where T: KV + Send + Sync + RefUnwindSafe + ?Sized {}
+
+#[cfg(all(not(feature = "nothreads"), not(feature = "std")))]
+/// This type is used to enforce `KV`s stored in `Logger`s are thread-safe.
+pub trait SendSyncRefUnwindSafeKV: KV + Send + Sync {}
+
+#[cfg(all(not(feature = "nothreads"), not(feature = "std")))]
+impl<T> SendSyncRefUnwindSafeKV for T where T: KV + Send + Sync + ?Sized {}
+
+/// Single pair `Key` and `Value`
+pub struct SingleKV<V>(pub Key, pub V)
+where
+ V: Value;
+
+#[cfg(feature = "dynamic-keys")]
+impl<V: Value> From<(String, V)> for SingleKV<V> {
+ fn from(x: (String, V)) -> SingleKV<V> {
+ SingleKV(Key::from(x.0), x.1)
+ }
+}
+#[cfg(feature = "dynamic-keys")]
+impl<V: Value> From<(&'static str, V)> for SingleKV<V> {
+ fn from(x: (&'static str, V)) -> SingleKV<V> {
+ SingleKV(Key::from(x.0), x.1)
+ }
+}
+#[cfg(not(feature = "dynamic-keys"))]
+impl<V: Value> From<(&'static str, V)> for SingleKV<V> {
+ fn from(x: (&'static str, V)) -> SingleKV<V> {
+ SingleKV(x.0, x.1)
+ }
+}
+
+impl<V> KV for SingleKV<V>
+where
+ V: Value,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ self.1.serialize(record, self.0.clone(), serializer)
+ }
+}
+
+impl KV for () {
+ fn serialize(
+ &self,
+ _record: &Record,
+ _serializer: &mut Serializer,
+ ) -> Result {
+ Ok(())
+ }
+}
+
+impl<T: KV, R: KV> KV for (T, R) {
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ try!(self.0.serialize(record, serializer));
+ self.1.serialize(record, serializer)
+ }
+}
+
+impl<T> KV for Box<T>
+where
+ T: KV + ?Sized,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (**self).serialize(record, serializer)
+ }
+}
+
+impl<T> KV for Arc<T>
+where
+ T: KV + ?Sized,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ (**self).serialize(record, serializer)
+ }
+}
+
+impl<T> KV for OwnedKV<T>
+where
+ T: SendSyncRefUnwindSafeKV + ?Sized,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ self.0.serialize(record, serializer)
+ }
+}
+
+impl<'a> KV for BorrowedKV<'a> {
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ self.0.serialize(record, serializer)
+ }
+}
+// }}}
+
+// {{{ OwnedKV
+/// Owned KV
+///
+/// "Owned" means that the contained data (key-value pairs) can belong
+/// to a `Logger` and thus must be thread-safe (`'static`, `Send`, `Sync`)
+///
+/// Zero, one or more owned key-value pairs.
+///
+/// Can be constructed with [`o!` macro](macro.o.html).
+pub struct OwnedKV<T>(
+ #[doc(hidden)]
+ /// The exact details of that it are not considered public
+ /// and stable API. `slog_o` or `o` macro should be used
+ /// instead to create `OwnedKV` instances.
+ pub T,
+)
+where
+ T: SendSyncRefUnwindSafeKV + ?Sized;
+// }}}
+
+// {{{ BorrowedKV
+/// Borrowed `KV`
+///
+/// "Borrowed" means that the data is only a temporary
+/// referenced (`&T`) and can't be stored directly.
+///
+/// Zero, one or more borrowed key-value pairs.
+///
+/// Can be constructed with [`b!` macro](macro.b.html).
+pub struct BorrowedKV<'a>(
+ /// The exact details of it function are not
+ /// considered public and stable API. `log` and other
+ /// macros should be used instead to create
+ /// `BorrowedKV` instances.
+ #[doc(hidden)]
+ pub &'a KV,
+);
+
+// }}}
+
+// {{{ OwnedKVList
+struct OwnedKVListNode<T>
+where
+ T: SendSyncRefUnwindSafeKV + 'static,
+{
+ next_node: Arc<SendSyncRefUnwindSafeKV + 'static>,
+ kv: T,
+}
+
+struct MultiListNode {
+ next_node: Arc<SendSyncRefUnwindSafeKV + 'static>,
+ node: Arc<SendSyncRefUnwindSafeKV + 'static>,
+}
+
+/// Chain of `SyncMultiSerialize`-s of a `Logger` and its ancestors
+#[derive(Clone)]
+pub struct OwnedKVList {
+ node: Arc<SendSyncRefUnwindSafeKV + 'static>,
+}
+
+impl<T> KV for OwnedKVListNode<T>
+where
+ T: SendSyncRefUnwindSafeKV + 'static,
+{
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ try!(self.kv.serialize(record, serializer));
+ try!(self.next_node.serialize(record, serializer));
+
+ Ok(())
+ }
+}
+
+impl KV for MultiListNode {
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ try!(self.next_node.serialize(record, serializer));
+ try!(self.node.serialize(record, serializer));
+
+ Ok(())
+ }
+}
+
+impl KV for OwnedKVList {
+ fn serialize(
+ &self,
+ record: &Record,
+ serializer: &mut Serializer,
+ ) -> Result {
+ try!(self.node.serialize(record, serializer));
+
+ Ok(())
+ }
+}
+
+impl fmt::Debug for OwnedKVList {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(write!(f, "("));
+ let mut i = 0;
+
+ {
+ let mut as_str_ser = AsFmtSerializer(|key, _val| {
+ if i != 0 {
+ try!(write!(f, ", "));
+ }
+
+ try!(write!(f, "{}", key));
+ i += 1;
+ Ok(())
+ });
+ let record_static = record_static!(Level::Trace, "");
+
+ try!(
+ self.node
+ .serialize(
+ &Record::new(
+ &record_static,
+ &format_args!(""),
+ BorrowedKV(&STATIC_TERMINATOR_UNIT)
+ ),
+ &mut as_str_ser
+ )
+ .map_err(|_| fmt::Error)
+ );
+ }
+
+ try!(write!(f, ")"));
+ Ok(())
+ }
+}
+
+impl OwnedKVList {
+ /// New `OwnedKVList` node without a parent (root)
+ fn root<T>(values: OwnedKV<T>) -> Self
+ where
+ T: SendSyncRefUnwindSafeKV + 'static,
+ {
+ OwnedKVList {
+ node: Arc::new(OwnedKVListNode {
+ next_node: Arc::new(()),
+ kv: values.0,
+ }),
+ }
+ }
+
+ /// New `OwnedKVList` node with an existing parent
+ fn new<T>(
+ values: OwnedKV<T>,
+ next_node: Arc<SendSyncRefUnwindSafeKV + 'static>,
+ ) -> Self
+ where
+ T: SendSyncRefUnwindSafeKV + 'static,
+ {
+ OwnedKVList {
+ node: Arc::new(OwnedKVListNode {
+ next_node: next_node,
+ kv: values.0,
+ }),
+ }
+ }
+}
+
+impl<T> convert::From<OwnedKV<T>> for OwnedKVList
+where
+ T: SendSyncRefUnwindSafeKV + 'static,
+{
+ fn from(from: OwnedKV<T>) -> Self {
+ OwnedKVList::root(from)
+ }
+}
+// }}}
+
+// {{{ Error
+#[derive(Debug)]
+#[cfg(feature = "std")]
+/// Serialization Error
+pub enum Error {
+ /// `io::Error` (not available in ![no_std] mode)
+ Io(std::io::Error),
+ /// `fmt::Error`
+ Fmt(std::fmt::Error),
+ /// Other error
+ Other,
+}
+
+#[derive(Debug)]
+#[cfg(not(feature = "std"))]
+/// Serialization Error
+pub enum Error {
+ /// `fmt::Error`
+ Fmt(core::fmt::Error),
+ /// Other error
+ Other,
+}
+
+/// Serialization `Result`
+pub type Result<T = ()> = result::Result<T, Error>;
+
+#[cfg(feature = "std")]
+impl From<std::io::Error> for Error {
+ fn from(err: std::io::Error) -> Error {
+ Error::Io(err)
+ }
+}
+
+impl From<core::fmt::Error> for Error {
+ fn from(_: core::fmt::Error) -> Error {
+ Error::Other
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Error> for std::io::Error {
+ fn from(e: Error) -> std::io::Error {
+ match e {
+ Error::Io(e) => e,
+ Error::Fmt(_) => std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "formatting error",
+ ),
+ Error::Other => {
+ std::io::Error::new(std::io::ErrorKind::Other, "other error")
+ }
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for Error {
+ fn description(&self) -> &str {
+ match *self {
+ Error::Io(ref e) => e.description(),
+ Error::Fmt(_) => "formatting error",
+ Error::Other => "serialization error",
+ }
+ }
+
+ fn cause(&self) -> Option<&std::error::Error> {
+ match *self {
+ Error::Io(ref e) => Some(e),
+ Error::Fmt(ref e) => Some(e),
+ Error::Other => None,
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl core::fmt::Display for Error {
+ fn fmt(&self, fmt: &mut core::fmt::Formatter) -> std::fmt::Result {
+ match *self {
+ Error::Io(ref e) => e.fmt(fmt),
+ Error::Fmt(ref e) => e.fmt(fmt),
+ Error::Other => fmt.write_str("Other serialization error"),
+ }
+ }
+}
+// }}}
+
+// {{{ Misc
+/// This type is here just to abstract away lack of `!` type support in stable
+/// rust during time of the release. It will be switched to `!` at some point
+/// and `Never` should not be considered "stable" API.
+#[doc(hidden)]
+pub type Never = private::NeverStruct;
+
+mod private {
+ #[doc(hidden)]
+ #[derive(Clone, Debug)]
+ pub struct NeverStruct(());
+}
+
+/// This is not part of "stable" API
+#[doc(hidden)]
+pub static STATIC_TERMINATOR_UNIT: () = ();
+
+#[allow(unknown_lints)]
+#[allow(inline_always)]
+#[inline(always)]
+#[doc(hidden)]
+/// Not an API
+///
+/// Generally it's a bad idea to depend on static logging level
+/// in your code. Use closures to perform operations lazily
+/// only when logging actually takes place.
+pub fn __slog_static_max_level() -> FilterLevel {
+ if !cfg!(debug_assertions) {
+ if cfg!(feature = "release_max_level_off") {
+ return FilterLevel::Off;
+ } else if cfg!(feature = "release_max_level_error") {
+ return FilterLevel::Error;
+ } else if cfg!(feature = "release_max_level_warn") {
+ return FilterLevel::Warning;
+ } else if cfg!(feature = "release_max_level_info") {
+ return FilterLevel::Info;
+ } else if cfg!(feature = "release_max_level_debug") {
+ return FilterLevel::Debug;
+ } else if cfg!(feature = "release_max_level_trace") {
+ return FilterLevel::Trace;
+ }
+ }
+ if cfg!(feature = "max_level_off") {
+ FilterLevel::Off
+ } else if cfg!(feature = "max_level_error") {
+ FilterLevel::Error
+ } else if cfg!(feature = "max_level_warn") {
+ FilterLevel::Warning
+ } else if cfg!(feature = "max_level_info") {
+ FilterLevel::Info
+ } else if cfg!(feature = "max_level_debug") {
+ FilterLevel::Debug
+ } else if cfg!(feature = "max_level_trace") {
+ FilterLevel::Trace
+ } else {
+ if !cfg!(debug_assertions) {
+ FilterLevel::Info
+ } else {
+ FilterLevel::Debug
+ }
+ }
+}
+
+// }}}
+
+// {{{ Slog v1 Compat
+#[deprecated(note = "Renamed to `Value`")]
+/// Compatibility name to ease upgrading from `slog v1`
+pub type Serialize = Value;
+
+#[deprecated(note = "Renamed to `PushFnValue`")]
+/// Compatibility name to ease upgrading from `slog v1`
+pub type PushLazy<T> = PushFnValue<T>;
+
+#[deprecated(note = "Renamed to `PushFnValueSerializer`")]
+/// Compatibility name to ease upgrading from `slog v1`
+pub type ValueSerializer<'a> = PushFnValueSerializer<'a>;
+
+#[deprecated(note = "Renamed to `OwnedKVList`")]
+/// Compatibility name to ease upgrading from `slog v1`
+pub type OwnedKeyValueList = OwnedKVList;
+
+#[deprecated(note = "Content of ser module moved to main namespace")]
+/// Compatibility name to ease upgrading from `slog v1`
+pub mod ser {
+ #[allow(deprecated)]
+ pub use super::{OwnedKeyValueList, PushLazy, Serialize, Serializer,
+ ValueSerializer};
+}
+// }}}
+
+// {{{ Test
+#[cfg(test)]
+mod tests;
+
+// }}}
+
+// vim: foldmethod=marker foldmarker={{{,}}}
diff --git a/src/tests.rs b/src/tests.rs
new file mode 100644
index 0000000..95c4c38
--- /dev/null
+++ b/src/tests.rs
@@ -0,0 +1,419 @@
+use {Discard, Logger, Never, KV, Drain, OwnedKVList, Record, AsFmtSerializer};
+
+// Separate module to test lack of imports
+mod no_imports {
+ use {Discard, Logger};
+ /// ensure o! macro expands without error inside a module
+ #[test]
+ fn test_o_macro_expansion() {
+ let _ = Logger::root(Discard, o!("a" => "aa"));
+ }
+ /// ensure o! macro expands without error inside a module
+ #[test]
+ fn test_slog_o_macro_expansion() {
+ let _ = Logger::root(Discard, slog_o!("a" => "aa"));
+ }
+}
+
+#[cfg(feature = "std")]
+mod std_only {
+ use super::super::*;
+ use std;
+
+ #[derive(Clone)]
+ struct CheckError;
+
+ impl Drain for CheckError {
+ type Ok = ();
+ type Err = Never;
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> std::result::Result<Self::Ok, Self::Err> {
+ struct ErrorSerializer(String);
+
+ impl Serializer for ErrorSerializer {
+ fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> Result {
+ use core::fmt::Write;
+
+ assert!(key == "error");
+ self.0.write_fmt(*val).unwrap();
+ Ok(())
+ }
+ }
+
+ let mut serializer = ErrorSerializer(String::new());
+ values.serialize(record, &mut serializer).unwrap();
+ assert_eq!(
+ serializer.0,
+ format!("{}", record.msg())
+ );
+ Ok(())
+ }
+ }
+
+ #[derive(Debug)]
+ struct TestError<E=std::string::ParseError>(&'static str, Option<E>);
+
+ impl TestError {
+ fn new(message: &'static str) -> Self {
+ TestError(message, None)
+ }
+ }
+
+ impl<E> fmt::Display for TestError<E> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+
+ impl<E: std::error::Error + 'static> std::error::Error for TestError<E> {
+ #[allow(deprecated)]
+ fn cause(&self) -> Option<&std::error::Error> {
+ self.1.as_ref().map(|error| error as _)
+ }
+
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "test error"
+ }
+ }
+
+ #[test]
+ fn logger_fmt_debug_sanity() {
+ let root = Logger::root(Discard, o!("a" => "aa"));
+ let log = root.new(o!("b" => "bb", "c" => "cc"));
+
+ assert_eq!(format!("{:?}", log), "Logger(c, b, a)");
+ }
+
+ #[test]
+ fn multichain() {
+ #[derive(Clone)]
+ struct CheckOwned;
+
+ impl Drain for CheckOwned {
+ type Ok = ();
+ type Err = Never;
+ fn log(
+ &self,
+ record: &Record,
+ values: &OwnedKVList,
+ ) -> std::result::Result<Self::Ok, Self::Err> {
+ assert_eq!(
+ format!("{}", record.msg()),
+ format!("{:?}", values)
+ );
+ Ok(())
+ }
+ }
+
+ let root = Logger::root(CheckOwned, o!("a" => "aa"));
+ let log = root.new(o!("b1" => "bb", "b2" => "bb"));
+
+ info!(log, "(b2, b1, a)");
+
+ let log = Logger::root(log, o!("c" => "cc"));
+ info!(log, "(c, b2, b1, a)");
+ let log = Logger::root(log, o!("d1" => "dd", "d2" => "dd"));
+ info!(log, "(d2, d1, c, b2, b1, a)");
+ }
+
+ #[test]
+ fn error_fmt_no_source() {
+ let logger = Logger::root(CheckError, o!("error" => #TestError::new("foo")));
+ info!(logger, "foo");
+ }
+
+ #[test]
+ fn error_fmt_single_source() {
+ let logger = Logger::root(CheckError, o!("error" => #TestError("foo", Some(TestError::new("bar")))));
+ info!(logger, "foo: bar");
+ }
+
+ #[test]
+ fn error_fmt_two_sources() {
+ let logger = Logger::root(CheckError, o!("error" => #TestError("foo", Some(TestError("bar", Some(TestError::new("baz")))))));
+ info!(logger, "foo: bar: baz");
+ }
+
+ #[test]
+ fn ioerror_impls_value() {
+ let logger = Logger::root(Discard, o!());
+ info!(logger, "not found"; "error" => std::io::Error::from(std::io::ErrorKind::NotFound));
+ // compiles?
+ info!(logger, "not found"; "error" => #std::io::Error::from(std::io::ErrorKind::NotFound));
+ }
+}
+
+#[test]
+fn expressions() {
+ use super::{Record, Result, Serializer, KV};
+
+ struct Foo;
+
+ impl Foo {
+ fn bar(&self) -> u32 {
+ 1
+ }
+ }
+
+ struct X {
+ foo: Foo,
+ }
+
+ let log = Logger::root(Discard, o!("version" => env!("CARGO_PKG_VERSION")));
+
+ let foo = Foo;
+ let r = X { foo: foo };
+
+ warn!(log, "logging message");
+ slog_warn!(log, "logging message");
+
+ info!(log, #"with tag", "logging message");
+ slog_info!(log, #"with tag", "logging message");
+
+ warn!(log, "logging message"; "a" => "b");
+ slog_warn!(log, "logging message"; "a" => "b");
+
+ warn!(log, "logging message bar={}", r.foo.bar());
+ slog_warn!(log, "logging message bar={}", r.foo.bar());
+
+ warn!(
+ log,
+ "logging message bar={} foo={}",
+ r.foo.bar(),
+ r.foo.bar()
+ );
+ slog_warn!(
+ log,
+ "logging message bar={} foo={}",
+ r.foo.bar(),
+ r.foo.bar()
+ );
+
+ // trailing comma check
+ warn!(
+ log,
+ "logging message bar={} foo={}",
+ r.foo.bar(),
+ r.foo.bar(),
+ );
+ slog_warn!(
+ log,
+ "logging message bar={} foo={}",
+ r.foo.bar(),
+ r.foo.bar(),
+ );
+
+ warn!(log, "logging message bar={}", r.foo.bar(); "x" => 1);
+ slog_warn!(log, "logging message bar={}", r.foo.bar(); "x" => 1);
+
+ // trailing comma check
+ warn!(log, "logging message bar={}", r.foo.bar(); "x" => 1,);
+ slog_warn!(log, "logging message bar={}", r.foo.bar(); "x" => 1,);
+
+ warn!(log,
+ "logging message bar={}", r.foo.bar(); "x" => 1, "y" => r.foo.bar());
+ slog_warn!(log,
+ "logging message bar={}", r.foo.bar();
+ "x" => 1, "y" => r.foo.bar());
+
+ warn!(log, "logging message bar={}", r.foo.bar(); "x" => r.foo.bar());
+ slog_warn!(log, "logging message bar={}", r.foo.bar(); "x" => r.foo.bar());
+
+ warn!(log, "logging message bar={}", r.foo.bar();
+ "x" => r.foo.bar(), "y" => r.foo.bar());
+ slog_warn!(log,
+ "logging message bar={}", r.foo.bar();
+ "x" => r.foo.bar(), "y" => r.foo.bar());
+
+ // trailing comma check
+ warn!(log,
+ "logging message bar={}", r.foo.bar();
+ "x" => r.foo.bar(), "y" => r.foo.bar(),);
+ slog_warn!(log,
+ "logging message bar={}", r.foo.bar();
+ "x" => r.foo.bar(), "y" => r.foo.bar(),);
+
+ {
+ #[derive(Clone)]
+ struct K;
+
+ impl KV for K {
+ fn serialize(
+ &self,
+ _record: &Record,
+ _serializer: &mut Serializer,
+ ) -> Result {
+ Ok(())
+ }
+ }
+
+ let x = K;
+
+ let _log = log.new(o!(x.clone()));
+ let _log = log.new(o!("foo" => "bar", x.clone()));
+ let _log = log.new(o!("foo" => "bar", x.clone(), x.clone()));
+ let _log = log.new(
+ slog_o!("foo" => "bar", x.clone(), x.clone(), "aaa" => "bbb"),
+ );
+
+ info!(log, "message"; "foo" => "bar", &x, &x, "aaa" => "bbb");
+ }
+
+ info!(
+ log,
+ "message {}",
+ { 3 + 3; 2};
+ "foo" => "bar",
+ "foo" => { 3 + 3; 2},
+ "aaa" => "bbb");
+}
+
+#[cfg(integer128)]
+#[test]
+fn integer_128_types() {
+ let log = Logger::root(Discard, o!("version" => env!("CARGO_PKG_VERSION")));
+
+ info!(log, "i128 = {}", 42i128; "foo" => 7i128);
+ info!(log, "u128 = {}", 42u128; "foo" => 7u128);
+}
+
+#[test]
+fn expressions_fmt() {
+ let log = Logger::root(Discard, o!("version" => env!("CARGO_PKG_VERSION")));
+
+ let f = "f";
+ let d = (1, 2);
+
+ info!(log, "message"; "f" => %f, "d" => ?d);
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn display_and_alternate_display() {
+ use core::fmt;
+ use core::cell::Cell;
+
+ struct Example;
+
+ impl fmt::Display for Example {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if f.alternate() {
+ f.write_str("alternate")
+ } else {
+ f.write_str("normal")
+ }
+ }
+ }
+
+ #[derive(Clone, Default)]
+ struct CheckExample;
+
+ impl Drain for CheckExample {
+ type Ok = ();
+ type Err = Never;
+
+ fn log(&self, record: &Record, values: &OwnedKVList) -> Result<(), Never> {
+ let mut checked_n = false;
+ let mut checked_a = false;
+ {
+ let mut serializer = AsFmtSerializer(|key, fmt_args| {
+ if key == "n" {
+ assert_eq!(format!("{}", fmt_args), "normal");
+ checked_n = true;
+ } else if key == "a" {
+ assert_eq!(format!("{}", fmt_args), "alternate");
+ checked_a = true;
+ } else {
+ panic!("Unexpected key: {}", key);
+ }
+ Ok(())
+ });
+
+ record.kv.serialize(record, &mut serializer).unwrap();
+ }
+
+ assert!(checked_n, "Expected the normal formatter to be used");
+ assert!(checked_a, "Expected the alternate formatter to be used");
+
+ Ok(())
+ }
+ }
+
+ let log = Logger::root(CheckExample, o!());
+
+ info!(log, ""; "n" => %Example, "a" => #%Example);
+}
+
+#[test]
+fn makers() {
+ use ::*;
+ let drain = Duplicate(
+ Discard.filter(|r| r.level().is_at_least(Level::Info)),
+ Discard.filter_level(Level::Warning),
+ ).map(Fuse);
+ let _log = Logger::root(
+ Arc::new(drain),
+ o!("version" => env!("CARGO_PKG_VERSION")),
+ );
+}
+
+#[test]
+fn simple_logger_erased() {
+ use ::*;
+
+ fn takes_arced_drain(_l: Logger) {}
+
+ let drain = Discard.filter_level(Level::Warning).map(Fuse);
+ let log =
+ Logger::root_typed(drain, o!("version" => env!("CARGO_PKG_VERSION")));
+
+ takes_arced_drain(log.to_erased());
+}
+
+#[test]
+fn logger_to_erased() {
+ use ::*;
+
+ fn takes_arced_drain(_l: Logger) {}
+
+ let drain = Duplicate(
+ Discard.filter(|r| r.level().is_at_least(Level::Info)),
+ Discard.filter_level(Level::Warning),
+ ).map(Fuse);
+ let log =
+ Logger::root_typed(drain, o!("version" => env!("CARGO_PKG_VERSION")));
+
+ takes_arced_drain(log.into_erased());
+}
+
+#[test]
+fn logger_by_ref() {
+ use ::*;
+ let drain = Discard.filter_level(Level::Warning).map(Fuse);
+ let log = Logger::root_typed(drain, o!("version" => env!("CARGO_PKG_VERSION")));
+ let f = "f";
+ let d = (1, 2);
+ info!(&log, "message"; "f" => %f, "d" => ?d);
+}
+
+#[test]
+fn test_never_type_clone() {
+ // We just want to make sure that this compiles
+ fn _do_not_run() {
+ let x: Never = panic!("Can't actually construct a Never type here!");
+ let y = x.clone();
+ }
+ // Always pass if we compiled
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn can_hash_keys() {
+ use std::collections::HashSet;
+ use Key;
+ let tab: HashSet<Key> = ["foo"].iter().map(|&k| k.into()).collect();
+}