diff --git a/Cargo.toml b/Cargo.toml index c78d884..2ba2109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,33 @@ license = "Apache-2.0/MIT" readme = "Readme.md" [dependencies] -chrono = "0.4" -lunatic = "0.12" -serde = { version = "1.0", features = ["derive"] } -yansi = "0.5.1" +cfg-if = "1.0.0" +lunatic = { version = "0.12.1", features = ["json_serializer"] } +lunatic-cached-process = "0.2.2" +lunatic-message-request = "0.1.1" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.94" + +ansi_term = { version = "0.12.1", optional = true } +chrono = { version = "0.4.23", optional = true } + +[features] +default = ["fmt"] +fmt = ["ansi_term", "chrono"] + +max_level_off = [] +max_level_error = [] +max_level_warn = [] +max_level_info = [] +max_level_debug = [] +max_level_trace = [] + +release_max_level_off = [] +release_max_level_error = [] +release_max_level_warn = [] +release_max_level_info = [] +release_max_level_debug = [] +release_max_level_trace = [] [package.metadata.docs.rs] targets = ["wasm32-wasi"] diff --git a/examples/data.rs b/examples/data.rs new file mode 100644 index 0000000..19e3098 --- /dev/null +++ b/examples/data.rs @@ -0,0 +1,36 @@ +#![allow(clippy::let_unit_value)] + +use lunatic_log::{info, subscriber::fmt::FmtSubscriber, LevelFilter}; +use serde::Serialize; + +#[derive(Serialize)] +struct Person { + name: String, + age: u8, +} + +fn main() { + // Init a subscriber + FmtSubscriber::new(LevelFilter::TRACE).pretty().init(); + + // Create some mock data + let null = (); + let bool_t = true; + let bool_f = false; + let number = 10.43; + let string = "Hello, World!"; + let array = vec![1, 2, 3]; + let object = Person { + name: "John Doe".to_string(), + age: 23, + }; + + // Log data + info!( + null, + bool_t, bool_f, number, string, array, object, "Additional log message." + ); + + // Wait for events to propagate + lunatic::sleep(std::time::Duration::from_millis(50)); +} diff --git a/examples/info.rs b/examples/info.rs index 8730e97..2ba5b4e 100644 --- a/examples/info.rs +++ b/examples/info.rs @@ -2,7 +2,7 @@ use lunatic_log::{info, subscriber::fmt::FmtSubscriber, LevelFilter}; fn main() { // Initialize subscriber - lunatic_log::init(FmtSubscriber::new(LevelFilter::Info)); + FmtSubscriber::new(LevelFilter::INFO).init(); // Log message info!("Hello, {}", "World"); diff --git a/examples/multiple.rs b/examples/multiple.rs index fb8b5e1..d5bece6 100644 --- a/examples/multiple.rs +++ b/examples/multiple.rs @@ -5,13 +5,11 @@ use lunatic_log::{ }; fn main() { - // Setup multiple subscribers - let subscriber = MultipleSubscribers::new() - .add_subscriber(FmtSubscriber::new(LevelFilter::Info)) - .add_subscriber(FmtSubscriber::new(LevelFilter::Info)); - // Initialize multiple subscribers - lunatic_log::init(subscriber); + MultipleSubscribers::new() + .add_subscriber(FmtSubscriber::new(LevelFilter::INFO)) + .add_subscriber(FmtSubscriber::new(LevelFilter::INFO)) + .init(); // Log message info!("Hello, {}", "World"); diff --git a/examples/pretty.rs b/examples/pretty.rs index 23f2a2f..f0708ce 100644 --- a/examples/pretty.rs +++ b/examples/pretty.rs @@ -2,7 +2,7 @@ use lunatic_log::{debug, error, info, subscriber::fmt::FmtSubscriber, trace, war fn main() { // Initialize subscriber - lunatic_log::init(FmtSubscriber::new(LevelFilter::Trace).pretty()); + FmtSubscriber::new(LevelFilter::TRACE).pretty().init(); // Log message error!("Error"); diff --git a/src/level.rs b/src/level.rs index 4f3a8f6..aaa89ae 100644 --- a/src/level.rs +++ b/src/level.rs @@ -1,372 +1,318 @@ -use std::{cmp, error, fmt, str::FromStr}; +//! Trace verbosity level filtering. -use serde::{Deserialize, Serialize}; +mod filter; -static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; +use std::{cmp, fmt, str}; -static LEVEL_PARSE_ERROR: &str = - "attempted to convert a string that doesn't match an existing log level"; +pub use filter::*; +use serde::{Deserialize, Serialize}; -/// An enum representing the available verbosity levels of the logger. +/// Describes the level of verbosity of a span or event. /// -/// Typical usage includes: checking if a certain `Level` is enabled with -/// [`log!`](super::log), and comparing a [`Level`] directly to a -/// [`LevelFilter`](LevelFilter). -#[repr(usize)] -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] -pub enum Level { +/// # Comparing Levels +/// +/// `Level` implements the [`PartialOrd`] and [`Ord`] traits, allowing two +/// `Level`s to be compared to determine which is considered more or less +/// verbose. Levels which are more verbose are considered "greater than" levels +/// which are less verbose, with [`Level::ERROR`] considered the lowest, and +/// [`Level::TRACE`] considered the highest. +/// +/// For example: +/// ``` +/// use tracing_core::Level; +/// +/// assert!(Level::TRACE > Level::DEBUG); +/// assert!(Level::ERROR < Level::WARN); +/// assert!(Level::INFO <= Level::DEBUG); +/// assert_eq!(Level::TRACE, Level::TRACE); +/// ``` +/// +/// # Filtering +/// +/// `Level`s are typically used to implement filtering that determines which +/// spans and events are enabled. Depending on the use case, more or less +/// verbose diagnostics may be desired. For example, when running in +/// development, [`DEBUG`]-level traces may be enabled by default. When running in +/// production, only [`INFO`]-level and lower traces might be enabled. Libraries +/// may include very verbose diagnostics at the [`DEBUG`] and/or [`TRACE`] levels. +/// Applications using those libraries typically chose to ignore those traces. However, when +/// debugging an issue involving said libraries, it may be useful to temporarily +/// enable the more verbose traces. +/// +/// The [`LevelFilter`] type is provided to enable filtering traces by +/// verbosity. `Level`s can be compared against [`LevelFilter`]s, and +/// [`LevelFilter`] has a variant for each `Level`, which compares analogously +/// to that level. In addition, [`LevelFilter`] adds a [`LevelFilter::OFF`] +/// variant, which is considered "less verbose" than every other `Level`. This is +/// intended to allow filters to completely disable tracing in a particular context. +/// +/// For example: +/// ``` +/// use tracing_core::{Level, LevelFilter}; +/// +/// assert!(LevelFilter::OFF < Level::TRACE); +/// assert!(LevelFilter::TRACE > Level::DEBUG); +/// assert!(LevelFilter::ERROR < Level::WARN); +/// assert!(LevelFilter::INFO <= Level::DEBUG); +/// assert!(LevelFilter::INFO >= Level::INFO); +/// ``` +/// +/// ## Examples +/// +/// Below is a simple example of how a [collector] could implement filtering through +/// a [`LevelFilter`]. When a span or event is recorded, the [`Collect::enabled`] method +/// compares the span or event's `Level` against the configured [`LevelFilter`]. +/// The optional [`Collect::max_level_hint`] method can also be implemented to allow spans +/// and events above a maximum verbosity level to be skipped more efficiently, +/// often improving performance in short-lived programs. +/// +/// ``` +/// use tracing_core::{span, Event, Level, LevelFilter, Collect, Metadata}; +/// # use tracing_core::span::{Id, Record, Current}; +/// +/// #[derive(Debug)] +/// pub struct MyCollector { +/// /// The most verbose level that this collector will enable. +/// max_level: LevelFilter, +/// +/// // ... +/// } +/// +/// impl MyCollector { +/// /// Returns a new `MyCollector` which will record spans and events up to +/// /// `max_level`. +/// pub fn with_max_level(max_level: LevelFilter) -> Self { +/// Self { +/// max_level, +/// // ... +/// } +/// } +/// } +/// impl Collect for MyCollector { +/// fn enabled(&self, meta: &Metadata<'_>) -> bool { +/// // A span or event is enabled if it is at or below the configured +/// // maximum level. +/// meta.level() <= &self.max_level +/// } +/// +/// // This optional method returns the most verbose level that this +/// // collector will enable. Although implementing this method is not +/// // *required*, it permits additional optimizations when it is provided, +/// // allowing spans and events above the max level to be skipped +/// // more efficiently. +/// fn max_level_hint(&self) -> Option { +/// Some(self.max_level) +/// } +/// +/// // Implement the rest of the collector... +/// fn new_span(&self, span: &span::Attributes<'_>) -> span::Id { +/// // ... +/// # drop(span); Id::from_u64(1) +/// } + +/// fn event(&self, event: &Event<'_>) { +/// // ... +/// # drop(event); +/// } +/// +/// // ... +/// # fn enter(&self, _: &Id) {} +/// # fn exit(&self, _: &Id) {} +/// # fn record(&self, _: &Id, _: &Record<'_>) {} +/// # fn record_follows_from(&self, _: &Id, _: &Id) {} +/// # fn current_span(&self) -> Current { Current::unknown() } +/// } +/// ``` +/// +/// It is worth noting that the `tracing-subscriber` crate provides [additional +/// APIs][envfilter] for performing more sophisticated filtering, such as +/// enabling different levels based on which module or crate a span or event is +/// recorded in. +/// +/// [`DEBUG`]: Level::DEBUG +/// [`INFO`]: Level::INFO +/// [`TRACE`]: Level::TRACE +/// [`Collect::enabled`]: crate::collect::Collect::enabled +/// [`Collect::max_level_hint`]: crate::collect::Collect::max_level_hint +/// [collector]: crate::collect::Collect +/// [envfilter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Level(LevelInner); + +impl Level { /// The "error" level. /// /// Designates very serious errors. - // This way these line up with the discriminants for [LevelFilter] below - // This works because Rust treats field-less enums the same way as C does: - // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-field-less-enumerations - Error = 1, + pub const ERROR: Level = Level(LevelInner::Error); /// The "warn" level. /// /// Designates hazardous situations. - Warn, + pub const WARN: Level = Level(LevelInner::Warn); /// The "info" level. /// /// Designates useful information. - Info, + pub const INFO: Level = Level(LevelInner::Info); /// The "debug" level. /// /// Designates lower priority information. - Debug, + pub const DEBUG: Level = Level(LevelInner::Debug); /// The "trace" level. /// /// Designates very low priority, often extremely verbose, information. - Trace, -} + pub const TRACE: Level = Level(LevelInner::Trace); -impl PartialEq for Level { - #[inline] - fn eq(&self, other: &LevelFilter) -> bool { - *self as usize == *other as usize - } -} - -impl PartialOrd for Level { - #[inline] - fn partial_cmp(&self, other: &Level) -> Option { - Some(self.cmp(other)) - } - - #[inline] - fn lt(&self, other: &Level) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &Level) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &Level) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &Level) -> bool { - *self as usize >= *other as usize - } -} - -impl PartialOrd for Level { - #[inline] - fn partial_cmp(&self, other: &LevelFilter) -> Option { - Some((*self as usize).cmp(&(*other as usize))) - } - - #[inline] - fn lt(&self, other: &LevelFilter) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &LevelFilter) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &LevelFilter) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &LevelFilter) -> bool { - *self as usize >= *other as usize - } -} - -impl Ord for Level { - #[inline] - fn cmp(&self, other: &Level) -> cmp::Ordering { - (*self as usize).cmp(&(*other as usize)) - } -} - -// Reimplemented here because std::ascii is not available in libcore -fn eq_ignore_ascii_case(a: &str, b: &str) -> bool { - fn to_ascii_uppercase(c: u8) -> u8 { - if (b'a'..=b'z').contains(&c) { - c - b'a' + b'A' - } else { - c + /// Returns the string representation of the `Level`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + match *self { + Level::TRACE => "TRACE", + Level::DEBUG => "DEBUG", + Level::INFO => "INFO", + Level::WARN => "WARN", + Level::ERROR => "ERROR", } } - - if a.len() == b.len() { - a.bytes() - .zip(b.bytes()) - .all(|(a, b)| to_ascii_uppercase(a) == to_ascii_uppercase(b)) - } else { - false - } -} - -impl FromStr for Level { - type Err = ParseLevelError; - fn from_str(level: &str) -> Result { - LOG_LEVEL_NAMES - .iter() - .position(|&name| eq_ignore_ascii_case(name, level)) - .into_iter() - .filter(|&idx| idx != 0) - .map(|idx| Level::from_usize(idx).unwrap()) - .next() - .ok_or(ParseLevelError(())) - } } impl fmt::Display for Level { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.pad(self.as_str()) - } -} - -impl Level { - fn from_usize(u: usize) -> Option { - match u { - 1 => Some(Level::Error), - 2 => Some(Level::Warn), - 3 => Some(Level::Info), - 4 => Some(Level::Debug), - 5 => Some(Level::Trace), - _ => None, + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Level::TRACE => f.pad("TRACE"), + Level::DEBUG => f.pad("DEBUG"), + Level::INFO => f.pad("INFO"), + Level::WARN => f.pad("WARN"), + Level::ERROR => f.pad("ERROR"), } } +} - /// Returns the most verbose logging level. - #[inline] - pub fn max() -> Level { - Level::Trace - } - - /// Converts the [`Level`] to the equivalent [`LevelFilter`]. - #[inline] - pub fn to_level_filter(&self) -> LevelFilter { - LevelFilter::from_usize(*self as usize).unwrap() - } +impl std::error::Error for ParseLevelError {} - /// Returns the string representation of the [`Level`]. - /// - /// This returns the same string as the [`fmt::Display`] implementation. - pub fn as_str(&self) -> &'static str { - LOG_LEVEL_NAMES[*self as usize] +impl str::FromStr for Level { + type Err = ParseLevelError; + fn from_str(s: &str) -> Result { + s.parse::() + .map_err(|_| ParseLevelError { _p: () }) + .and_then(|num| match num { + 1 => Ok(Level::ERROR), + 2 => Ok(Level::WARN), + 3 => Ok(Level::INFO), + 4 => Ok(Level::DEBUG), + 5 => Ok(Level::TRACE), + _ => Err(ParseLevelError { _p: () }), + }) + .or_else(|_| match s { + s if s.eq_ignore_ascii_case("error") => Ok(Level::ERROR), + s if s.eq_ignore_ascii_case("warn") => Ok(Level::WARN), + s if s.eq_ignore_ascii_case("info") => Ok(Level::INFO), + s if s.eq_ignore_ascii_case("debug") => Ok(Level::DEBUG), + s if s.eq_ignore_ascii_case("trace") => Ok(Level::TRACE), + _ => Err(ParseLevelError { _p: () }), + }) } +} - /// Iterate through all supported logging levels. +#[repr(usize)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +enum LevelInner { + /// The "trace" level. /// - /// The order of iteration is from more severe to less severe log messages. + /// Designates very low priority, often extremely verbose, information. + Trace = 0, + /// The "debug" level. /// - /// # Examples + /// Designates lower priority information. + Debug = 1, + /// The "info" level. /// - /// ``` - /// use lunatic_log::Level; + /// Designates useful information. + Info = 2, + /// The "warn" level. /// - /// let mut levels = Level::iter(); + /// Designates hazardous situations. + Warn = 3, + /// The "error" level. /// - /// assert_eq!(Some(Level::Error), levels.next()); - /// assert_eq!(Some(Level::Trace), levels.last()); - /// ``` - pub fn iter() -> impl Iterator { - (1..6).map(|i| Self::from_usize(i).unwrap()) - } -} - -/// An enum representing the available verbosity level filters of the logger. -/// -/// A [`LevelFilter`] may be compared directly to a [`Level`]. -#[repr(usize)] -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] -pub enum LevelFilter { - /// A level lower than all log levels. - Off, - /// Corresponds to the `Error` log level. - Error, - /// Corresponds to the `Warn` log level. - Warn, - /// Corresponds to the `Info` log level. - Info, - /// Corresponds to the `Debug` log level. - Debug, - /// Corresponds to the `Trace` log level. - Trace, + /// Designates very serious errors. + Error = 4, } -impl PartialEq for LevelFilter { - #[inline] - fn eq(&self, other: &Level) -> bool { - other.eq(self) - } +/// Returned if parsing a `Level` fails. +#[derive(Debug)] +pub struct ParseLevelError { + _p: (), } -impl PartialOrd for LevelFilter { - #[inline] - fn partial_cmp(&self, other: &LevelFilter) -> Option { - Some(self.cmp(other)) - } - - #[inline] - fn lt(&self, other: &LevelFilter) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &LevelFilter) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &LevelFilter) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &LevelFilter) -> bool { - *self as usize >= *other as usize +impl fmt::Display for ParseLevelError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad( + "error parsing level: expected one of \"error\", \"warn\", \ + \"info\", \"debug\", \"trace\", or a number 1-5", + ) } } -impl PartialOrd for LevelFilter { - #[inline] +impl PartialOrd for Level { + #[inline(always)] fn partial_cmp(&self, other: &Level) -> Option { - Some((*self as usize).cmp(&(*other as usize))) + Some(self.cmp(other)) } - #[inline] + #[inline(always)] fn lt(&self, other: &Level) -> bool { - (*self as usize) < *other as usize + (other.0 as usize) < (self.0 as usize) } - #[inline] + #[inline(always)] fn le(&self, other: &Level) -> bool { - *self as usize <= *other as usize + (other.0 as usize) <= (self.0 as usize) } - #[inline] + #[inline(always)] fn gt(&self, other: &Level) -> bool { - *self as usize > *other as usize + (other.0 as usize) > (self.0 as usize) } - #[inline] + #[inline(always)] fn ge(&self, other: &Level) -> bool { - *self as usize >= *other as usize - } -} - -impl Ord for LevelFilter { - #[inline] - fn cmp(&self, other: &LevelFilter) -> cmp::Ordering { - (*self as usize).cmp(&(*other as usize)) + (other.0 as usize) >= (self.0 as usize) } } -impl FromStr for LevelFilter { - type Err = ParseLevelError; - fn from_str(level: &str) -> Result { - LOG_LEVEL_NAMES - .iter() - .position(|&name| eq_ignore_ascii_case(name, level)) - .map(|p| LevelFilter::from_usize(p).unwrap()) - .ok_or(ParseLevelError(())) +impl Ord for Level { + #[inline(always)] + fn cmp(&self, other: &Self) -> cmp::Ordering { + (other.0 as usize).cmp(&(self.0 as usize)) } } -impl fmt::Display for LevelFilter { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.pad(self.as_str()) +impl Serialize for LevelInner { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_u8(*self as u8) } } -impl LevelFilter { - fn from_usize(u: usize) -> Option { - match u { - 0 => Some(LevelFilter::Off), - 1 => Some(LevelFilter::Error), - 2 => Some(LevelFilter::Warn), - 3 => Some(LevelFilter::Info), - 4 => Some(LevelFilter::Debug), - 5 => Some(LevelFilter::Trace), - _ => None, +impl<'de> Deserialize<'de> for LevelInner { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let level = u8::deserialize(deserializer)?; + match level { + 0 => Ok(LevelInner::Trace), + 1 => Ok(LevelInner::Trace), + 2 => Ok(LevelInner::Trace), + 3 => Ok(LevelInner::Trace), + 4 => Ok(LevelInner::Trace), + n => Err(::invalid_value( + serde::de::Unexpected::Unsigned(n as u64), + &"number in range 0..=4", + )), } } - - /// Returns the most verbose logging level filter. - #[inline] - pub fn max() -> LevelFilter { - LevelFilter::Trace - } - - /// Converts `self` to the equivalent [`Level`]. - /// - /// Returns [`None`] if `self` is [`LevelFilter::Off`]. - #[inline] - pub fn to_level(&self) -> Option { - Level::from_usize(*self as usize) - } - - /// Returns the string representation of the [`LevelFilter`]. - /// - /// This returns the same string as the [`fmt::Display`] implementation. - pub fn as_str(&self) -> &'static str { - LOG_LEVEL_NAMES[*self as usize] - } - - /// Iterate through all supported filtering levels. - /// - /// The order of iteration is from less to more verbose filtering. - /// - /// # Examples - /// - /// ``` - /// use lunatic_log::LevelFilter; - /// - /// let mut levels = LevelFilter::iter(); - /// - /// assert_eq!(Some(LevelFilter::Off), levels.next()); - /// assert_eq!(Some(LevelFilter::Trace), levels.last()); - /// ``` - pub fn iter() -> impl Iterator { - (0..6).map(|i| Self::from_usize(i).unwrap()) - } -} - -/// The type returned by [`from_str`] when the string doesn't match any of the log levels. -/// -/// [`from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str -#[allow(missing_copy_implementations)] -#[derive(Debug, PartialEq)] -pub struct ParseLevelError(()); - -impl fmt::Display for ParseLevelError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(LEVEL_PARSE_ERROR) - } } - -// The Error trait is not available in libcore -impl error::Error for ParseLevelError {} diff --git a/src/level/filter.rs b/src/level/filter.rs new file mode 100644 index 0000000..86dbf64 --- /dev/null +++ b/src/level/filter.rs @@ -0,0 +1,418 @@ +use std::{ + cmp, fmt, str, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use lunatic_cached_process::CachedLookup; +use lunatic_message_request::ProcessRequest; +use serde::{Deserialize, Serialize}; + +use crate::subscriber::{SubscriberMessage, SUBSCRIBER}; + +use super::{Level, LevelInner}; + +static MAX_LEVEL: AtomicUsize = AtomicUsize::new(LevelFilter::UNSET_USIZE); + +/// A filter comparable to a verbosity [`Level`]. +/// +/// If a [`Level`] is considered less than a `LevelFilter`, it should be +/// considered enabled; if greater than or equal to the `LevelFilter`, +/// that level is disabled. See [`LevelFilter::current`] for more +/// details. +/// +/// Note that this is essentially identical to the `Level` type, but with the +/// addition of an [`OFF`] level that completely disables all trace +/// instrumentation. +/// +/// See the documentation for the [`Level`] type to see how `Level`s +/// and `LevelFilter`s interact. +/// +/// [`OFF`]: LevelFilter::OFF +#[repr(transparent)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct LevelFilter(Option); + +/// Indicates that a string could not be parsed to a valid level. +#[derive(Clone, Debug)] +pub struct ParseLevelFilterError(()); + +impl From for LevelFilter { + #[inline] + fn from(level: Level) -> Self { + Self::from_level(level) + } +} + +impl From> for LevelFilter { + #[inline] + fn from(level: Option) -> Self { + Self(level) + } +} + +impl From for Option { + #[inline] + fn from(filter: LevelFilter) -> Self { + filter.into_level() + } +} + +impl LevelFilter { + /// The "off" level. + /// + /// Designates that trace instrumentation should be completely disabled. + pub const OFF: LevelFilter = LevelFilter(None); + /// The "error" level. + /// + /// Designates very serious errors. + pub const ERROR: LevelFilter = LevelFilter::from_level(Level::ERROR); + /// The "warn" level. + /// + /// Designates hazardous situations. + pub const WARN: LevelFilter = LevelFilter::from_level(Level::WARN); + /// The "info" level. + /// + /// Designates useful information. + pub const INFO: LevelFilter = LevelFilter::from_level(Level::INFO); + /// The "debug" level. + /// + /// Designates lower priority information. + pub const DEBUG: LevelFilter = LevelFilter::from_level(Level::DEBUG); + /// The "trace" level. + /// + /// Designates very low priority, often extremely verbose, information. + pub const TRACE: LevelFilter = LevelFilter(Some(Level::TRACE)); + + /// Returns a `LevelFilter` that enables spans and events with verbosity up + /// to and including `level`. + pub const fn from_level(level: Level) -> Self { + Self(Some(level)) + } + + /// Returns the most verbose [`Level`] that this filter accepts, or `None` + /// if it is [`OFF`]. + /// + /// [`Level`]: super::Level + /// [`OFF`]: LevelFilter::OFF + pub const fn into_level(self) -> Option { + self.0 + } + + // These consts are necessary because `as` casts are not allowed as + // match patterns. + const ERROR_USIZE: usize = LevelInner::Error as usize; + const WARN_USIZE: usize = LevelInner::Warn as usize; + const INFO_USIZE: usize = LevelInner::Info as usize; + const DEBUG_USIZE: usize = LevelInner::Debug as usize; + const TRACE_USIZE: usize = LevelInner::Trace as usize; + // Using the value of the last variant + 1 ensures that we match the value + // for `Option::None` as selected by the niche optimization for + // `LevelFilter`. If this is the case, converting a `usize` value into a + // `LevelFilter` (in `LevelFilter::current`) will be an identity conversion, + // rather than generating a lookup table. + const OFF_USIZE: usize = LevelInner::Error as usize + 1; + const UNSET_USIZE: usize = usize::MAX; + + /// Returns a `LevelFilter` that matches the most verbose [`Level`] that any + /// currently active [collector] will enable. + /// + /// User code should treat this as a *hint*. If a given span or event has a + /// level *higher* than the returned `LevelFilter`, it will not be enabled. + /// However, if the level is less than or equal to this value, the span or + /// event is *not* guaranteed to be enabled; the collector will still + /// filter each callsite individually. + /// + /// Therefore, comparing a given span or event's level to the returned + /// `LevelFilter` **can** be used for determining if something is + /// *disabled*, but **should not** be used for determining if something is + /// *enabled*. + /// + /// [`Level`]: super::Level + /// [collector]: super::Collect + #[inline(always)] + pub fn current() -> Self { + match MAX_LEVEL.load(Ordering::Relaxed) { + Self::ERROR_USIZE => Self::ERROR, + Self::WARN_USIZE => Self::WARN, + Self::INFO_USIZE => Self::INFO, + Self::DEBUG_USIZE => Self::DEBUG, + Self::TRACE_USIZE => Self::TRACE, + Self::OFF_USIZE => Self::OFF, + Self::UNSET_USIZE => { + // Load the max level for this process + match SUBSCRIBER.get() { + Some(process) => { + let max_level = process + .request(SubscriberMessage::MaxLevelHint, ()) + .unwrap_or(LevelFilter::TRACE); + LevelFilter::set_max(max_level); + max_level + } + None => Self::OFF, + } + } + #[cfg(debug_assertions)] + unknown => unreachable!( + "/!\\ `LevelFilter` representation seems to have changed! /!\\ \n\ + This is a bug (and it's pretty bad).\n \ + The offending repr was: {:?}", + unknown, + ), + #[cfg(not(debug_assertions))] + _ => unsafe { + // Using `unreachable_unchecked` here (rather than + // `unreachable!()`) is necessary to ensure that rustc generates + // an identity conversion from integer -> discriminant, rather + // than generating a lookup table. We want to ensure this + // function is a single `mov` instruction (on x86) if at all + // possible, because it is called *every* time a span/event + // callsite is hit; and it is (potentially) the only code in the + // hottest path for skipping a majority of callsites when level + // filtering is in use. + // + // safety: This branch is only truly unreachable if we guarantee + // that no values other than the possible enum discriminants + // will *ever* be present. The `AtomicUsize` is initialized to + // the `OFF` value. It is only set by the `set_max` function, + // which takes a `LevelFilter` as a parameter. This restricts + // the inputs to `set_max` to the set of valid discriminants. + // Therefore, **as long as `MAX_VALUE` is only ever set by + // `set_max`**, this is safe. + core::hint::unreachable_unchecked() + }, + } + } + + pub(crate) fn set_max(LevelFilter(level): LevelFilter) { + let val = match level { + Some(Level(level)) => level as usize, + None => Self::OFF_USIZE, + }; + + // using an AcqRel swap ensures an ordered relationship of writes to the + // max level. + MAX_LEVEL.swap(val, Ordering::AcqRel); + } +} + +impl fmt::Display for LevelFilter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + LevelFilter::OFF => f.pad("off"), + LevelFilter::ERROR => f.pad("error"), + LevelFilter::WARN => f.pad("warn"), + LevelFilter::INFO => f.pad("info"), + LevelFilter::DEBUG => f.pad("debug"), + LevelFilter::TRACE => f.pad("trace"), + } + } +} + +impl fmt::Debug for LevelFilter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + LevelFilter::OFF => f.pad("LevelFilter::OFF"), + LevelFilter::ERROR => f.pad("LevelFilter::ERROR"), + LevelFilter::WARN => f.pad("LevelFilter::WARN"), + LevelFilter::INFO => f.pad("LevelFilter::INFO"), + LevelFilter::DEBUG => f.pad("LevelFilter::DEBUG"), + LevelFilter::TRACE => f.pad("LevelFilter::TRACE"), + } + } +} + +impl str::FromStr for LevelFilter { + type Err = ParseLevelFilterError; + fn from_str(from: &str) -> Result { + from.parse::() + .ok() + .and_then(|num| match num { + 0 => Some(LevelFilter::OFF), + 1 => Some(LevelFilter::ERROR), + 2 => Some(LevelFilter::WARN), + 3 => Some(LevelFilter::INFO), + 4 => Some(LevelFilter::DEBUG), + 5 => Some(LevelFilter::TRACE), + _ => None, + }) + .or_else(|| match from { + "" => Some(LevelFilter::ERROR), + s if s.eq_ignore_ascii_case("error") => Some(LevelFilter::ERROR), + s if s.eq_ignore_ascii_case("warn") => Some(LevelFilter::WARN), + s if s.eq_ignore_ascii_case("info") => Some(LevelFilter::INFO), + s if s.eq_ignore_ascii_case("debug") => Some(LevelFilter::DEBUG), + s if s.eq_ignore_ascii_case("trace") => Some(LevelFilter::TRACE), + s if s.eq_ignore_ascii_case("off") => Some(LevelFilter::OFF), + _ => None, + }) + .ok_or(ParseLevelFilterError(())) + } +} + +impl fmt::Display for ParseLevelFilterError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad( + "error parsing level filter: expected one of \"off\", \"error\", \ + \"warn\", \"info\", \"debug\", \"trace\", or a number 0-5", + ) + } +} + +impl std::error::Error for ParseLevelFilterError {} + +impl PartialEq for Level { + #[inline(always)] + fn eq(&self, other: &LevelFilter) -> bool { + self.0 as usize == filter_as_usize(&other.0) + } +} + +impl PartialOrd for Level { + #[inline(always)] + fn partial_cmp(&self, other: &LevelFilter) -> Option { + Some(filter_as_usize(&other.0).cmp(&(self.0 as usize))) + } + + #[inline(always)] + fn lt(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) < (self.0 as usize) + } + + #[inline(always)] + fn le(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) <= (self.0 as usize) + } + + #[inline(always)] + fn gt(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) > (self.0 as usize) + } + + #[inline(always)] + fn ge(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) >= (self.0 as usize) + } +} + +impl PartialEq for LevelFilter { + #[inline(always)] + fn eq(&self, other: &Level) -> bool { + filter_as_usize(&self.0) == other.0 as usize + } +} + +impl PartialOrd for LevelFilter { + #[inline(always)] + fn partial_cmp(&self, other: &LevelFilter) -> Option { + Some(self.cmp(other)) + } + + #[inline(always)] + fn lt(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) < filter_as_usize(&self.0) + } + + #[inline(always)] + fn le(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) <= filter_as_usize(&self.0) + } + + #[inline(always)] + fn gt(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) > filter_as_usize(&self.0) + } + + #[inline(always)] + fn ge(&self, other: &LevelFilter) -> bool { + filter_as_usize(&other.0) >= filter_as_usize(&self.0) + } +} + +impl Ord for LevelFilter { + #[inline(always)] + fn cmp(&self, other: &Self) -> cmp::Ordering { + filter_as_usize(&other.0).cmp(&filter_as_usize(&self.0)) + } +} + +impl PartialOrd for LevelFilter { + #[inline(always)] + fn partial_cmp(&self, other: &Level) -> Option { + Some((other.0 as usize).cmp(&filter_as_usize(&self.0))) + } + + #[inline(always)] + fn lt(&self, other: &Level) -> bool { + (other.0 as usize) < filter_as_usize(&self.0) + } + + #[inline(always)] + fn le(&self, other: &Level) -> bool { + (other.0 as usize) <= filter_as_usize(&self.0) + } + + #[inline(always)] + fn gt(&self, other: &Level) -> bool { + (other.0 as usize) > filter_as_usize(&self.0) + } + + #[inline(always)] + fn ge(&self, other: &Level) -> bool { + (other.0 as usize) >= filter_as_usize(&self.0) + } +} + +#[inline(always)] +fn filter_as_usize(x: &Option) -> usize { + match x { + Some(Level(f)) => *f as usize, + None => LevelFilter::OFF_USIZE, + } +} + +pub use static_max_level::STATIC_MAX_LEVEL; + +mod static_max_level { + use super::LevelFilter; + + /// The statically configured maximum trace level. + /// + /// See the [module-level documentation] for information on how to configure + /// this. + /// + /// This value is checked by the `event!` and `span!` macros. Code that + /// manually constructs events or spans via the `Event::record` function or + /// `Span` constructors should compare the level against this value to + /// determine if those spans or events are enabled. + /// + /// [module-level documentation]: self#compile-time-filters + pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL; + + cfg_if::cfg_if! { + if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] { + const MAX_LEVEL: LevelFilter = LevelFilter::OFF; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] { + const MAX_LEVEL: LevelFilter = LevelFilter::ERROR; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] { + const MAX_LEVEL: LevelFilter = LevelFilter::WARN; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] { + const MAX_LEVEL: LevelFilter = LevelFilter::INFO; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] { + const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] { + const MAX_LEVEL: LevelFilter = LevelFilter::TRACE; + } else if #[cfg(feature = "max_level_off")] { + const MAX_LEVEL: LevelFilter = LevelFilter::OFF; + } else if #[cfg(feature = "max_level_error")] { + const MAX_LEVEL: LevelFilter = LevelFilter::ERROR; + } else if #[cfg(feature = "max_level_warn")] { + const MAX_LEVEL: LevelFilter = LevelFilter::WARN; + } else if #[cfg(feature = "max_level_info")] { + const MAX_LEVEL: LevelFilter = LevelFilter::INFO; + } else if #[cfg(feature = "max_level_debug")] { + const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; + } else { + const MAX_LEVEL: LevelFilter = LevelFilter::TRACE; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 28d327d..c0cb3e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ -//! A logging library for lunatic Rust applications. +//! A logging library for lunatic Rust applications, inspired by [tracing](https://crates.io/crates/tracing). //! -//! A [`Subscriber`] is initialized in a [`lunatic::Process`] with [`init`]. -//! Logs are emitted to the subscriber when the [`error`], [`warn`], [`info`], [`debug`], [`trace`] macros are used. +//! A global [`Subscriber`] is initialized in its own [`lunatic::Process`] with [`subscriber::init_subscriber`]. +//! Logs are emitted to the subscriber as an [`Event`](subscriber::Event) when the [`error`], [`warn`], [`info`], [`debug`], [`trace`] macros are used. //! //! # Example //! @@ -9,7 +9,7 @@ //! use lunatic_log::{info, subscriber::fmt::FmtSubscriber}; //! //! // Initialize subscriber -//! init(FmtSubscriber::new(LevelFilter::Info).pretty()); +//! FmtSubscriber::new(LevelFilter::Info).pretty().init(); //! //! // Log info message //! info!("Hello, {}", "world"); @@ -17,102 +17,11 @@ #![deny(missing_docs)] -mod level; -#[macro_use] -mod macros; -mod metadata; +pub mod level; +pub mod metadata; pub mod subscriber; -use std::cell::RefCell; - -use lunatic::{process_local, spawn_link, Process}; -use serde::{Deserialize, Serialize}; -use subscriber::Subscriber; - -pub use crate::level::*; -pub use crate::metadata::*; - -process_local! { - static LOGGING_PROCESS: RefCell = RefCell::new(LoggingProcess::NotLookedUp); -} - -enum LoggingProcess { - NotLookedUp, - NotPresent, - Present(Process), -} - -/// Initialize a subscriber to handle log events. -/// -/// The subscriber is spawned in a [`lunatic::Process`] and receives log events. -pub fn init(subscriber: impl Subscriber) -> Process { - if Process::::lookup("lunatic::logger").is_some() { - panic!("logger already initialized"); - } - - let process = spawn_subscriber(subscriber); - process.register("lunatic::logger"); - LOGGING_PROCESS.with_borrow_mut(|mut proc| *proc = LoggingProcess::Present(process.clone())); - process -} - -/// Spawn a subscriber process. -pub fn spawn_subscriber(subscriber: impl Subscriber) -> Process { - spawn_link!(|subscriber, mailbox: Mailbox| { - loop { - let event = mailbox.receive(); - if subscriber.enabled(event.metadata()) { - subscriber.event(&event); - } - } - }) -} - -/// An event to be logged by a subscriber, storing a message and metadata. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Event { - message: String, - metadata: Metadata, -} - -impl Event { - /// Creates a new event given a message and metadata. - pub const fn new(message: String, metadata: Metadata) -> Self { - Event { metadata, message } - } - - /// Returns the message string to be logged. - pub fn message(&self) -> &String { - &self.message - } - - /// Returns [metadata] describing this `Event`. - pub fn metadata(&self) -> &Metadata { - &self.metadata - } -} +#[macro_use] +mod macros; -// This is an internal function, and it's API is subject to change at any time. -#[doc(hidden)] -pub fn __lookup_logging_process() -> Option> { - LOGGING_PROCESS.with(|proc| { - let proc_ref = proc.borrow(); - match &*proc_ref { - LoggingProcess::NotLookedUp => { - std::mem::drop(proc_ref); - match Process::::lookup("lunatic::logger") { - Some(process) => { - *proc.borrow_mut() = LoggingProcess::Present(process.clone()); - Some(process) - } - None => { - *proc.borrow_mut() = LoggingProcess::NotPresent; - None - } - } - } - LoggingProcess::NotPresent => None, - LoggingProcess::Present(process) => Some(process.clone()), - } - }) -} +pub use level::{Level, LevelFilter}; diff --git a/src/macros.rs b/src/macros.rs index 70f850b..14d3acb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,157 +1,2007 @@ -/// Logs a message with a specified level. -/// -/// You should use [`error`], [`warn`], [`info`], [`debug`], [`trace`] macros instead. -#[macro_export] -macro_rules! log { - // log!(target: "my_target", Level::Info; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ - use lunatic::process::Message; - if let Some(proc) = $crate::__lookup_logging_process() { - let metadata = $crate::Metadata::new( - concat!( - "event ", - file!(), - ":", - line!() - ).to_string(), - $target.into(), - $lvl, - Some(module_path!().to_string()), - Some(file!().to_string()), - Some(line!()), - ); - let message = format!($($arg)+); - let event = $crate::Event::new(message, metadata); - proc.send(event) - } - }); +// /// Constructs a new span. +// /// +// /// See [the top-level documentation][lib] for details on the syntax accepted by +// /// this macro. +// /// +// /// [lib]: crate#using-the-macros +// /// +// /// # Examples +// /// +// /// Creating a new span: +// /// ``` +// /// # use tracing::{span, Level}; +// /// # fn main() { +// /// let span = span!(Level::TRACE, "my span"); +// /// let _enter = span.enter(); +// /// // do work inside the span... +// /// # } +// /// ``` +// #[macro_export] +// macro_rules! span { +// (target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => { +// $crate::span!(target: $target, parent: $parent, $lvl, $name,) +// }; +// (target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { +// { +// use $crate::__macro_support::Callsite as _; +// static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { +// name: $name, +// kind: $crate::metadata::Kind::SPAN, +// target: $target, +// level: $lvl, +// fields: $($fields)* +// }; +// let mut interest = $crate::collect::Interest::never(); +// if $crate::level_enabled!($lvl) +// && { interest = CALLSITE.interest(); !interest.is_never() } +// && CALLSITE.is_enabled(interest) +// { +// let meta = CALLSITE.metadata(); +// // span with explicit parent +// $crate::Span::child_of( +// $parent, +// meta, +// &$crate::valueset!(meta.fields(), $($fields)*), +// ) +// } else { +// let span = CALLSITE.disabled_span(); +// $crate::if_log_enabled! { $lvl, { +// span.record_all(&$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)); +// }}; +// span +// } +// } +// }; +// (target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { +// { +// use $crate::__macro_support::{Callsite as _, Registration}; +// static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { +// name: $name, +// kind: $crate::metadata::Kind::SPAN, +// target: $target, +// level: $lvl, +// fields: $($fields)* +// }; - // log!(Level::Info, "a log event") - ($lvl:expr, $($arg:tt)+) => ($crate::log!(target: module_path!(), $lvl, $($arg)+)); -} +// let mut interest = $crate::collect::Interest::never(); +// if $crate::level_enabled!($lvl) +// && { interest = CALLSITE.interest(); !interest.is_never() } +// && CALLSITE.is_enabled(interest) +// { +// let meta = CALLSITE.metadata(); +// // span with contextual parent +// $crate::Span::new( +// meta, +// &$crate::valueset!(meta.fields(), $($fields)*), +// ) +// } else { +// let span = CALLSITE.disabled_span(); +// $crate::if_log_enabled! { $lvl, { +// span.record_all(&$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)); +// }}; +// span +// } +// } +// }; +// (target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => { +// $crate::span!(target: $target, parent: $parent, $lvl, $name,) +// }; +// (parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $lvl, +// $name, +// $($fields)* +// ) +// }; +// (parent: $parent:expr, $lvl:expr, $name:expr) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $lvl, +// $name, +// ) +// }; +// (target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { +// $crate::span!( +// target: $target, +// $lvl, +// $name, +// $($fields)* +// ) +// }; +// (target: $target:expr, $lvl:expr, $name:expr) => { +// $crate::span!(target: $target, $lvl, $name,) +// }; +// ($lvl:expr, $name:expr, $($fields:tt)*) => { +// $crate::span!( +// target: module_path!(), +// $lvl, +// $name, +// $($fields)* +// ) +// }; +// ($lvl:expr, $name:expr) => { +// $crate::span!( +// target: module_path!(), +// $lvl, +// $name, +// ) +// }; +// } -/// Logs a message at the error level. +// /// Constructs a span at the trace level. +// /// +// /// [Fields] and [attributes] are set using the same syntax as the [`span!`] +// /// macro. +// /// +// /// See [the top-level documentation][lib] for details on the syntax accepted by +// /// this macro. +// /// +// /// [lib]: crate#using-the-macros +// /// [attributes]: crate#configuring-attributes +// /// [Fields]: crate#recording-fields +// /// [`span!`]: span! +// /// +// /// # Examples +// /// +// /// ```rust +// /// # use tracing::{trace_span, span, Level}; +// /// # fn main() { +// /// trace_span!("my_span"); +// /// // is equivalent to: +// /// span!(Level::TRACE, "my_span"); +// /// # } +// /// ``` +// /// +// /// ```rust +// /// # use tracing::{trace_span, span, Level}; +// /// # fn main() { +// /// let span = trace_span!("my span"); +// /// span.in_scope(|| { +// /// // do work inside the span... +// /// }); +// /// # } +// /// ``` +// #[macro_export] +// macro_rules! trace_span { +// (target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// parent: $parent, +// $crate::Level::TRACE, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, parent: $parent:expr, $name:expr) => { +// $crate::trace_span!(target: $target, parent: $parent, $name,) +// }; +// (parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $crate::Level::TRACE, +// $name, +// $($field)* +// ) +// }; +// (parent: $parent:expr, $name:expr) => { +// $crate::trace_span!(parent: $parent, $name,) +// }; +// (target: $target:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// $crate::Level::TRACE, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, $name:expr) => { +// $crate::trace_span!(target: $target, $name,) +// }; +// ($name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// $crate::Level::TRACE, +// $name, +// $($field)* +// ) +// }; +// ($name:expr) => { $crate::trace_span!($name,) }; +// } + +// /// Constructs a span at the debug level. +// /// +// /// [Fields] and [attributes] are set using the same syntax as the [`span!`] +// /// macro. +// /// +// /// See [the top-level documentation][lib] for details on the syntax accepted by +// /// this macro. +// /// +// /// [lib]: crate#using-the-macros +// /// [attributes]: crate#configuring-attributes +// /// [Fields]: crate#recording-fields +// /// [`span!`]: span! +// /// +// /// # Examples +// /// +// /// ```rust +// /// # use tracing::{debug_span, span, Level}; +// /// # fn main() { +// /// debug_span!("my_span"); +// /// // is equivalent to: +// /// span!(Level::DEBUG, "my_span"); +// /// # } +// /// ``` +// /// +// /// ```rust +// /// # use tracing::debug_span; +// /// # fn main() { +// /// let span = debug_span!("my span"); +// /// span.in_scope(|| { +// /// // do work inside the span... +// /// }); +// /// # } +// /// ``` +// #[macro_export] +// macro_rules! debug_span { +// (target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// parent: $parent, +// $crate::Level::DEBUG, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, parent: $parent:expr, $name:expr) => { +// $crate::debug_span!(target: $target, parent: $parent, $name,) +// }; +// (parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $crate::Level::DEBUG, +// $name, +// $($field)* +// ) +// }; +// (parent: $parent:expr, $name:expr) => { +// $crate::debug_span!(parent: $parent, $name,) +// }; +// (target: $target:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// $crate::Level::DEBUG, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, $name:expr) => { +// $crate::debug_span!(target: $target, $name,) +// }; +// ($name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// $crate::Level::DEBUG, +// $name, +// $($field)* +// ) +// }; +// ($name:expr) => {$crate::debug_span!($name,)}; +// } + +// /// Constructs a span at the info level. +// /// +// /// [Fields] and [attributes] are set using the same syntax as the [`span!`] +// /// macro. +// /// +// /// See [the top-level documentation][lib] for details on the syntax accepted by +// /// this macro. +// /// +// /// [lib]: crate#using-the-macros +// /// [attributes]: crate#configuring-attributes +// /// [Fields]: crate#recording-fields +// /// [`span!`]: span! +// /// +// /// # Examples +// /// +// /// ```rust +// /// # use tracing::{span, info_span, Level}; +// /// # fn main() { +// /// info_span!("my_span"); +// /// // is equivalent to: +// /// span!(Level::INFO, "my_span"); +// /// # } +// /// ``` +// /// +// /// ```rust +// /// # use tracing::info_span; +// /// # fn main() { +// /// let span = info_span!("my span"); +// /// span.in_scope(|| { +// /// // do work inside the span... +// /// }); +// /// # } +// /// ``` +// #[macro_export] +// macro_rules! info_span { +// (target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// parent: $parent, +// $crate::Level::INFO, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, parent: $parent:expr, $name:expr) => { +// $crate::info_span!(target: $target, parent: $parent, $name,) +// }; +// (parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $crate::Level::INFO, +// $name, +// $($field)* +// ) +// }; +// (parent: $parent:expr, $name:expr) => { +// $crate::info_span!(parent: $parent, $name,) +// }; +// (target: $target:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// $crate::Level::INFO, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, $name:expr) => { +// $crate::info_span!(target: $target, $name,) +// }; +// ($name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// $crate::Level::INFO, +// $name, +// $($field)* +// ) +// }; +// ($name:expr) => {$crate::info_span!($name,)}; +// } + +// /// Constructs a span at the warn level. +// /// +// /// [Fields] and [attributes] are set using the same syntax as the [`span!`] +// /// macro. +// /// +// /// See [the top-level documentation][lib] for details on the syntax accepted by +// /// this macro. +// /// +// /// [lib]: crate#using-the-macros +// /// [attributes]: crate#configuring-attributes +// /// [Fields]: crate#recording-fields +// /// [`span!`]: span! +// /// +// /// # Examples +// /// +// /// ```rust +// /// # use tracing::{warn_span, span, Level}; +// /// # fn main() { +// /// warn_span!("my_span"); +// /// // is equivalent to: +// /// span!(Level::WARN, "my_span"); +// /// # } +// /// ``` +// /// +// /// ```rust +// /// use tracing::warn_span; +// /// # fn main() { +// /// let span = warn_span!("my span"); +// /// span.in_scope(|| { +// /// // do work inside the span... +// /// }); +// /// # } +// /// ``` +// #[macro_export] +// macro_rules! warn_span { +// (target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// parent: $parent, +// $crate::Level::WARN, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, parent: $parent:expr, $name:expr) => { +// $crate::warn_span!(target: $target, parent: $parent, $name,) +// }; +// (parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $crate::Level::WARN, +// $name, +// $($field)* +// ) +// }; +// (parent: $parent:expr, $name:expr) => { +// $crate::warn_span!(parent: $parent, $name,) +// }; +// (target: $target:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// $crate::Level::WARN, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, $name:expr) => { +// $crate::warn_span!(target: $target, $name,) +// }; +// ($name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// $crate::Level::WARN, +// $name, +// $($field)* +// ) +// }; +// ($name:expr) => {$crate::warn_span!($name,)}; +// } +// /// Constructs a span at the error level. +// /// +// /// [Fields] and [attributes] are set using the same syntax as the [`span!`] +// /// macro. +// /// +// /// See [the top-level documentation][lib] for details on the syntax accepted by +// /// this macro. +// /// +// /// [lib]: crate#using-the-macros +// /// [attributes]: crate#configuring-attributes +// /// [Fields]: crate#recording-fields +// /// [`span!`]: span! +// /// +// /// # Examples +// /// +// /// ```rust +// /// # use tracing::{span, error_span, Level}; +// /// # fn main() { +// /// error_span!("my_span"); +// /// // is equivalent to: +// /// span!(Level::ERROR, "my_span"); +// /// # } +// /// ``` +// /// +// /// ```rust +// /// # use tracing::error_span; +// /// # fn main() { +// /// let span = error_span!("my span"); +// /// span.in_scope(|| { +// /// // do work inside the span... +// /// }); +// /// # } +// /// ``` +// #[macro_export] +// macro_rules! error_span { +// (target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// parent: $parent, +// $crate::Level::ERROR, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, parent: $parent:expr, $name:expr) => { +// $crate::error_span!(target: $target, parent: $parent, $name,) +// }; +// (parent: $parent:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// parent: $parent, +// $crate::Level::ERROR, +// $name, +// $($field)* +// ) +// }; +// (parent: $parent:expr, $name:expr) => { +// $crate::error_span!(parent: $parent, $name,) +// }; +// (target: $target:expr, $name:expr, $($field:tt)*) => { +// $crate::span!( +// target: $target, +// $crate::Level::ERROR, +// $name, +// $($field)* +// ) +// }; +// (target: $target:expr, $name:expr) => { +// $crate::error_span!(target: $target, $name,) +// }; +// ($name:expr, $($field:tt)*) => { +// $crate::span!( +// target: module_path!(), +// $crate::Level::ERROR, +// $name, +// $($field)* +// ) +// }; +// ($name:expr) => {$crate::error_span!($name,)}; +// } + +/// Constructs a new `Event`. +/// +/// The event macro is invoked with a `Level` and up to 32 key-value fields. +/// Optionally, a format string and arguments may follow the fields; this will +/// be used to construct an implicit field named "message". +/// +/// See [the top-level documentation][lib] for details on the syntax accepted by +/// this macro. +/// +/// [lib]: crate#using-the-macros /// /// # Examples /// -/// ``` -/// use lunatic_log::error; +/// ```rust +/// use tracing::{event, Level}; /// /// # fn main() { -/// let (err_info, port) = ("No connection", 22); +/// let data = (42, "forty-two"); +/// let private_data = "private"; +/// let error = "a bad error"; /// -/// error!("Error: {} on port {}", err_info, port); -/// error!(target: "app_events", "App Error: {}, Port: {}", err_info, 22); +/// event!(Level::ERROR, %error, "Received error"); +/// event!( +/// target: "app_events", +/// Level::WARN, +/// private_data, +/// ?data, +/// "App warning: {}", +/// error +/// ); +/// event!(Level::INFO, the_answer = data.0); /// # } /// ``` -#[macro_export(local_inner_macros)] -macro_rules! error { - // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // error!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Error, $($arg)+)); +/// +// /// Note that *unlike `span!`*, `event!` requires a value for all fields. As +// /// events are recorded immediately when the macro is invoked, there is no +// /// opportunity for fields to be recorded later. A trailing comma on the final +// /// field is valid. +// /// +// /// For example, the following does not compile: +// /// ```rust,compile_fail +// /// # use tracing::{Level, event}; +// /// # fn main() { +// /// event!(Level::INFO, foo = 5, bad_field, bar = "hello") +// /// #} +// /// ``` +#[macro_export] +macro_rules! event { + // (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + // use $crate::__macro_support::Callsite as _; + // static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + // name: concat!( + // "event ", + // file!(), + // ":", + // line!() + // ), + // kind: $crate::metadata::Kind::EVENT, + // target: $target, + // level: $lvl, + // fields: $($fields)* + // }; + + // let enabled = $crate::level_enabled!($lvl) && { + // let interest = CALLSITE.interest(); + // !interest.is_never() && CALLSITE.is_enabled(interest) + // }; + // if enabled { + // (|value_set: $crate::field::ValueSet| { + // $crate::__tracing_log!( + // $lvl, + // CALLSITE, + // &value_set + // ); + // let meta = CALLSITE.metadata(); + // // event with explicit parent + // $crate::Event::child_of( + // $parent, + // meta, + // &value_set + // ); + // })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)); + // } else { + // $crate::__tracing_log!( + // $lvl, + // CALLSITE, + // &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*) + // ); + // } + // }); - // error!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Error, $($arg)+)) + // (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: $target, + // parent: $parent, + // $lvl, + // { $($arg)+, $($fields)* } + // ) + // ); + // (target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + // $crate::event!(target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* }) + // ); + // (target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => ( + // $crate::event!(target: $target, parent: $parent, $lvl, { $($arg)+ }) + // ); + (target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ + if $crate::level_enabled!($lvl) { + let metadata = $crate::metadata!($lvl); + let (message, data) = $crate::valueset!(entry: $( $fields )*); + let event = $crate::subscriber::Event::new(message, data, metadata); + $crate::subscriber::dispatch(event); + } + }); + (target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: $target, + $lvl, + { $($arg)+, $($fields)* } + ) + ); + (target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => ( + $crate::event!(target: $target, $lvl, { $($k).+ = $($fields)* }) + ); + (target: $target:expr, $lvl:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $lvl, { $($arg)+ }) + ); + // (parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { $($arg)+, $($fields)* } + // ) + // ); + // (parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { $($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $lvl:expr, ?$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { ?$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $lvl:expr, %$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { %$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $lvl:expr, $($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { $($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $lvl:expr, %$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { %$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $lvl:expr, ?$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $lvl, + // { ?$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $lvl:expr, $($arg:tt)+ ) => ( + // $crate::event!(target: module_path!(), parent: $parent, $lvl, { $($arg)+ }) + // ); + ( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $lvl, + { $($arg)+, $($fields)* } + ) + ); + ($lvl:expr, $($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { $($k).+ = $($field)*} + ) + ); + ($lvl:expr, $($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { $($k).+, $($field)*} + ) + ); + ($lvl:expr, ?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { ?$($k).+, $($field)*} + ) + ); + ($lvl:expr, %$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $lvl, + { %$($k).+, $($field)*} + ) + ); + ($lvl:expr, ?$($k:ident).+) => ( + $crate::event!($lvl, ?$($k).+,) + ); + ($lvl:expr, %$($k:ident).+) => ( + $crate::event!($lvl, %$($k).+,) + ); + ($lvl:expr, $($k:ident).+) => ( + $crate::event!($lvl, $($k).+,) + ); + ( $lvl:expr, $($arg:tt)+ ) => ( + $crate::event!(target: module_path!(), $lvl, { $($arg)+ }) + ); } -/// Logs a message at the warn level. +/// Constructs an event at the trace level. /// -/// # Examples +/// This functions similarly to the [`event!`] macro. See [the top-level +/// documentation][lib] for details on the syntax accepted by +/// this macro. /// -/// ``` -/// use lunatic_log::warn; +/// [`event!`]: event! +/// [lib]: crate#using-the-macros +/// +/// # Examples /// +/// ```rust +/// use tracing::trace; +/// # #[derive(Debug, Copy, Clone)] struct Position { x: f32, y: f32 } +/// # impl Position { +/// # const ORIGIN: Self = Self { x: 0.0, y: 0.0 }; +/// # fn dist(&self, other: Position) -> f32 { +/// # let x = (other.x - self.x).exp2(); let y = (self.y - other.y).exp2(); +/// # (x + y).sqrt() +/// # } +/// # } /// # fn main() { -/// let warn_description = "Invalid Input"; +/// let pos = Position { x: 3.234, y: -1.223 }; +/// let origin_dist = pos.dist(Position::ORIGIN); /// -/// warn!("Warning! {}!", warn_description); -/// warn!(target: "input_events", "App received warning: {}", warn_description); +/// trace!(position = ?pos, ?origin_dist); +/// trace!( +/// target: "app_events", +/// position = ?pos, +/// "x is {} and y is {}", +/// if pos.x >= 0.0 { "positive" } else { "negative" }, +/// if pos.y >= 0.0 { "positive" } else { "negative" } +/// ); /// # } /// ``` -#[macro_export(local_inner_macros)] -macro_rules! warn { - // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // warn!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Warn, $($arg)+)); - - // warn!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Warn, $($arg)+)) +#[macro_export] +macro_rules! trace { + // (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*) + // ); + // (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+) + // ); + // (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { $($field)+ }, + // $($arg)+ + // ) + // ); + // (parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { $($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { ?$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { %$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { $($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { ?$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // { %$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $($arg:tt)+) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::TRACE, + // {}, + // $($arg)+ + // ) + // ); + (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*) + ); + (target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { $($k).+ $($field)* }) + ); + (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* }) + ); + (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* }) + ); + (target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::TRACE, {}, $($arg)+) + ); + ({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($field)+ }, + $($arg)+ + ) + ); + ($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($k).+ = $($field)*} + ) + ); + ($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($k).+, $($field)*} + ) + ); + (?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { ?$($k).+, $($field)*} + ) + ); + (%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { %$($k).+, $($field)*} + ) + ); + (?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { ?$($k).+ } + ) + ); + (%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { %$($k).+ } + ) + ); + ($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + { $($k).+ } + ) + ); + ($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::TRACE, + {}, + $($arg)+ + ) + ); } -/// Logs a message at the info level. +/// Constructs an event at the debug level. +/// +/// This functions similarly to the [`event!`] macro. See [the top-level +/// documentation][lib] for details on the syntax accepted by +/// this macro. +/// +/// [`event!`]: event! +/// [lib]: crate#using-the-macros /// /// # Examples /// +/// ```rust +/// use tracing::debug; +/// # fn main() { +/// # #[derive(Debug)] struct Position { x: f32, y: f32 } +/// +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// debug!(?pos.x, ?pos.y); +/// debug!(target: "app_events", position = ?pos, "New position"); +/// # } /// ``` -/// use lunatic_log::info; +#[macro_export] +macro_rules! debug { + // (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*) + // ); + // (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+) + // ); + // (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { $($field)+ }, + // $($arg)+ + // ) + // ); + // (parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { $($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { ?$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { %$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { $($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { ?$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // { %$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $($arg:tt)+) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::DEBUG, + // {}, + // $($arg)+ + // ) + // ); + (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*) + ); + (target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* }) + ); + (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* }) + ); + (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* }) + ); + (target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::DEBUG, {}, $($arg)+) + ); + ({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($field)+ }, + $($arg)+ + ) + ); + ($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($k).+ = $($field)*} + ) + ); + (?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { ?$($k).+ = $($field)*} + ) + ); + (%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { %$($k).+ = $($field)*} + ) + ); + ($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($k).+, $($field)*} + ) + ); + (?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { ?$($k).+, $($field)*} + ) + ); + (%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { %$($k).+, $($field)*} + ) + ); + (?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { ?$($k).+ } + ) + ); + (%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { %$($k).+ } + ) + ); + ($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + { $($k).+ } + ) + ); + ($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::DEBUG, + {}, + $($arg)+ + ) + ); +} + +/// Constructs an event at the info level. /// +/// This functions similarly to the [`event!`] macro. See [the top-level +/// documentation][lib] for details on the syntax accepted by +/// this macro. +/// +/// [`event!`]: event! +/// [lib]: crate#using-the-macros +/// +/// # Examples +/// +/// ```rust +/// use tracing::info; +/// # // this is so the test will still work in no-std mode +/// # #[derive(Debug)] +/// # pub struct Ipv4Addr; +/// # impl Ipv4Addr { fn new(o1: u8, o2: u8, o3: u8, o4: u8) -> Self { Self } } /// # fn main() { /// # struct Connection { port: u32, speed: f32 } -/// let conn_info = Connection { port: 40, speed: 3.20 }; +/// use tracing::field; +/// +/// let addr = Ipv4Addr::new(127, 0, 0, 1); +/// let conn = Connection { port: 40, speed: 3.20 }; /// -/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); -/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", -/// conn_info.port, conn_info.speed); +/// info!(conn.port, "connected to {:?}", addr); +/// info!( +/// target: "connection_events", +/// ip = ?addr, +/// conn.port, +/// ?conn.speed, +/// ); /// # } /// ``` -#[macro_export(local_inner_macros)] +#[macro_export] macro_rules! info { - // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // info!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Info, $($arg)+)); - - // info!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Info, $($arg)+)) + // (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*) + // ); + // (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+) + // ); + // (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { $($field)+ }, + // $($arg)+ + // ) + // ); + // (parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { $($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { ?$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { %$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { $($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { ?$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // { %$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $($arg:tt)+) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::INFO, + // {}, + // $($arg)+ + // ) + // ); + (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*) + ); + (target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* }) + ); + (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* }) + ); + (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* }) + ); + (target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::INFO, {}, $($arg)+) + ); + ({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($field)+ }, + $($arg)+ + ) + ); + ($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($k).+ = $($field)*} + ) + ); + (?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { ?$($k).+ = $($field)*} + ) + ); + (%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { %$($k).+ = $($field)*} + ) + ); + ($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($k).+, $($field)*} + ) + ); + (?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { ?$($k).+, $($field)*} + ) + ); + (%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { %$($k).+, $($field)*} + ) + ); + (?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { ?$($k).+ } + ) + ); + (%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { %$($k).+ } + ) + ); + ($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + { $($k).+ } + ) + ); + ($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::INFO, + {}, + $($arg)+ + ) + ); } -/// Logs a message at the debug level. +/// Constructs an event at the warn level. /// -/// # Examples +/// This functions similarly to the [`event!`] macro. See [the top-level +/// documentation][lib] for details on the syntax accepted by +/// this macro. /// -/// ``` -/// use lunatic_log::debug; +/// [`event!`]: event! +/// [lib]: crate#using-the-macros +/// +/// # Examples /// +/// ```rust +/// use tracing::warn; /// # fn main() { -/// # struct Position { x: f32, y: f32 } -/// let pos = Position { x: 3.234, y: -1.223 }; /// -/// debug!("New position: x: {}, y: {}", pos.x, pos.y); -/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); +/// let warn_description = "Invalid Input"; +/// let input = &[0x27, 0x45]; +/// +/// warn!(?input, warning = warn_description); +/// warn!( +/// target: "input_events", +/// warning = warn_description, +/// "Received warning for input: {:?}", input, +/// ); /// # } /// ``` -#[macro_export(local_inner_macros)] -macro_rules! debug { - // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // debug!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Debug, $($arg)+)); - - // debug!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Debug, $($arg)+)) +#[macro_export] +macro_rules! warn { + // (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*) + // ); + // (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+) + // ); + // (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { $($field)+ }, + // $($arg)+ + // ) + // ); + // (parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { $($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { ?$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { %$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { $($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { ?$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // { %$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $($arg:tt)+) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::WARN, + // {}, + // $($arg)+ + // ) + // ); + (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*) + ); + (target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { $($k).+ $($field)* }) + ); + (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* }) + ); + (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::WARN, { %$($k).+ $($field)* }) + ); + (target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::WARN, {}, $($arg)+) + ); + ({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($field)+ }, + $($arg)+ + ) + ); + ($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($k).+ = $($field)*} + ) + ); + (?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { ?$($k).+ = $($field)*} + ) + ); + (%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { %$($k).+ = $($field)*} + ) + ); + ($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($k).+, $($field)*} + ) + ); + (?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { ?$($k).+, $($field)*} + ) + ); + (%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { %$($k).+, $($field)*} + ) + ); + (?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { ?$($k).+ } + ) + ); + (%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { %$($k).+ } + ) + ); + ($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + { $($k).+ } + ) + ); + ($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::WARN, + {}, + $($arg)+ + ) + ); } -/// Logs a message at the trace level. +/// Constructs an event at the error level. /// -/// # Examples +/// This functions similarly to the [`event!`] macro. See [the top-level +/// documentation][lib] for details on the syntax accepted by +/// this macro. /// -/// ``` -/// use lunatic_log::trace; +/// [`event!`]: event! +/// [lib]: crate#using-the-macros +/// +/// # Examples /// +/// ```rust +/// use tracing::error; /// # fn main() { -/// # struct Position { x: f32, y: f32 } -/// let pos = Position { x: 3.234, y: -1.223 }; /// -/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); -/// trace!(target: "app_events", "x is {} and y is {}", -/// if pos.x >= 0.0 { "positive" } else { "negative" }, -/// if pos.y >= 0.0 { "positive" } else { "negative" }); +/// let (err_info, port) = ("No connection", 22); +/// +/// error!(port, error = %err_info); +/// error!(target: "app_events", "App Error: {}", err_info); +/// error!({ info = err_info }, "error on port: {}", port); /// # } /// ``` -#[macro_export(local_inner_macros)] -macro_rules! trace { - // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // trace!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Trace, $($arg)+)); +#[macro_export] +macro_rules! error { + // (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*) + // ); + // (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ }) + // ); + // (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => ( + // $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+) + // ); + // (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { $($field)+ }, + // $($arg)+ + // ) + // ); + // (parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { $($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { ?$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { %$($k).+ = $($field)*} + // ) + // ); + // (parent: $parent:expr, $($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { $($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { ?$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // { %$($k).+, $($field)*} + // ) + // ); + // (parent: $parent:expr, $($arg:tt)+) => ( + // $crate::event!( + // target: module_path!(), + // parent: $parent, + // $crate::Level::ERROR, + // {}, + // $($arg)+ + // ) + // ); + (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*) + ); + (target: $target:expr, $($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { $($k).+ $($field)* }) + ); + (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* }) + ); + (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* }) + ); + (target: $target:expr, $($arg:tt)+ ) => ( + $crate::event!(target: $target, $crate::Level::ERROR, {}, $($arg)+) + ); + ({ $($field:tt)+ }, $($arg:tt)+ ) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($field)+ }, + $($arg)+ + ) + ); + ($($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($k).+ = $($field)*} + ) + ); + (?$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { ?$($k).+ = $($field)*} + ) + ); + (%$($k:ident).+ = $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { %$($k).+ = $($field)*} + ) + ); + ($($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($k).+, $($field)*} + ) + ); + (?$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { ?$($k).+, $($field)*} + ) + ); + (%$($k:ident).+, $($field:tt)*) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { %$($k).+, $($field)*} + ) + ); + (?$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { ?$($k).+ } + ) + ); + (%$($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { %$($k).+ } + ) + ); + ($($k:ident).+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + { $($k).+ } + ) + ); + ($($arg:tt)+) => ( + $crate::event!( + target: module_path!(), + $crate::Level::ERROR, + {}, + $($arg)+ + ) + ); +} + +#[doc(hidden)] +#[macro_export] +macro_rules! valueset { + + // === base case === + (@ { $(,)* $($val:expr),* $(,)* }, $message:ident, $next:expr $(,)*) => { + [ $($val),* ].into_iter().collect() + }; + + // === recursive case (more tts) === - // trace!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Trace, $($arg)+)) + // TODO(#1138): determine a new syntax for uninitialized span fields, and + // re-enable this. + // (@{ $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = _, $($rest:tt)*) => { + // $crate::valueset!($message:ident, @ { $($out),*, (&$next, None) }, $next, $($rest)*) + // }; + // foo = ?bar ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{:?}", $val).into()) }, + $message, + $next, + $($rest)* + ) + }; + // foo = %bar ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{}", $val).into()) }, + $message, + $next, + $($rest)* + ) + }; + // foo = bar ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), serde_json::to_value(&$val).unwrap()) }, + $message, + $next, + $($rest)* + ) + }; + // foo ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), serde_json::to_value(&$($k).+).unwrap()) }, + $message, + $next, + $($rest)* + ) + }; + // ?foo ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, ?$($k:ident).+, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{:?}", $($k).+).into()) }, + $message, + $next, + $($rest)* + ) + }; + // %foo ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, %$($k:ident).+, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{}", $($k).+).into()) }, + $message, + $next, + $($rest)* + ) + }; + // foo = ?bar + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+ = ?$val:expr) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{:?}", $val).into()) }, + $message, + $next, + ) + }; + // foo = %bar + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+ = %$val:expr) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{}", $val).into()) }, + $message, + $next, + ) + }; + // foo = bar + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+ = $val:expr) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), serde_json::to_value(&$val).unwrap()) }, + $message, + $next, + ) + }; + // foo + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($k:ident).+) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), serde_json::to_value(&$($k).+).unwrap()) }, + $message, + $next, + ) + }; + // ?foo + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, ?$($k:ident).+) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{:?}", $($k).+).into()) }, + $message, + $next, + ) + }; + // %foo + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, %$($k:ident).+) => { + $crate::valueset!( + @ { $($out),*, (stringify!($($k).+).to_string(), format!("{}", $($k).+).into()) }, + $message, + $next, + ) + }; + + // Handle literal names + // "foo" = ?bar ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, ($k.to_string(), format!("{:?}", $val).into()) }, + $message, + $next, + $($rest)* + ) + }; + // "foo" = %bar ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, ($k.to_string(), format!("{}", $val).into()) }, + $message, + $next, + $($rest)* + ) + }; + // "foo" = bar ... + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => { + $crate::valueset!( + @ { $($out),*, ($k.to_string(), serde_json::to_value(&$val).unwrap()) }, + $message, + $next, + $($rest)* + ) + }; + // "foo" = ?bar + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $k:literal = ?$val:expr) => { + $crate::valueset!( + @ { $($out),*, ($k.to_string(), format!("{:?}", $val).into()) }, + $message, + $next, + ) + }; + // "foo" = %bar + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $k:literal = %$val:expr) => { + $crate::valueset!( + @ { $($out),*, ($k.to_string(), format!("{}", $val).into()) }, + $message, + $next, + ) + }; + // "foo" = bar + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $k:literal = $val:expr) => { + $crate::valueset!( + @ { $($out),*, ($k.to_string(), serde_json::to_value(&$val).unwrap()) }, + $message, + $next, + ) + }; + + // Remainder is unparseable, but exists --- must be format args! + (@ { $(,)* $($out:expr),* }, $message:ident, $next:expr, $($rest:tt)+) => {{ + $message = format!($($rest)+); + $crate::valueset!( + @ { $($out),* }, + $message, + $next, + ) + }}; + + // === entry === + (entry: $($kvs:tt)+) => { + { + extern crate serde_json; + extern crate std; + + use std::convert::Into; + use std::format; + use std::iter::{Iterator, IntoIterator}; + use std::string::{ToString, String}; + + let mut message = String::new(); + let data: serde_json::Map = $crate::valueset!( + @ { }, + message, + (), + $($kvs)+ + ); + (message, data) + } + }; + // () => { + // { + // $fields.value_set(&[]) + // } + // }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! level_enabled { + ($lvl:expr) => { + $lvl <= $crate::level::STATIC_MAX_LEVEL && $lvl <= $crate::level::LevelFilter::current() + }; } diff --git a/src/metadata.rs b/src/metadata.rs index 3704a91..f59e6bc 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,106 +1,99 @@ +//! Metadata containing extra information for each event. + use serde::{Deserialize, Serialize}; use crate::level::Level; -/// Metadata describing an [Event](super::Event). -/// -/// All events have the following metadata: -/// - A [name], represented as a static string. -/// - A [target], a string that categorizes part of the system where the event -/// or event occurred. The macros default to using the module -/// path where the event or event originated as the target, but it may be -/// overridden. -/// - A [verbosity level]. This determines how verbose a given event -/// is, and allows enabling or disabling more verbose diagnostics -/// situationally. See the documentation for the [`Level`] type for details. -/// - The names of the [fields] defined by the event. -/// - Whether the metadata corresponds to a event. -/// -/// In addition, the following optional metadata describing the source code -/// location where the event originated _may_ be provided: -/// - The [file name] -/// - The [line number] -/// - The [module path] -#[derive(Clone, Debug, Serialize, Deserialize)] +/// Metadata containing extra information for each event including the log level, the module path, +/// file and line number, and node id & process id that the event was created from. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Metadata { - /// The name of the event described by this metadata. - name: String, + /// The level of verbosity of the described span. + level: Level, - /// The part of the system that the event that this metadata describes - /// occurred in. - target: String, + /// The name of the Rust module where the span occurred. + module_path: String, - /// The level of verbosity of the described event. - level: Level, + /// The name of the source code file where the span occurred. + file: String, - /// The name of the Rust module where the event occurred, or `None` if this - /// could not be determined. - module_path: Option, + /// The line number in the source code file where the span occurred. + line: u32, - /// The name of the source code file where the event occurred, or `None` if - /// this could not be determined. - file: Option, + /// The node ID where the span occurred. + node_id: u64, - /// The line number in the source code file where the event occurred, or - /// `None` if this could not be determined. - line: Option, + /// The process ID where the span occurred. + process_id: u64, } impl Metadata { - /// Construct new metadata for a event, with a name, target, level, field - /// names, and optional source code location. + /// Creates a new instance of metadata. pub const fn new( - name: String, - target: String, level: Level, - module_path: Option, - file: Option, - line: Option, + module_path: String, + file: String, + line: u32, + node_id: u64, + process_id: u64, ) -> Self { Metadata { - name, - target, level, module_path, file, line, + node_id, + process_id, } } - /// Returns the level of verbosity of the described span or event. - pub fn level(&self) -> &Level { - &self.level + /// The level of verbosity of the described span. + pub fn level(&self) -> Level { + self.level } - /// Returns the name of the span. - pub fn name(&self) -> &String { - &self.name + /// The name of the Rust module where the span occurred. + pub fn module_path(&self) -> &str { + &self.module_path } - /// Returns a string describing the part of the system where the span or - /// event that this metadata describes occurred. - /// - /// Typically, this is the module path, but alternate targets may be set - /// when spans or events are constructed. - pub fn target(&self) -> &String { - &self.target + /// The name of the source code file where the span occurred. + pub fn file(&self) -> &str { + &self.file } - /// Returns the path to the Rust module where the span occurred, or - /// `None` if the module path is unknown. - pub fn module_path(&self) -> Option<&String> { - self.module_path.as_ref() + /// The line number in the source code file where the span occurred. + pub fn line(&self) -> u32 { + self.line } - /// Returns the name of the source code file where the span - /// occurred, or `None` if the file is unknown - pub fn file(&self) -> Option<&String> { - self.file.as_ref() + /// The node ID where the span occurred. + pub fn node_id(&self) -> u64 { + self.node_id } - /// Returns the line number in the source code file where the span - /// occurred, or `None` if the line number is unknown. - pub fn line(&self) -> Option { - self.line + /// The process ID where the span occurred. + pub fn process_id(&self) -> u64 { + self.process_id } } + +/// Creates an instance of metadata using the current context to populate the fields. +#[macro_export] +macro_rules! metadata { + ($level:expr) => {{ + extern crate lunatic; + + use std::string::ToString; + + let this_process: lunatic::Process<()> = lunatic::Process::this(); + $crate::metadata::Metadata::new( + $level, + module_path!().to_string(), + file!().to_string(), + line!(), + this_process.node_id(), + this_process.id(), + ) + }}; +} diff --git a/src/subscriber.rs b/src/subscriber.rs index 718fe01..434ee62 100644 --- a/src/subscriber.rs +++ b/src/subscriber.rs @@ -1,14 +1,73 @@ //! A [`Subscriber`] handles log events. //! //! It can be used to print to stdout with [`FmtSubscriber`](fmt::FmtSubscriber), -//! but is also capable of handling logs in other ways. +//! but is also capable of handling logs in other ways such as persisting to a file. +#[cfg(feature = "fmt")] pub mod fmt; pub mod multiple; -use serde::{de::DeserializeOwned, Serialize}; +use std::error; -use crate::{Event, Metadata}; +use lunatic::{function::FuncRef, serializer, spawn, Process}; +use lunatic_cached_process::{cached_process, CachedLookup}; +use lunatic_message_request::MessageRequest; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; + +use crate::{level::LevelFilter, metadata::Metadata}; + +const SUBSCRIBER_NAME: &str = "lunatic-tracing-subscriber"; + +cached_process! { + pub(crate) static SUBSCRIBER: Process = SUBSCRIBER_NAME; +} + +/// Type alias for a `Process`. +pub type SubscriberProcess = Process; + +/// The message type for subscriber processes. +#[derive(Serialize, Deserialize)] +pub enum SubscriberMessage { + /// An event dispatched. + Event(Event), + /// A request for the current max level hint. + MaxLevelHint(MessageRequest<(), Option>), +} + +/// An event containing a message, data, and metadata. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Event { + message: String, + data: Map, + metadata: Metadata, +} + +impl Event { + /// Creates a new event given a message and metadata. + pub const fn new(message: String, data: Map, metadata: Metadata) -> Self { + Event { + message, + data, + metadata, + } + } + + /// Returns the message string to be logged. + pub fn message(&self) -> &str { + &self.message + } + + /// Returns the data. + pub fn data(&self) -> &Map { + &self.data + } + + /// Returns [metadata] describing this `Event`. + pub fn metadata(&self) -> &Metadata { + &self.metadata + } +} /// A subscriber which handles incoming log [`Event`]s. /// @@ -30,10 +89,119 @@ use crate::{Event, Metadata}; /// } /// } /// ``` -pub trait Subscriber: Serialize + DeserializeOwned { +pub trait Subscriber { /// Indicate whether subscriber is enabled given some [`Metadata`]. fn enabled(&self, metadata: &Metadata) -> bool; /// Handle a log [`Event`]. fn event(&self, event: &Event); + + /// Returns the highest [verbosity level][level] that this `Subscriber` will + /// enable, or `None`, if the subscriber does not implement level-based + /// filtering or chooses not to implement this method. + /// + /// If this method returns a [`Level`][level], it will be used as a hint to + /// determine the most verbose level that will be enabled. This will allow + /// spans and events which are more verbose than that level to be skipped + /// more efficiently. Subscribers which perform filtering are strongly + /// encouraged to provide an implementation of this method. + fn max_level_hint(&self) -> Option { + None + } +} + +/// An error indicating a global subscriber has already been spawned. +#[derive(Debug)] +pub struct SubscriberAlreadyExistsError; + +impl std::fmt::Display for SubscriberAlreadyExistsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "subscriber already exists") + } +} + +impl error::Error for SubscriberAlreadyExistsError {} + +/// Initializes a global subscriber in its own process. +/// +/// Only one global subscriber may exist, and calling this function multiple times will return an error. +pub fn init_subscriber(subscriber: S) -> Result +where + S: Subscriber + Serialize + for<'de> Deserialize<'de>, +{ + if SUBSCRIBER.get().is_some() { + return Err(SubscriberAlreadyExistsError); + } + let process = spawn_subscriber(subscriber); + process.register(SUBSCRIBER_NAME); + SUBSCRIBER.set(process); + Ok(process) +} + +/// Initializes a global subscriber in its own process using a subscriber factory function. +/// This is useful if a subscriber is not serializable. +/// +/// Only one global subscriber may exist, and calling this function multiple times will return an error. +pub fn init_subscriber_fn( + subscriber: fn() -> S, +) -> Result +where + S: Subscriber, +{ + if SUBSCRIBER.get().is_some() { + return Err(SubscriberAlreadyExistsError); + } + let process = spawn_subscriber_fn(subscriber); + process.register(SUBSCRIBER_NAME); + SUBSCRIBER.set(process); + Ok(process) +} + +/// Spawns a subscriber in its own process from a serializable subscriber. +pub fn spawn_subscriber(subscriber: S) -> SubscriberProcess +where + S: Subscriber + Serialize + for<'de> Deserialize<'de>, +{ + spawn!( + |subscriber, mailbox: Mailbox| { + loop { + handle_message(&subscriber, mailbox.receive()); + } + } + ) +} + +/// Spawns a subscriber in its own process from a subscriber factory function. +/// This is useful if a subscriber is not serializable. +pub fn spawn_subscriber_fn(subscriber: fn() -> S) -> SubscriberProcess +where + S: Subscriber, +{ + let subscriber_fn = FuncRef::new(subscriber); + spawn!( + |subscriber_fn, mailbox: Mailbox| { + let subscriber = subscriber_fn(); + loop { + handle_message(&subscriber, mailbox.receive()); + } + } + ) +} + +/// Dispatches an event to the global subscriber, if present. +pub fn dispatch(event: Event) { + if let Some(subscriber) = SUBSCRIBER.get() { + subscriber.send(SubscriberMessage::Event(event)); + } +} + +fn handle_message(subscriber: &impl Subscriber, message: SubscriberMessage) { + match message { + SubscriberMessage::Event(event) => { + if subscriber.enabled(&event.metadata) { + subscriber.event(&event); + } + } + SubscriberMessage::MaxLevelHint(req) => req.reply(subscriber.max_level_hint()), + } } diff --git a/src/subscriber/fmt.rs b/src/subscriber/fmt.rs index ed70d69..2662f33 100644 --- a/src/subscriber/fmt.rs +++ b/src/subscriber/fmt.rs @@ -4,15 +4,19 @@ use std::fmt::Write; +use ansi_term::{Color, Style}; use chrono::Utc; use serde::{Deserialize, Serialize}; -use yansi::Color; +use serde_json::{Map, Number, Value}; -use crate::{level::LevelFilter, Event, Level, Metadata}; +use crate::{ + level::{Level, LevelFilter}, + metadata::Metadata, +}; -use super::Subscriber; +use super::{init_subscriber, Event, Subscriber, SubscriberAlreadyExistsError, SubscriberProcess}; -const GRAY: Color = Color::Black; +const GRAY: Color = Color::RGB(135, 135, 135); /// A subscriber printing to stdout/stderr. /// @@ -45,7 +49,7 @@ impl Default for FmtSubscriber { color: false, file: false, level: false, - level_filter: LevelFilter::Off, + level_filter: LevelFilter::OFF, line_number: false, target: false, time: false, @@ -112,16 +116,101 @@ impl FmtSubscriber { /// Customize the time format. /// - /// This must be in the `strftime` format supported by [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html). + /// This must be in the [`strftime`](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format supported by [chrono](https://docs.rs/chrono). pub fn with_time_format(mut self, time_format: impl Into) -> Self { self.time_format = Some(time_format.into()); self } + + /// Initializes as the global subscriber. + /// + /// Note, this will cause a panic if a global subscriber has already been initialized. + /// Use the [`try_init`] to handle this error. + pub fn init(self) { + self.try_init().unwrap(); + } + + /// Initializes as the global subscriber, returning an error if a global subscriber has already been initialized. + pub fn try_init(self) -> Result { + init_subscriber(self) + } + + fn write_data(&self, line: &mut String, data: &Map) { + for (i, (k, v)) in data.iter().enumerate() { + if i > 0 { + write!(line, " ").unwrap(); + } + write!(line, "{}=", Style::new().italic().paint(k)).unwrap(); + self.write_value(line, v); + } + } + + fn write_value(&self, line: &mut String, value: &Value) { + match value { + Value::Null => self.write_null(line), + Value::Bool(b) => self.write_bool(line, *b), + Value::Number(n) => self.write_number(line, n), + Value::String(s) => self.write_string(line, s), + Value::Array(a) => self.write_array(line, a), + Value::Object(o) => self.write_object(line, o), + } + } + + fn write_null(&self, line: &mut String) { + if self.color { + write!(line, "{}", GRAY.paint("null")).unwrap(); + } else { + write!(line, "null").unwrap(); + } + } + + fn write_bool(&self, line: &mut String, b: bool) { + if self.color { + if b { + write!(line, "{}", Color::Green.paint("true")).unwrap(); + } else { + write!(line, "{}", Color::Red.paint("false")).unwrap(); + } + } else { + write!(line, "{b}").unwrap(); + } + } + + fn write_number(&self, line: &mut String, n: &Number) { + write!(line, "{n}").unwrap(); + } + + fn write_string(&self, line: &mut String, s: &str) { + write!(line, r#""{s}""#).unwrap(); + } + + fn write_array(&self, line: &mut String, values: &[Value]) { + write!(line, "[ ").unwrap(); + for (i, value) in values.iter().enumerate() { + if i > 0 { + write!(line, ", ").unwrap(); + } + self.write_value(line, value); + } + write!(line, " ]").unwrap(); + } + + fn write_object(&self, line: &mut String, object: &Map) { + write!(line, "{{ ").unwrap(); + for (i, (k, v)) in object.iter().enumerate() { + if i > 0 { + write!(line, ", ").unwrap(); + } + write!(line, "{k}: ").unwrap(); + self.write_value(line, v); + } + write!(line, " }}").unwrap(); + } } impl Subscriber for FmtSubscriber { fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= &self.level_filter + metadata.level() <= self.level_filter } fn event(&self, event: &Event) { @@ -155,11 +244,11 @@ impl Subscriber for FmtSubscriber { insert_space!(); if self.color { let level_string = match event.metadata().level() { - Level::Error => Color::Red, - Level::Warn => Color::Yellow, - Level::Info => Color::Green, - Level::Debug => Color::Blue, - Level::Trace => Color::Magenta, + Level::ERROR => Color::Red, + Level::WARN => Color::Yellow, + Level::INFO => Color::Green, + Level::DEBUG => Color::Blue, + Level::TRACE => Color::Purple, } .paint(event.metadata().level().as_str()); for _ in 0..(5 - event.metadata().level().as_str().len()) { @@ -181,44 +270,58 @@ impl Subscriber for FmtSubscriber { write!( line, "{}", - GRAY.paint(format!("{}:", event.metadata().target())) + GRAY.paint(format!("{}:", event.metadata().module_path())) ) .unwrap(); } else { - write!(line, "{}:", event.metadata().target()).unwrap(); + write!(line, "{}:", event.metadata().module_path()).unwrap(); } } if self.file { - if let Some(file) = event.metadata().file() { - insert_space!(); - if self.color { - write!(line, "{}", GRAY.paint(format!("{file}:"))).unwrap(); - } else { - write!(line, "{}:", file).unwrap(); - } + insert_space!(); + if self.color { + write!( + line, + "{}", + GRAY.paint(format!("{}:", event.metadata().file())) + ) + .unwrap(); + } else { + write!(line, "{}:", self.file).unwrap(); } } if self.line_number { - if let Some(line_number) = event.metadata().line() { - if self.file && event.metadata().file().is_some() { - insert_space!(); - } - if self.color { - write!(line, "{}", GRAY.paint(format!("{line_number}:"))).unwrap(); - } else { - write!(line, "{}:", line_number).unwrap(); - } + if self.color { + write!( + line, + "{}", + GRAY.paint(format!("{}:", event.metadata().line())) + ) + .unwrap(); + } else { + write!(line, "{}:", event.metadata().line()).unwrap(); } } - insert_space!(); + if !event.data().is_empty() { + insert_space!(); + self.write_data(&mut line, event.data()); + } - if event.metadata().level() == &Level::Error { + if !event.message().is_empty() { + insert_space!() + } + + if event.metadata().level() == Level::ERROR { eprintln!("{line}{}", event.message()); } else { println!("{line}{}", event.message()); } } + + fn max_level_hint(&self) -> Option { + Some(self.level_filter) + } } diff --git a/src/subscriber/multiple.rs b/src/subscriber/multiple.rs index f9970ba..d0e647e 100644 --- a/src/subscriber/multiple.rs +++ b/src/subscriber/multiple.rs @@ -1,18 +1,23 @@ //! Combine multiple subscribers. -use lunatic::Process; use serde::{Deserialize, Serialize}; -use crate::{spawn_subscriber, Event, Metadata}; +use crate::{ + metadata::Metadata, + subscriber::{spawn_subscriber, Event}, +}; -use super::Subscriber; +use super::{ + init_subscriber, spawn_subscriber_fn, Subscriber, SubscriberAlreadyExistsError, + SubscriberMessage, SubscriberProcess, +}; /// Combines multiple subscribers into a single subscriber. /// /// Child subscriber processes are spawned, and each one is notified of incoming events. #[derive(Default, Serialize, Deserialize)] pub struct MultipleSubscribers { - subscribers: Vec>, + subscribers: Vec, } impl MultipleSubscribers { @@ -22,11 +27,38 @@ impl MultipleSubscribers { } /// Adds a child subscriber which runs in its own process. - pub fn add_subscriber(mut self, subscriber: impl Subscriber) -> Self { + pub fn add_subscriber(mut self, subscriber: S) -> Self + where + S: Subscriber + Serialize + for<'de> Deserialize<'de>, + { let process = spawn_subscriber(subscriber); self.subscribers.push(process); self } + + /// Adds a child subscriber which runs in its own process. + /// This is useful if a subscriber is not serializable. + pub fn add_subscriber_fn(mut self, subscriber: fn() -> S) -> Self + where + S: Subscriber, + { + let process = spawn_subscriber_fn(subscriber); + self.subscribers.push(process); + self + } + + /// Initializes as the global subscriber. + /// + /// Note, this will cause a panic if a global subscriber has already been initialized. + /// Use the [`try_init`] to handle this error. + pub fn init(self) { + self.try_init().unwrap(); + } + + /// Initializes as the global subscriber, returning an error if a global subscriber has already been initialized. + pub fn try_init(self) -> Result { + init_subscriber(self) + } } impl Subscriber for MultipleSubscribers { @@ -36,7 +68,7 @@ impl Subscriber for MultipleSubscribers { fn event(&self, event: &Event) { for subscriber in &self.subscribers { - subscriber.send(event.clone()); + subscriber.send(SubscriberMessage::Event(event.clone())); } } }