From 5add9a8c345212670630dfd2144e4bc00b657629 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Thu, 1 Aug 2024 08:41:47 +0000 Subject: [PATCH] support field values in err! macro Signed-off-by: Jason Volk --- src/core/debug.rs | 6 +- src/core/error/err.rs | 139 ++++++++++++++++++++++++++++++++------- src/core/error/mod.rs | 3 +- src/core/utils/string.rs | 10 ++- 4 files changed, 127 insertions(+), 31 deletions(-) diff --git a/src/core/debug.rs b/src/core/debug.rs index 63f25d47..3ab9ed0d 100644 --- a/src/core/debug.rs +++ b/src/core/debug.rs @@ -14,7 +14,7 @@ pub use crate::utils::debug::*; #[macro_export] macro_rules! debug_event { ( $level:expr, $($x:tt)+ ) => { - if cfg!(debug_assertions) && cfg!(not(feature = "dev_release_log_level")) { + if $crate::debug::logging() { ::tracing::event!( $level, $($x)+ ) } else { ::tracing::debug!( $($x)+ ) @@ -88,3 +88,7 @@ pub fn panic_str(p: &Box) -> &'static str { p.downcast_ref::<&st #[inline(always)] #[must_use] pub fn type_name(_: &T) -> &'static str { std::any::type_name::() } + +#[must_use] +#[inline] +pub const fn logging() -> bool { cfg!(debug_assertions) && cfg!(not(feature = "dev_release_log_level")) } diff --git a/src/core/error/err.rs b/src/core/error/err.rs index 55e38f18..b3d0240e 100644 --- a/src/core/error/err.rs +++ b/src/core/error/err.rs @@ -41,60 +41,149 @@ macro_rules! Err { #[macro_export] macro_rules! err { - (Config($item:literal, $($args:expr),*)) => {{ - $crate::error!(config = %$item, $($args),*); - $crate::error::Error::Config($item, $crate::format_maybe!($($args),*)) - }}; - - (Request(Forbidden($level:ident!($($args:expr),*)))) => {{ - $crate::$level!($($args),*); + (Request(Forbidden($level:ident!($($args:tt)+)))) => {{ + let mut buf = String::new(); $crate::error::Error::Request( ::ruma::api::client::error::ErrorKind::forbidden(), - $crate::format_maybe!($($args),*), + $crate::err_log!(buf, $level, $($args)+), ::http::StatusCode::BAD_REQUEST ) }}; - (Request(Forbidden($($args:expr),*))) => { + (Request(Forbidden($($args:tt)+))) => { $crate::error::Error::Request( ::ruma::api::client::error::ErrorKind::forbidden(), - $crate::format_maybe!($($args),*), + $crate::format_maybe!($($args)+), ::http::StatusCode::BAD_REQUEST ) }; - (Request($variant:ident($level:ident!($($args:expr),*)))) => {{ - $crate::$level!($($args),*); + (Request($variant:ident($level:ident!($($args:tt)+)))) => {{ + let mut buf = String::new(); $crate::error::Error::Request( ::ruma::api::client::error::ErrorKind::$variant, - $crate::format_maybe!($($args),*), + $crate::err_log!(buf, $level, $($args)+), ::http::StatusCode::BAD_REQUEST ) }}; - (Request($variant:ident($($args:expr),*))) => { + (Request($variant:ident($($args:tt)+))) => { $crate::error::Error::Request( ::ruma::api::client::error::ErrorKind::$variant, - $crate::format_maybe!($($args),*), + $crate::format_maybe!($($args)+), ::http::StatusCode::BAD_REQUEST ) }; - ($variant:ident($level:ident!($($args:expr),*))) => {{ - $crate::$level!($($args),*); - $crate::error::Error::$variant($crate::format_maybe!($($args),*)) + (Config($item:literal, $($args:tt)+)) => {{ + let mut buf = String::new(); + $crate::error::Error::Config($item, $crate::err_log!(buf, error, config = %$item, $($args)+)) }}; - ($variant:ident($($args:expr),*)) => { - $crate::error::Error::$variant($crate::format_maybe!($($args),*)) + ($variant:ident($level:ident!($($args:tt)+))) => {{ + let mut buf = String::new(); + $crate::error::Error::$variant($crate::err_log!(buf, $level, $($args)+)) + }}; + + ($variant:ident($($args:tt)+)) => { + $crate::error::Error::$variant($crate::format_maybe!($($args)+)) }; - ($level:ident!($($args:expr),*)) => {{ - $crate::$level!($($args),*); - $crate::error::Error::Err($crate::format_maybe!($($args),*)) + ($level:ident!($($args:tt)+)) => {{ + let mut buf = String::new(); + $crate::error::Error::Err($crate::err_log!(buf, $level, $($args)+)) }}; - ($($args:expr),*) => { - $crate::error::Error::Err($crate::format_maybe!($($args),*)) + ($($args:tt)+) => { + $crate::error::Error::Err($crate::format_maybe!($($args)+)) + }; +} + +/// A trinity of integration between tracing, logging, and Error. This is a +/// customization of tracing::event! with the primary purpose of sharing the +/// error string, fieldset parsing and formatting. An added benefit is that we +/// can share the same callsite metadata for the source of our Error and the +/// associated logging and tracing event dispatches. +#[macro_export] +macro_rules! err_log { + ($out:ident, $level:ident, $($fields:tt)+) => {{ + use std::{fmt, fmt::Write}; + + use ::tracing::{ + callsite, callsite2, level_enabled, metadata, valueset, Callsite, Event, __macro_support, + __tracing_log, + field::{Field, ValueSet, Visit}, + Level, + }; + + const LEVEL: Level = $crate::err_lev!($level); + static __CALLSITE: callsite::DefaultCallsite = callsite2! { + name: std::concat! { + "event ", + std::file!(), + ":", + std::line!(), + }, + kind: metadata::Kind::EVENT, + target: std::module_path!(), + level: LEVEL, + fields: $($fields)+, + }; + + let visit = &mut |vs: ValueSet<'_>| { + struct Visitor<'a>(&'a mut String); + impl Visit for Visitor<'_> { + fn record_debug(&mut self, field: &Field, val: &dyn fmt::Debug) { + if field.name() == "message" { + write!(self.0, "{:?}", val).expect("stream error"); + } else { + write!(self.0, " {}={:?}", field.name(), val).expect("stream error"); + } + } + } + + let meta = __CALLSITE.metadata(); + let enabled = level_enabled!(LEVEL) && { + let interest = __CALLSITE.interest(); + !interest.is_never() && __macro_support::__is_enabled(meta, interest) + }; + + if enabled { + Event::dispatch(meta, &vs); + } + + __tracing_log!(LEVEL, __CALLSITE, &vs); + vs.record(&mut Visitor(&mut $out)); + }; + + (visit)(valueset!(__CALLSITE.metadata().fields(), $($fields)+)); + ($out).into() + }} +} + +#[macro_export] +macro_rules! err_lev { + (debug_warn) => { + if $crate::debug::logging() { + ::tracing::Level::WARN + } else { + ::tracing::Level::DEBUG + } + }; + + (debug_error) => { + if $crate::debug::logging() { + ::tracing::Level::ERROR + } else { + ::tracing::Level::DEBUG + } + }; + + (warn) => { + ::tracing::Level::WARN + }; + + (error) => { + ::tracing::Level::ERROR }; } diff --git a/src/core/error/mod.rs b/src/core/error/mod.rs index 5406d5ff..730220ac 100644 --- a/src/core/error/mod.rs +++ b/src/core/error/mod.rs @@ -5,8 +5,7 @@ mod response; use std::{any::Any, borrow::Cow, convert::Infallible, fmt}; -pub use log::*; - +pub use self::log::*; use crate::error; #[derive(thiserror::Error)] diff --git a/src/core/utils/string.rs b/src/core/utils/string.rs index fc3891df..a597d198 100644 --- a/src/core/utils/string.rs +++ b/src/core/utils/string.rs @@ -8,12 +8,12 @@ pub const EMPTY: &str = ""; /// arguments are provided the first is assumed to be a format string. #[macro_export] macro_rules! format_maybe { - ($s:literal) => { + ($s:literal $(,)?) => { if $crate::is_format!($s) { std::format!($s).into() } else { $s.into() } }; - ($($args:expr),*) => { - std::format!($($args),*).into() + ($s:literal, $($args:tt)+) => { + std::format!($s, $($args)+).into() }; } @@ -24,6 +24,10 @@ macro_rules! is_format { ($s:literal) => { ::const_str::contains!($s, "{") && ::const_str::contains!($s, "}") }; + + ($($s:tt)+) => { + false + }; } #[inline]