support field values in err! macro

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-08-01 08:41:47 +00:00
parent 5b1642f641
commit 5add9a8c34
4 changed files with 127 additions and 31 deletions

View file

@ -14,7 +14,7 @@ pub use crate::utils::debug::*;
#[macro_export] #[macro_export]
macro_rules! debug_event { macro_rules! debug_event {
( $level:expr, $($x:tt)+ ) => { ( $level:expr, $($x:tt)+ ) => {
if cfg!(debug_assertions) && cfg!(not(feature = "dev_release_log_level")) { if $crate::debug::logging() {
::tracing::event!( $level, $($x)+ ) ::tracing::event!( $level, $($x)+ )
} else { } else {
::tracing::debug!( $($x)+ ) ::tracing::debug!( $($x)+ )
@ -88,3 +88,7 @@ pub fn panic_str(p: &Box<dyn Any + Send>) -> &'static str { p.downcast_ref::<&st
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn type_name<T>(_: &T) -> &'static str { std::any::type_name::<T>() } pub fn type_name<T>(_: &T) -> &'static str { std::any::type_name::<T>() }
#[must_use]
#[inline]
pub const fn logging() -> bool { cfg!(debug_assertions) && cfg!(not(feature = "dev_release_log_level")) }

View file

@ -41,60 +41,149 @@ macro_rules! Err {
#[macro_export] #[macro_export]
macro_rules! err { macro_rules! err {
(Config($item:literal, $($args:expr),*)) => {{ (Request(Forbidden($level:ident!($($args:tt)+)))) => {{
$crate::error!(config = %$item, $($args),*); let mut buf = String::new();
$crate::error::Error::Config($item, $crate::format_maybe!($($args),*))
}};
(Request(Forbidden($level:ident!($($args:expr),*)))) => {{
$crate::$level!($($args),*);
$crate::error::Error::Request( $crate::error::Error::Request(
::ruma::api::client::error::ErrorKind::forbidden(), ::ruma::api::client::error::ErrorKind::forbidden(),
$crate::format_maybe!($($args),*), $crate::err_log!(buf, $level, $($args)+),
::http::StatusCode::BAD_REQUEST ::http::StatusCode::BAD_REQUEST
) )
}}; }};
(Request(Forbidden($($args:expr),*))) => { (Request(Forbidden($($args:tt)+))) => {
$crate::error::Error::Request( $crate::error::Error::Request(
::ruma::api::client::error::ErrorKind::forbidden(), ::ruma::api::client::error::ErrorKind::forbidden(),
$crate::format_maybe!($($args),*), $crate::format_maybe!($($args)+),
::http::StatusCode::BAD_REQUEST ::http::StatusCode::BAD_REQUEST
) )
}; };
(Request($variant:ident($level:ident!($($args:expr),*)))) => {{ (Request($variant:ident($level:ident!($($args:tt)+)))) => {{
$crate::$level!($($args),*); let mut buf = String::new();
$crate::error::Error::Request( $crate::error::Error::Request(
::ruma::api::client::error::ErrorKind::$variant, ::ruma::api::client::error::ErrorKind::$variant,
$crate::format_maybe!($($args),*), $crate::err_log!(buf, $level, $($args)+),
::http::StatusCode::BAD_REQUEST ::http::StatusCode::BAD_REQUEST
) )
}}; }};
(Request($variant:ident($($args:expr),*))) => { (Request($variant:ident($($args:tt)+))) => {
$crate::error::Error::Request( $crate::error::Error::Request(
::ruma::api::client::error::ErrorKind::$variant, ::ruma::api::client::error::ErrorKind::$variant,
$crate::format_maybe!($($args),*), $crate::format_maybe!($($args)+),
::http::StatusCode::BAD_REQUEST ::http::StatusCode::BAD_REQUEST
) )
}; };
($variant:ident($level:ident!($($args:expr),*))) => {{ (Config($item:literal, $($args:tt)+)) => {{
$crate::$level!($($args),*); let mut buf = String::new();
$crate::error::Error::$variant($crate::format_maybe!($($args),*)) $crate::error::Error::Config($item, $crate::err_log!(buf, error, config = %$item, $($args)+))
}}; }};
($variant:ident($($args:expr),*)) => { ($variant:ident($level:ident!($($args:tt)+))) => {{
$crate::error::Error::$variant($crate::format_maybe!($($args),*)) 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),*)) => {{ ($level:ident!($($args:tt)+)) => {{
$crate::$level!($($args),*); let mut buf = String::new();
$crate::error::Error::Err($crate::format_maybe!($($args),*)) $crate::error::Error::Err($crate::err_log!(buf, $level, $($args)+))
}}; }};
($($args:expr),*) => { ($($args:tt)+) => {
$crate::error::Error::Err($crate::format_maybe!($($args),*)) $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
}; };
} }

View file

@ -5,8 +5,7 @@ mod response;
use std::{any::Any, borrow::Cow, convert::Infallible, fmt}; use std::{any::Any, borrow::Cow, convert::Infallible, fmt};
pub use log::*; pub use self::log::*;
use crate::error; use crate::error;
#[derive(thiserror::Error)] #[derive(thiserror::Error)]

View file

@ -8,12 +8,12 @@ pub const EMPTY: &str = "";
/// arguments are provided the first is assumed to be a format string. /// arguments are provided the first is assumed to be a format string.
#[macro_export] #[macro_export]
macro_rules! format_maybe { macro_rules! format_maybe {
($s:literal) => { ($s:literal $(,)?) => {
if $crate::is_format!($s) { std::format!($s).into() } else { $s.into() } if $crate::is_format!($s) { std::format!($s).into() } else { $s.into() }
}; };
($($args:expr),*) => { ($s:literal, $($args:tt)+) => {
std::format!($($args),*).into() std::format!($s, $($args)+).into()
}; };
} }
@ -24,6 +24,10 @@ macro_rules! is_format {
($s:literal) => { ($s:literal) => {
::const_str::contains!($s, "{") && ::const_str::contains!($s, "}") ::const_str::contains!($s, "{") && ::const_str::contains!($s, "}")
}; };
($($s:tt)+) => {
false
};
} }
#[inline] #[inline]