aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Vander Stoep <jeffv@google.com>2022-12-12 21:50:02 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-12-12 21:50:02 +0000
commit5c58326c093280a508c274c856b614b839e139d2 (patch)
treee0a28e86a5c61477dd8f8c94720693537c787dca
parent957e963fa3d0caa43dca220fa5a4abb01c2c2c85 (diff)
parentc64c175fa4e4fa4862d2490cae08d4f8f5c0b697 (diff)
downloadlog-5c58326c093280a508c274c856b614b839e139d2.tar.gz
Merge "Upgrade log to 0.4.17" am: b81e1f277c am: c64c175fa4
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/log/+/2345064 Change-Id: I22cc0eca0e8eee3cb97d973be70823bd63c691af Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json7
-rw-r--r--.github/workflows/main.yml18
-rw-r--r--Android.bp3
-rw-r--r--CHANGELOG.md28
-rw-r--r--Cargo.toml57
-rw-r--r--Cargo.toml.orig13
-rw-r--r--METADATA14
-rw-r--r--README.md39
-rw-r--r--build.rs5
-rw-r--r--src/kv/error.rs20
-rw-r--r--src/kv/value.rs383
-rw-r--r--src/lib.rs208
-rw-r--r--src/macros.rs92
13 files changed, 810 insertions, 77 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 798cd7f..b243187 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
{
"git": {
- "sha1": "9d4206770dd93f07cb27c3e1f41dc21c45031302"
- }
-}
+ "sha1": "7fb28c36c7a418912612ab37ab49bd4ca1a3a7f5"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9a8f9aa..18e5c40 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -37,6 +37,8 @@ jobs:
rustup update ${{ matrix.rust }} --no-self-update
rustup default ${{ matrix.rust }}
- run: cargo test --verbose
+ - run: cargo test --verbose --no-default-features
+ - run: cargo test --verbose --all-features
- run: cargo test --verbose --features serde
- run: cargo test --verbose --features std
- run: cargo test --verbose --features kv_unstable
@@ -74,6 +76,22 @@ jobs:
- run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable kv_unstable_std"
- run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable kv_unstable_sval kv_unstable_serde"
+ minimalv:
+ name: Minimal versions
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - name: Install Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ - run: cargo build --verbose -Z minimal-versions --features kv_unstable
+ - run: cargo build --verbose -Z minimal-versions --features "kv_unstable std"
+ - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_sval"
+ - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_serde"
+ - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_std"
+ - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_sval kv_unstable_serde"
+
msrv:
name: MSRV
runs-on: ubuntu-latest
diff --git a/Android.bp b/Android.bp
index 065685f..3c90d98 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,11 +40,10 @@ license {
rust_library {
name: "liblog_rust",
stem: "liblog",
- // has rustc warnings
host_supported: true,
crate_name: "log",
cargo_env_compat: true,
- cargo_pkg_version: "0.4.14",
+ cargo_pkg_version: "0.4.17",
srcs: ["src/lib.rs"],
edition: "2015",
features: ["std"],
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63c3ccd..7d68f89 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,29 @@
## [Unreleased]
+## [0.4.17] - 2022-04-29
+
+* Update `kv_unstable` internal dependencies.
+
+## [0.4.16] - 2022-03-22
+
+* Fix a conflict with unqualified `Option` use in macros.
+
+## [0.4.15] - 2022-02-23
+
+* Silence a warning about the deprecated `spin_loop_hint`.
+* Relax ordering in the atomic `set_max_level` call.
+* Add thumbv4t-none-eabi to targets that don't support atomics
+* Allow levels to be iterated over.
+* Implement `Log` on some common wrapper types.
+* Improvements to test coverage.
+* Improvements to documentation.
+* Add key-value support to the `log!` macros.
+* Tighten `kv_unstable` internal dependencies so they don't bump past their current alpha.
+* Add a simple visit API to `kv_unstable`.
+* Support `NonZero*` integers as values in structured logging
+* Support static strings as keys in structured logging
+
## [0.4.14] - 2021-01-27
* Remove the `__private_api_log_lit` special case.
@@ -196,7 +219,10 @@ version using log 0.4.x to avoid losing module and file information.
Look at the [release tags] for information about older releases.
-[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.14...HEAD
+[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.17...HEAD
+[0.4.17]: https://github.com/rust-lang-nursery/log/compare/0.4.16...0.4.17
+[0.4.16]: https://github.com/rust-lang-nursery/log/compare/0.4.15...0.4.16
+[0.4.15]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.15
[0.4.14]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.14
[0.4.13]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.13
[0.4.12]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.12
diff --git a/Cargo.toml b/Cargo.toml
index 3cbe5e0..5fed87f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,28 +3,36 @@
# 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
+# 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)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
name = "log"
-version = "0.4.14"
+version = "0.4.17"
authors = ["The Rust Project Developers"]
build = "build.rs"
-exclude = ["rfcs/**/*", "/.travis.yml", "/appveyor.yml"]
-description = "A lightweight logging facade for Rust\n"
+exclude = ["rfcs/**/*"]
+description = """
+A lightweight logging facade for Rust
+"""
documentation = "https://docs.rs/log"
readme = "README.md"
keywords = ["logging"]
categories = ["development-tools::debugging"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/log"
+
[package.metadata.docs.rs]
-features = ["std", "serde", "kv_unstable_std", "kv_unstable_sval", "kv_unstable_serde"]
+features = [
+ "std",
+ "serde",
+ "kv_unstable_std",
+ "kv_unstable_sval",
+ "kv_unstable_serde",
+]
[[test]]
name = "filters"
@@ -35,6 +43,7 @@ harness = false
name = "macros"
path = "tests/macros.rs"
harness = true
+
[dependencies.cfg-if]
version = "1.0"
@@ -44,14 +53,18 @@ optional = true
default-features = false
[dependencies.sval]
-version = "1.0.0-alpha.5"
+version = "=1.0.0-alpha.5"
optional = true
default-features = false
[dependencies.value-bag]
-version = "1.0.0-alpha.6"
+version = "=1.0.0-alpha.9"
optional = true
default-features = false
+
+[dev-dependencies.rustversion]
+version = "1.0"
+
[dev-dependencies.serde]
version = "1.0"
features = ["derive"]
@@ -60,18 +73,30 @@ features = ["derive"]
version = "1.0"
[dev-dependencies.sval]
-version = "1.0.0-alpha.5"
+version = "=1.0.0-alpha.5"
features = ["derive"]
[dev-dependencies.value-bag]
-version = "1.0.0-alpha.6"
+version = "=1.0.0-alpha.9"
features = ["test"]
[features]
kv_unstable = ["value-bag"]
-kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"]
-kv_unstable_std = ["std", "kv_unstable", "value-bag/error"]
-kv_unstable_sval = ["kv_unstable", "value-bag/sval", "sval"]
+kv_unstable_serde = [
+ "kv_unstable_std",
+ "value-bag/serde",
+ "serde",
+]
+kv_unstable_std = [
+ "std",
+ "kv_unstable",
+ "value-bag/error",
+]
+kv_unstable_sval = [
+ "kv_unstable",
+ "value-bag/sval",
+ "sval",
+]
max_level_debug = []
max_level_error = []
max_level_info = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 0d31859..9bac9d7 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,7 +1,7 @@
[package]
name = "log"
-version = "0.4.14" # remember to update html_root_url
+version = "0.4.17" # remember to update html_root_url
authors = ["The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@@ -12,7 +12,7 @@ A lightweight logging facade for Rust
"""
categories = ["development-tools::debugging"]
keywords = ["logging"]
-exclude = ["rfcs/**/*", "/.travis.yml", "/appveyor.yml"]
+exclude = ["rfcs/**/*"]
build = "build.rs"
[package.metadata.docs.rs]
@@ -55,11 +55,12 @@ kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"]
[dependencies]
cfg-if = "1.0"
serde = { version = "1.0", optional = true, default-features = false }
-sval = { version = "1.0.0-alpha.5", optional = true, default-features = false }
-value-bag = { version = "1.0.0-alpha.6", optional = true, default-features = false }
+sval = { version = "=1.0.0-alpha.5", optional = true, default-features = false }
+value-bag = { version = "=1.0.0-alpha.9", optional = true, default-features = false }
[dev-dependencies]
+rustversion = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_test = "1.0"
-sval = { version = "1.0.0-alpha.5", features = ["derive"] }
-value-bag = { version = "1.0.0-alpha.6", features = ["test"] }
+sval = { version = "=1.0.0-alpha.5", features = ["derive"] }
+value-bag = { version = "=1.0.0-alpha.9", features = ["test"] }
diff --git a/METADATA b/METADATA
index 86f9298..1078fee 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/log
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "log"
description: "A lightweight logging facade for Rust"
third_party {
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/log/log-0.4.14.crate"
+ value: "https://static.crates.io/crates/log/log-0.4.17.crate"
}
- version: "0.4.14"
+ version: "0.4.17"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 4
- day: 2
+ year: 2022
+ month: 12
+ day: 12
}
}
diff --git a/README.md b/README.md
index 0f87ba7..7fa4353 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ This version is explicitly tested in CI and may be bumped in any release as need
## Usage
-## In libraries
+### In libraries
Libraries should link only to the `log` crate, and use the provided macros to
log whatever information will be useful to downstream consumers:
@@ -55,15 +55,15 @@ pub fn shave_the_yak(yak: &mut Yak) {
}
```
-## In executables
+### In executables
In order to produce log output, executables have to use a logger implementation compatible with the facade.
There are many available implementations to choose from, here are some of the most popular ones:
* Simple minimal loggers:
* [`env_logger`](https://docs.rs/env_logger/*/env_logger/)
- * [`simple_logger`](https://github.com/borntyping/rust-simple_logger)
- * [`simplelog`](https://github.com/drakulix/simplelog.rs)
+ * [`simple_logger`](https://docs.rs/simple_logger/*/simple_logger/)
+ * [`simplelog`](https://docs.rs/simplelog/*/simplelog/)
* [`pretty_env_logger`](https://docs.rs/pretty_env_logger/*/pretty_env_logger/)
* [`stderrlog`](https://docs.rs/stderrlog/*/stderrlog/)
* [`flexi_logger`](https://docs.rs/flexi_logger/*/flexi_logger/)
@@ -72,11 +72,17 @@ There are many available implementations to choose from, here are some of the mo
* [`fern`](https://docs.rs/fern/*/fern/)
* Adaptors for other facilities:
* [`syslog`](https://docs.rs/syslog/*/syslog/)
+ * [`systemd-journal-logger`](https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/)
* [`slog-stdlog`](https://docs.rs/slog-stdlog/*/slog_stdlog/)
* [`android_log`](https://docs.rs/android_log/*/android_log/)
* [`win_dbg_logger`](https://docs.rs/win_dbg_logger/*/win_dbg_logger/)
+ * [`db_logger`](https://docs.rs/db_logger/*/db_logger/)
* For WebAssembly binaries:
* [`console_log`](https://docs.rs/console_log/*/console_log/)
+* For dynamic libraries:
+ * You may need to construct [an FFI-safe wrapper over `log`](https://github.com/rust-lang/log/issues/421) to initialize in your libraries.
+* Utilities:
+ * [`log_err`](https://docs.rs/log_err/*/log_err/)
Executables should choose a logger implementation and initialize it early in the
runtime of the program. Logger implementations will typically include a
@@ -84,3 +90,28 @@ function to do this. Any log messages generated before the logger is
initialized will be ignored.
The executable itself may use the `log` crate to log as well.
+
+## Structured logging
+
+If you enable the `kv_unstable` feature, you can associate structured data with your log records:
+
+```rust
+use log::{info, trace, warn, as_serde, as_error};
+
+pub fn shave_the_yak(yak: &mut Yak) {
+ trace!(target = "yak_events", yak = as_serde!(yak); "Commencing yak shaving");
+
+ loop {
+ match find_a_razor() {
+ Ok(razor) => {
+ info!(razor = razor; "Razor located");
+ yak.shave(razor);
+ break;
+ }
+ Err(err) => {
+ warn!(err = as_error!(err); "Unable to locate a razor, retrying");
+ }
+ }
+ }
+}
+```
diff --git a/build.rs b/build.rs
index 22cfa78..30c7edb 100644
--- a/build.rs
+++ b/build.rs
@@ -33,7 +33,10 @@ fn target_has_atomic_cas(target: &str) -> bool {
fn target_has_atomics(target: &str) -> bool {
match &target[..] {
- "msp430-none-elf" | "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => false,
+ "thumbv4t-none-eabi"
+ | "msp430-none-elf"
+ | "riscv32i-unknown-none-elf"
+ | "riscv32imc-unknown-none-elf" => false,
_ => true,
}
}
diff --git a/src/kv/error.rs b/src/kv/error.rs
index f68f2da..1adad23 100644
--- a/src/kv/error.rs
+++ b/src/kv/error.rs
@@ -11,6 +11,7 @@ enum Inner {
#[cfg(feature = "std")]
Boxed(std_support::BoxedError),
Msg(&'static str),
+ Value(value_bag::Error),
Fmt,
}
@@ -21,6 +22,24 @@ impl Error {
inner: Inner::Msg(msg),
}
}
+
+ // Not public so we don't leak the `value_bag` API
+ pub(super) fn from_value(err: value_bag::Error) -> Self {
+ Error {
+ inner: Inner::Value(err),
+ }
+ }
+
+ // Not public so we don't leak the `value_bag` API
+ pub(super) fn into_value(self) -> value_bag::Error {
+ match self.inner {
+ Inner::Value(err) => err,
+ #[cfg(feature = "kv_unstable_std")]
+ _ => value_bag::Error::boxed(self),
+ #[cfg(not(feature = "kv_unstable_std"))]
+ _ => value_bag::Error::msg("error inspecting a value"),
+ }
+ }
}
impl fmt::Display for Error {
@@ -29,6 +48,7 @@ impl fmt::Display for Error {
match &self.inner {
#[cfg(feature = "std")]
&Boxed(ref err) => err.fmt(f),
+ &Value(ref err) => err.fmt(f),
&Msg(ref msg) => msg.fmt(f),
&Fmt => fmt::Error.fmt(f),
}
diff --git a/src/kv/value.rs b/src/kv/value.rs
index 942d59e..9730f47 100644
--- a/src/kv/value.rs
+++ b/src/kv/value.rs
@@ -37,6 +37,49 @@ impl<'v> ToValue for Value<'v> {
}
}
+/// Get a value from a type implementing `std::fmt::Debug`.
+#[macro_export]
+macro_rules! as_debug {
+ ($capture:expr) => {
+ $crate::kv::Value::from_debug(&$capture)
+ };
+}
+
+/// Get a value from a type implementing `std::fmt::Display`.
+#[macro_export]
+macro_rules! as_display {
+ ($capture:expr) => {
+ $crate::kv::Value::from_display(&$capture)
+ };
+}
+
+/// Get a value from an error.
+#[cfg(feature = "kv_unstable_std")]
+#[macro_export]
+macro_rules! as_error {
+ ($capture:expr) => {
+ $crate::kv::Value::from_dyn_error(&$capture)
+ };
+}
+
+#[cfg(feature = "kv_unstable_serde")]
+/// Get a value from a type implementing `serde::Serialize`.
+#[macro_export]
+macro_rules! as_serde {
+ ($capture:expr) => {
+ $crate::kv::Value::from_serde(&$capture)
+ };
+}
+
+/// Get a value from a type implementing `sval::value::Value`.
+#[cfg(feature = "kv_unstable_sval")]
+#[macro_export]
+macro_rules! as_sval {
+ ($capture:expr) => {
+ $crate::kv::Value::from_sval(&$capture)
+ };
+}
+
/// A value in a structured key-value pair.
///
/// # Capturing values
@@ -263,6 +306,78 @@ impl<'v> Value<'v> {
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.inner.downcast_ref::<T>()
}
+
+ /// Inspect this value using a simple visitor.
+ pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> {
+ struct Visitor<V>(V);
+
+ impl<'v, V> value_bag::visit::Visit<'v> for Visitor<V>
+ where
+ V: Visit<'v>,
+ {
+ fn visit_any(&mut self, value: ValueBag) -> Result<(), value_bag::Error> {
+ self.0
+ .visit_any(Value { inner: value })
+ .map_err(Error::into_value)
+ }
+
+ fn visit_u64(&mut self, value: u64) -> Result<(), value_bag::Error> {
+ self.0.visit_u64(value).map_err(Error::into_value)
+ }
+
+ fn visit_i64(&mut self, value: i64) -> Result<(), value_bag::Error> {
+ self.0.visit_i64(value).map_err(Error::into_value)
+ }
+
+ fn visit_u128(&mut self, value: u128) -> Result<(), value_bag::Error> {
+ self.0.visit_u128(value).map_err(Error::into_value)
+ }
+
+ fn visit_i128(&mut self, value: i128) -> Result<(), value_bag::Error> {
+ self.0.visit_i128(value).map_err(Error::into_value)
+ }
+
+ fn visit_f64(&mut self, value: f64) -> Result<(), value_bag::Error> {
+ self.0.visit_f64(value).map_err(Error::into_value)
+ }
+
+ fn visit_bool(&mut self, value: bool) -> Result<(), value_bag::Error> {
+ self.0.visit_bool(value).map_err(Error::into_value)
+ }
+
+ fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> {
+ self.0.visit_str(value).map_err(Error::into_value)
+ }
+
+ fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), value_bag::Error> {
+ self.0.visit_borrowed_str(value).map_err(Error::into_value)
+ }
+
+ fn visit_char(&mut self, value: char) -> Result<(), value_bag::Error> {
+ self.0.visit_char(value).map_err(Error::into_value)
+ }
+
+ #[cfg(feature = "kv_unstable_std")]
+ fn visit_error(
+ &mut self,
+ err: &(dyn std::error::Error + 'static),
+ ) -> Result<(), value_bag::Error> {
+ self.0.visit_error(err).map_err(Error::into_value)
+ }
+
+ #[cfg(feature = "kv_unstable_std")]
+ fn visit_borrowed_error(
+ &mut self,
+ err: &'v (dyn std::error::Error + 'static),
+ ) -> Result<(), value_bag::Error> {
+ self.0.visit_borrowed_error(err).map_err(Error::into_value)
+ }
+ }
+
+ self.inner
+ .visit(&mut Visitor(visitor))
+ .map_err(Error::from_value)
+ }
}
impl<'v> fmt::Debug for Value<'v> {
@@ -326,12 +441,62 @@ impl ToValue for str {
}
}
+impl ToValue for u128 {
+ fn to_value(&self) -> Value {
+ Value::from(self)
+ }
+}
+
+impl ToValue for i128 {
+ fn to_value(&self) -> Value {
+ Value::from(self)
+ }
+}
+
+impl ToValue for std::num::NonZeroU128 {
+ fn to_value(&self) -> Value {
+ Value::from(self)
+ }
+}
+
+impl ToValue for std::num::NonZeroI128 {
+ fn to_value(&self) -> Value {
+ Value::from(self)
+ }
+}
+
impl<'v> From<&'v str> for Value<'v> {
fn from(value: &'v str) -> Self {
Value::from_value_bag(value)
}
}
+impl<'v> From<&'v u128> for Value<'v> {
+ fn from(value: &'v u128) -> Self {
+ Value::from_value_bag(value)
+ }
+}
+
+impl<'v> From<&'v i128> for Value<'v> {
+ fn from(value: &'v i128) -> Self {
+ Value::from_value_bag(value)
+ }
+}
+
+impl<'v> From<&'v std::num::NonZeroU128> for Value<'v> {
+ fn from(v: &'v std::num::NonZeroU128) -> Value<'v> {
+ // SAFETY: `NonZeroU128` and `u128` have the same ABI
+ Value::from_value_bag(unsafe { std::mem::transmute::<&std::num::NonZeroU128, &u128>(v) })
+ }
+}
+
+impl<'v> From<&'v std::num::NonZeroI128> for Value<'v> {
+ fn from(v: &'v std::num::NonZeroI128) -> Value<'v> {
+ // SAFETY: `NonZeroI128` and `i128` have the same ABI
+ Value::from_value_bag(unsafe { std::mem::transmute::<&std::num::NonZeroI128, &i128>(v) })
+ }
+}
+
impl ToValue for () {
fn to_value(&self) -> Value {
Value::from_value_bag(())
@@ -368,6 +533,24 @@ macro_rules! impl_to_value_primitive {
};
}
+macro_rules! impl_to_value_nonzero_primitive {
+ ($($into_ty:ident,)*) => {
+ $(
+ impl ToValue for std::num::$into_ty {
+ fn to_value(&self) -> Value {
+ Value::from(self.get())
+ }
+ }
+
+ impl<'v> From<std::num::$into_ty> for Value<'v> {
+ fn from(value: std::num::$into_ty) -> Self {
+ Value::from(value.get())
+ }
+ }
+ )*
+ };
+}
+
macro_rules! impl_value_to_primitive {
($(#[doc = $doc:tt] $into_name:ident -> $into_ty:ty,)*) => {
impl<'v> Value<'v> {
@@ -381,8 +564,12 @@ macro_rules! impl_value_to_primitive {
}
}
-impl_to_value_primitive![
- usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool,
+impl_to_value_primitive![usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64, char, bool,];
+
+#[rustfmt::skip]
+impl_to_value_nonzero_primitive![
+ NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64,
+ NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64,
];
impl_value_to_primitive![
@@ -448,6 +635,142 @@ mod std_support {
self.inner.to_str()
}
}
+
+ impl<'v> From<&'v String> for Value<'v> {
+ fn from(v: &'v String) -> Self {
+ Value::from(&**v)
+ }
+ }
+}
+
+/// A visitor for a `Value`.
+pub trait Visit<'v> {
+ /// Visit a `Value`.
+ ///
+ /// This is the only required method on `Visit` and acts as a fallback for any
+ /// more specific methods that aren't overridden.
+ /// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation,
+ /// or serialized using its `sval::Value` or `serde::Serialize` implementation.
+ fn visit_any(&mut self, value: Value) -> Result<(), Error>;
+
+ /// Visit an unsigned integer.
+ fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
+ self.visit_any(value.into())
+ }
+
+ /// Visit a signed integer.
+ fn visit_i64(&mut self, value: i64) -> Result<(), Error> {
+ self.visit_any(value.into())
+ }
+
+ /// Visit a big unsigned integer.
+ fn visit_u128(&mut self, value: u128) -> Result<(), Error> {
+ self.visit_any((&value).into())
+ }
+
+ /// Visit a big signed integer.
+ fn visit_i128(&mut self, value: i128) -> Result<(), Error> {
+ self.visit_any((&value).into())
+ }
+
+ /// Visit a floating point.
+ fn visit_f64(&mut self, value: f64) -> Result<(), Error> {
+ self.visit_any(value.into())
+ }
+
+ /// Visit a boolean.
+ fn visit_bool(&mut self, value: bool) -> Result<(), Error> {
+ self.visit_any(value.into())
+ }
+
+ /// Visit a string.
+ fn visit_str(&mut self, value: &str) -> Result<(), Error> {
+ self.visit_any(value.into())
+ }
+
+ /// Visit a string.
+ fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> {
+ self.visit_str(value)
+ }
+
+ /// Visit a Unicode character.
+ fn visit_char(&mut self, value: char) -> Result<(), Error> {
+ let mut b = [0; 4];
+ self.visit_str(&*value.encode_utf8(&mut b))
+ }
+
+ /// Visit an error.
+ #[cfg(feature = "kv_unstable_std")]
+ fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> {
+ self.visit_any(Value::from_dyn_error(err))
+ }
+
+ /// Visit an error.
+ #[cfg(feature = "kv_unstable_std")]
+ fn visit_borrowed_error(
+ &mut self,
+ err: &'v (dyn std::error::Error + 'static),
+ ) -> Result<(), Error> {
+ self.visit_any(Value::from_dyn_error(err))
+ }
+}
+
+impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T
+where
+ T: Visit<'v>,
+{
+ fn visit_any(&mut self, value: Value) -> Result<(), Error> {
+ (**self).visit_any(value)
+ }
+
+ fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
+ (**self).visit_u64(value)
+ }
+
+ fn visit_i64(&mut self, value: i64) -> Result<(), Error> {
+ (**self).visit_i64(value)
+ }
+
+ fn visit_u128(&mut self, value: u128) -> Result<(), Error> {
+ (**self).visit_u128(value)
+ }
+
+ fn visit_i128(&mut self, value: i128) -> Result<(), Error> {
+ (**self).visit_i128(value)
+ }
+
+ fn visit_f64(&mut self, value: f64) -> Result<(), Error> {
+ (**self).visit_f64(value)
+ }
+
+ fn visit_bool(&mut self, value: bool) -> Result<(), Error> {
+ (**self).visit_bool(value)
+ }
+
+ fn visit_str(&mut self, value: &str) -> Result<(), Error> {
+ (**self).visit_str(value)
+ }
+
+ fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> {
+ (**self).visit_borrowed_str(value)
+ }
+
+ fn visit_char(&mut self, value: char) -> Result<(), Error> {
+ (**self).visit_char(value)
+ }
+
+ #[cfg(feature = "kv_unstable_std")]
+ fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> {
+ (**self).visit_error(err)
+ }
+
+ #[cfg(feature = "kv_unstable_std")]
+ fn visit_borrowed_error(
+ &mut self,
+ err: &'v (dyn std::error::Error + 'static),
+ ) -> Result<(), Error> {
+ (**self).visit_borrowed_error(err)
+ }
}
#[cfg(test)]
@@ -469,6 +792,11 @@ pub(crate) mod tests {
Value::from(32u32),
Value::from(64u64),
Value::from(1usize),
+ Value::from(std::num::NonZeroU8::new(8).unwrap()),
+ Value::from(std::num::NonZeroU16::new(16).unwrap()),
+ Value::from(std::num::NonZeroU32::new(32).unwrap()),
+ Value::from(std::num::NonZeroU64::new(64).unwrap()),
+ Value::from(std::num::NonZeroUsize::new(1).unwrap()),
]
.into_iter()
}
@@ -480,6 +808,11 @@ pub(crate) mod tests {
Value::from(-32i32),
Value::from(-64i64),
Value::from(-1isize),
+ Value::from(std::num::NonZeroI8::new(-8).unwrap()),
+ Value::from(std::num::NonZeroI16::new(-16).unwrap()),
+ Value::from(std::num::NonZeroI32::new(-32).unwrap()),
+ Value::from(std::num::NonZeroI64::new(-64).unwrap()),
+ Value::from(std::num::NonZeroIsize::new(-1).unwrap()),
]
.into_iter()
}
@@ -652,4 +985,50 @@ pub(crate) mod tests {
assert!(v.is::<Foo>());
assert_eq!(42u64, v.downcast_ref::<Foo>().expect("invalid downcast").0);
}
+
+ #[test]
+ fn test_visit_integer() {
+ struct Extract(Option<u64>);
+
+ impl<'v> Visit<'v> for Extract {
+ fn visit_any(&mut self, value: Value) -> Result<(), Error> {
+ unimplemented!("unexpected value: {:?}", value)
+ }
+
+ fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
+ self.0 = Some(value);
+
+ Ok(())
+ }
+ }
+
+ let mut extract = Extract(None);
+ Value::from(42u64).visit(&mut extract).unwrap();
+
+ assert_eq!(Some(42), extract.0);
+ }
+
+ #[test]
+ fn test_visit_borrowed_str() {
+ struct Extract<'v>(Option<&'v str>);
+
+ impl<'v> Visit<'v> for Extract<'v> {
+ fn visit_any(&mut self, value: Value) -> Result<(), Error> {
+ unimplemented!("unexpected value: {:?}", value)
+ }
+
+ fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> {
+ self.0 = Some(value);
+
+ Ok(())
+ }
+ }
+
+ let mut extract = Extract(None);
+
+ let short_lived = String::from("A short-lived string");
+ Value::from(&*short_lived).visit(&mut extract).unwrap();
+
+ assert_eq!(Some("A short-lived string"), extract.0);
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index e754268..4ead826 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -24,7 +24,7 @@
//! though that default may be overridden. Logger implementations typically use
//! the target to filter requests based on some user configuration.
//!
-//! # Use
+//! # Usage
//!
//! The basic use of the log crate is through the five logging macros: [`error!`],
//! [`warn!`], [`info!`], [`debug!`] and [`trace!`]
@@ -86,6 +86,42 @@
//!
//! The logging system may only be initialized once.
//!
+//! ## Structured logging
+//!
+//! If you enable the `kv_unstable` feature you can associate structured values
+//! with your log records. If we take the example from before, we can include
+//! some additional context besides what's in the formatted message:
+//!
+//! ```edition2018
+//! # #[macro_use] extern crate serde;
+//! # #[derive(Debug, Serialize)] pub struct Yak(String);
+//! # impl Yak { fn shave(&mut self, _: u32) {} }
+//! # fn find_a_razor() -> Result<u32, std::io::Error> { Ok(1) }
+//! # #[cfg(feature = "kv_unstable_serde")]
+//! # fn main() {
+//! use log::{info, warn, as_serde, as_error};
+//!
+//! pub fn shave_the_yak(yak: &mut Yak) {
+//! info!(target: "yak_events", yak = as_serde!(yak); "Commencing yak shaving");
+//!
+//! loop {
+//! match find_a_razor() {
+//! Ok(razor) => {
+//! info!(razor = razor; "Razor located");
+//! yak.shave(razor);
+//! break;
+//! }
+//! Err(err) => {
+//! warn!(err = as_error!(err); "Unable to locate a razor, retrying");
+//! }
+//! }
+//! }
+//! }
+//! # }
+//! # #[cfg(not(feature = "kv_unstable_serde"))]
+//! # fn main() {}
+//! ```
+//!
//! # Available logging implementations
//!
//! In order to produce log output executables have to use
@@ -106,6 +142,14 @@
//! * Adaptors for other facilities:
//! * [syslog]
//! * [slog-stdlog]
+//! * [systemd-journal-logger]
+//! * [android_log]
+//! * [win_dbg_logger]
+//! * [db_logger]
+//! * For WebAssembly binaries:
+//! * [console_log]
+//! * For dynamic libraries:
+//! * You may need to construct an FFI-safe wrapper over `log` to initialize in your libraries
//!
//! # Implementing a Logger
//!
@@ -262,14 +306,18 @@
//! [slog-stdlog]: https://docs.rs/slog-stdlog/*/slog_stdlog/
//! [log4rs]: https://docs.rs/log4rs/*/log4rs/
//! [fern]: https://docs.rs/fern/*/fern/
+//! [systemd-journal-logger]: https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/
+//! [android_log]: https://docs.rs/android_log/*/android_log/
+//! [win_dbg_logger]: https://docs.rs/win_dbg_logger/*/win_dbg_logger/
+//! [console_log]: https://docs.rs/console_log/*/console_log/
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
- html_root_url = "https://docs.rs/log/0.4.14"
+ html_root_url = "https://docs.rs/log/0.4.17"
)]
#![warn(missing_docs)]
-#![deny(missing_debug_implementations)]
+#![deny(missing_debug_implementations, unconditional_recursion)]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
// When compiled for the rustc compiler itself we want to make sure that this is
// an unstable crate
@@ -560,6 +608,24 @@ impl Level {
pub fn as_str(&self) -> &'static str {
LOG_LEVEL_NAMES[*self as usize]
}
+
+ /// Iterate through all supported logging levels.
+ ///
+ /// The order of iteration is from more severe to less severe log messages.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use log::Level;
+ ///
+ /// let mut levels = Level::iter();
+ ///
+ /// assert_eq!(Some(Level::Error), levels.next());
+ /// assert_eq!(Some(Level::Trace), levels.last());
+ /// ```
+ pub fn iter() -> impl Iterator<Item = Self> {
+ (1..6).map(|i| Self::from_usize(i).unwrap())
+ }
}
/// An enum representing the available verbosity level filters of the logger.
@@ -702,6 +768,7 @@ impl LevelFilter {
_ => None,
}
}
+
/// Returns the most verbose logging level filter.
#[inline]
pub fn max() -> LevelFilter {
@@ -722,6 +789,24 @@ impl LevelFilter {
pub fn as_str(&self) -> &'static str {
LOG_LEVEL_NAMES[*self as usize]
}
+
+ /// Iterate through all supported filtering levels.
+ ///
+ /// The order of iteration is from less to more verbose filtering.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use log::LevelFilter;
+ ///
+ /// let mut levels = LevelFilter::iter();
+ ///
+ /// assert_eq!(Some(LevelFilter::Off), levels.next());
+ /// assert_eq!(Some(LevelFilter::Trace), levels.last());
+ /// ```
+ pub fn iter() -> impl Iterator<Item = Self> {
+ (0..6).map(|i| Self::from_usize(i).unwrap())
+ }
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
@@ -880,7 +965,7 @@ impl<'a> Record<'a> {
self.line
}
- /// The structued key-value pairs associated with the message.
+ /// The structured key-value pairs associated with the message.
#[cfg(feature = "kv_unstable")]
#[inline]
pub fn key_values(&self) -> &dyn kv::Source {
@@ -915,7 +1000,6 @@ impl<'a> Record<'a> {
///
/// # Examples
///
-///
/// ```edition2018
/// use log::{Level, Record};
///
@@ -1189,10 +1273,18 @@ pub trait Log: Sync + Send {
/// This is used by the `log_enabled!` macro to allow callers to avoid
/// expensive computation of log message arguments if the message would be
/// discarded anyway.
+ ///
+ /// # For implementors
+ ///
+ /// This method isn't called automatically by the `log!` macros.
+ /// It's up to an implementation of the `Log` trait to call `enabled` in its own
+ /// `log` method implementation to guarantee that filtering is applied.
fn enabled(&self, metadata: &Metadata) -> bool;
/// Logs the `Record`.
///
+ /// # For implementors
+ ///
/// Note that `enabled` is *not* necessarily called before this method.
/// Implementations of `log` should perform all necessary filtering
/// internally.
@@ -1214,6 +1306,22 @@ impl Log for NopLogger {
fn flush(&self) {}
}
+impl<T> Log for &'_ T
+where
+ T: ?Sized + Log,
+{
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ (**self).enabled(metadata)
+ }
+
+ fn log(&self, record: &Record) {
+ (**self).log(record)
+ }
+ fn flush(&self) {
+ (**self).flush()
+ }
+}
+
#[cfg(feature = "std")]
impl<T> Log for std::boxed::Box<T>
where
@@ -1231,12 +1339,31 @@ where
}
}
+#[cfg(feature = "std")]
+impl<T> Log for std::sync::Arc<T>
+where
+ T: ?Sized + Log,
+{
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ self.as_ref().enabled(metadata)
+ }
+
+ fn log(&self, record: &Record) {
+ self.as_ref().log(record)
+ }
+ fn flush(&self) {
+ self.as_ref().flush()
+ }
+}
+
/// Sets the global maximum log level.
///
/// Generally, this should only be called by the active logging implementation.
+///
+/// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs.
#[inline]
pub fn set_max_level(level: LevelFilter) {
- MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::SeqCst)
+ MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed)
}
/// Returns the current maximum log level.
@@ -1362,6 +1489,8 @@ where
}
INITIALIZING => {
while STATE.load(Ordering::SeqCst) == INITIALIZING {
+ // TODO: replace with `hint::spin_loop` once MSRV is 1.49.0.
+ #[allow(deprecated)]
std::sync::atomic::spin_loop_hint();
}
Err(SetLoggerError(()))
@@ -1452,10 +1581,39 @@ pub fn logger() -> &'static dyn Log {
// WARNING: this is not part of the crate's public API and is subject to change at any time
#[doc(hidden)]
+#[cfg(not(feature = "kv_unstable"))]
+pub fn __private_api_log(
+ args: fmt::Arguments,
+ level: Level,
+ &(target, module_path, file, line): &(&str, &'static str, &'static str, u32),
+ kvs: Option<&[(&str, &str)]>,
+) {
+ if kvs.is_some() {
+ panic!(
+ "key-value support is experimental and must be enabled using the `kv_unstable` feature"
+ )
+ }
+
+ logger().log(
+ &Record::builder()
+ .args(args)
+ .level(level)
+ .target(target)
+ .module_path_static(Some(module_path))
+ .file_static(Some(file))
+ .line(Some(line))
+ .build(),
+ );
+}
+
+// WARNING: this is not part of the crate's public API and is subject to change at any time
+#[doc(hidden)]
+#[cfg(feature = "kv_unstable")]
pub fn __private_api_log(
args: fmt::Arguments,
level: Level,
&(target, module_path, file, line): &(&str, &'static str, &'static str, u32),
+ kvs: Option<&[(&str, &dyn kv::ToValue)]>,
) {
logger().log(
&Record::builder()
@@ -1465,6 +1623,7 @@ pub fn __private_api_log(
.module_path_static(Some(module_path))
.file_static(Some(file))
.line(Some(line))
+ .key_values(&kvs)
.build(),
);
}
@@ -1475,6 +1634,12 @@ pub fn __private_api_enabled(level: Level, target: &str) -> bool {
logger().enabled(&Metadata::builder().level(level).target(target).build())
}
+// WARNING: this is not part of the crate's public API and is subject to change at any time
+#[doc(hidden)]
+pub mod __private_api {
+ pub use std::option::Option;
+}
+
/// The statically resolved maximum log level.
///
/// See the crate level documentation for information on how to configure this.
@@ -1772,4 +1937,35 @@ mod tests {
.expect("invalid value")
);
}
+
+ // Test that the `impl Log for Foo` blocks work
+ // This test mostly operates on a type level, so failures will be compile errors
+ #[test]
+ fn test_foreign_impl() {
+ use super::Log;
+ #[cfg(feature = "std")]
+ use std::sync::Arc;
+
+ fn assert_is_log<T: Log + ?Sized>() {}
+
+ assert_is_log::<&dyn Log>();
+
+ #[cfg(feature = "std")]
+ assert_is_log::<Box<dyn Log>>();
+
+ #[cfg(feature = "std")]
+ assert_is_log::<Arc<dyn Log>>();
+
+ // Assert these statements for all T: Log + ?Sized
+ #[allow(unused)]
+ fn forall<T: Log + ?Sized>() {
+ #[cfg(feature = "std")]
+ assert_is_log::<Box<T>>();
+
+ assert_is_log::<&T>();
+
+ #[cfg(feature = "std")]
+ assert_is_log::<Arc<T>>();
+ }
+ }
}
diff --git a/src/macros.rs b/src/macros.rs
index a234e04..f214d0d 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -29,6 +29,20 @@
/// ```
#[macro_export(local_inner_macros)]
macro_rules! log {
+ // log!(target: "my_target", Level::Info; key1 = 42, key2 = true; "a {} event", "log");
+ (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ({
+ let lvl = $lvl;
+ if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() {
+ $crate::__private_api_log(
+ __log_format_args!($($arg)+),
+ lvl,
+ &($target, __log_module_path!(), __log_file!(), __log_line!()),
+ $crate::__private_api::Option::Some(&[$((__log_key!($key), &$value)),+])
+ );
+ }
+ });
+
+ // log!(target: "my_target", Level::Info; "a {} event", "log");
(target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
let lvl = $lvl;
if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() {
@@ -36,10 +50,13 @@ macro_rules! log {
__log_format_args!($($arg)+),
lvl,
&($target, __log_module_path!(), __log_file!(), __log_line!()),
+ $crate::__private_api::Option::None,
);
}
});
- ($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+))
+
+ // log!(Level::Info, "a log event")
+ ($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+));
}
/// Logs a message at the error level.
@@ -58,12 +75,12 @@ macro_rules! log {
/// ```
#[macro_export(local_inner_macros)]
macro_rules! error {
- (target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Error, $($arg)+)
- );
- ($($arg:tt)+) => (
- log!($crate::Level::Error, $($arg)+)
- )
+ // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log")
+ // error!(target: "my_target", "a {} event", "log")
+ (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Error, $($arg)+));
+
+ // error!("a {} event", "log")
+ ($($arg:tt)+) => (log!($crate::Level::Error, $($arg)+))
}
/// Logs a message at the warn level.
@@ -82,12 +99,12 @@ macro_rules! error {
/// ```
#[macro_export(local_inner_macros)]
macro_rules! warn {
- (target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Warn, $($arg)+)
- );
- ($($arg:tt)+) => (
- log!($crate::Level::Warn, $($arg)+)
- )
+ // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log")
+ // warn!(target: "my_target", "a {} event", "log")
+ (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Warn, $($arg)+));
+
+ // warn!("a {} event", "log")
+ ($($arg:tt)+) => (log!($crate::Level::Warn, $($arg)+))
}
/// Logs a message at the info level.
@@ -108,12 +125,12 @@ macro_rules! warn {
/// ```
#[macro_export(local_inner_macros)]
macro_rules! info {
- (target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Info, $($arg)+)
- );
- ($($arg:tt)+) => (
- log!($crate::Level::Info, $($arg)+)
- )
+ // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log")
+ // info!(target: "my_target", "a {} event", "log")
+ (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Info, $($arg)+));
+
+ // info!("a {} event", "log")
+ ($($arg:tt)+) => (log!($crate::Level::Info, $($arg)+))
}
/// Logs a message at the debug level.
@@ -133,12 +150,12 @@ macro_rules! info {
/// ```
#[macro_export(local_inner_macros)]
macro_rules! debug {
- (target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Debug, $($arg)+)
- );
- ($($arg:tt)+) => (
- log!($crate::Level::Debug, $($arg)+)
- )
+ // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log")
+ // debug!(target: "my_target", "a {} event", "log")
+ (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Debug, $($arg)+));
+
+ // debug!("a {} event", "log")
+ ($($arg:tt)+) => (log!($crate::Level::Debug, $($arg)+))
}
/// Logs a message at the trace level.
@@ -160,12 +177,12 @@ macro_rules! debug {
/// ```
#[macro_export(local_inner_macros)]
macro_rules! trace {
- (target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Trace, $($arg)+)
- );
- ($($arg:tt)+) => (
- log!($crate::Level::Trace, $($arg)+)
- )
+ // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log")
+ // trace!(target: "my_target", "a {} event", "log")
+ (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Trace, $($arg)+));
+
+ // trace!("a {} event", "log")
+ ($($arg:tt)+) => (log!($crate::Level::Trace, $($arg)+))
}
/// Determines if a message logged at the specified level in that module will
@@ -248,3 +265,16 @@ macro_rules! __log_line {
line!()
};
}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __log_key {
+ // key1 = 42
+ ($($args:ident)*) => {
+ stringify!($($args)*)
+ };
+ // "key1" = 42
+ ($($args:expr)*) => {
+ $($args)*
+ };
+}