diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/key/dynamic.rs | 203 | ||||
-rw-r--r-- | src/key/dynamic_nostd.rs | 168 | ||||
-rw-r--r-- | src/key/mod.rs | 10 | ||||
-rw-r--r-- | src/key/static.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 3752 | ||||
-rw-r--r-- | src/tests.rs | 419 |
6 files changed, 4554 insertions, 0 deletions
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(); +} |