From e0c0008264192f30119c0cbbbf1de86c609d3714 Mon Sep 17 00:00:00 2001 From: Aik Kalantarian Date: Tue, 26 Nov 2024 20:24:59 -0800 Subject: [PATCH 1/2] commit! --- Cargo.toml | 1 + src/backtrace.rs | 38 ++++++++++++++++ src/context.rs | 11 +++-- src/error.rs | 110 +++++++++++++++++++++++++++++++++++++++++------ src/kind.rs | 5 ++- 5 files changed, 147 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0cd83c5..8865bdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ std = [] # besides bringing in an unused dependency, as `std::backtrace` is always # preferred. backtrace = { version = "0.3.51", optional = true } +tracing = { version = "0.1.7", optional = true } [dev-dependencies] futures = { version = "0.3", default-features = false } diff --git a/src/backtrace.rs b/src/backtrace.rs index 303f911..a878cc8 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -35,6 +35,20 @@ macro_rules! backtrace { }; } +#[cfg(feature = "tracing")] +macro_rules! capture_span { + () => { + Some(::tracing::Span::current()) + }; +} + +#[cfg(not(feature = "tracing"))] +macro_rules! capture_span { + () => { + None + }; +} + #[cfg(error_generic_member_access)] macro_rules! backtrace_if_absent { ($err:expr) => { @@ -68,6 +82,30 @@ macro_rules! backtrace_if_absent { }; } +#[cfg(all(error_generic_member_access, feature = "tracing"))] +macro_rules! span_if_absent { + ($err:expr) => { + match core::error::request_ref::<::tracing::Span>($err as &dyn core::error::Error) { + Some(_) => None, + None => capture_span!(), + } + }; +} + +#[cfg(all(not(error_generic_member_access), feature = "tracing"))] +macro_rules! span_if_absent { + ($err:expr) => { + capture_span!() + }; +} + +#[cfg(not(feature = "tracing"))] +macro_rules! span_if_absent { + ($err:expr) => { + None + }; +} + #[cfg(all(not(std_backtrace), feature = "backtrace"))] mod capture { use alloc::borrow::{Cow, ToOwned as _}; diff --git a/src/context.rs b/src/context.rs index b52f682..8777ff2 100644 --- a/src/context.rs +++ b/src/context.rs @@ -25,7 +25,8 @@ mod ext { C: Display + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(&self); - Error::from_context(context, self, backtrace) + let span = span_if_absent!(&self); + Error::from_context(context, self, backtrace, span) } } @@ -96,7 +97,7 @@ impl Context for Option { // backtrace. match self { Some(ok) => Ok(ok), - None => Err(Error::from_display(context, backtrace!())), + None => Err(Error::from_display(context, backtrace!(), capture_span!())), } } @@ -107,7 +108,11 @@ impl Context for Option { { match self { Some(ok) => Ok(ok), - None => Err(Error::from_display(context(), backtrace!())), + None => Err(Error::from_display( + context(), + backtrace!(), + capture_span!(), + )), } } } diff --git a/src/error.rs b/src/error.rs index a83eb7e..d40d54c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,7 +36,8 @@ impl Error { E: StdError + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(&error); - Error::from_std(error, backtrace) + let span = span_if_absent!(&error); + Error::from_std(error, backtrace, span) } /// Create a new error object from a printable error message. @@ -82,12 +83,16 @@ impl Error { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message, backtrace!(), capture_span!()) } #[cfg(any(feature = "std", not(anyhow_no_core_error)))] #[cold] - pub(crate) fn from_std(error: E, backtrace: Option) -> Self + pub(crate) fn from_std( + error: E, + backtrace: Option, + span: Option, + ) -> Self where E: StdError + Send + Sync + 'static, { @@ -106,14 +111,20 @@ impl Error { any(std_backtrace, feature = "backtrace") ))] object_backtrace: no_backtrace, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: no_span, }; // Safety: passing vtable that operates on the right type E. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, span) } } #[cold] - pub(crate) fn from_adhoc(message: M, backtrace: Option) -> Self + pub(crate) fn from_adhoc( + message: M, + backtrace: Option, + span: Option, + ) -> Self where M: Display + Debug + Send + Sync + 'static, { @@ -134,15 +145,21 @@ impl Error { any(std_backtrace, feature = "backtrace") ))] object_backtrace: no_backtrace, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: no_span, }; // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, span) } } #[cold] - pub(crate) fn from_display(message: M, backtrace: Option) -> Self + pub(crate) fn from_display( + message: M, + backtrace: Option, + span: Option, + ) -> Self where M: Display + Send + Sync + 'static, { @@ -163,16 +180,23 @@ impl Error { any(std_backtrace, feature = "backtrace") ))] object_backtrace: no_backtrace, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: no_span, }; // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, span) } } #[cfg(any(feature = "std", not(anyhow_no_core_error)))] #[cold] - pub(crate) fn from_context(context: C, error: E, backtrace: Option) -> Self + pub(crate) fn from_context( + context: C, + error: E, + backtrace: Option, + span: Option, + ) -> Self where C: Display + Send + Sync + 'static, E: StdError + Send + Sync + 'static, @@ -194,10 +218,12 @@ impl Error { any(std_backtrace, feature = "backtrace") ))] object_backtrace: no_backtrace, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: no_span, }; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, span) } } #[cfg(any(feature = "std", not(anyhow_no_core_error)))] @@ -205,6 +231,7 @@ impl Error { pub(crate) fn from_boxed( error: Box, backtrace: Option, + span: Option, ) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); @@ -223,11 +250,13 @@ impl Error { any(std_backtrace, feature = "backtrace") ))] object_backtrace: no_backtrace, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: no_span, }; // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, span) } } // Takes backtrace as argument rather than capturing it here so that the @@ -240,6 +269,7 @@ impl Error { error: E, vtable: &'static ErrorVTable, backtrace: Option, + span: Option, ) -> Self where E: StdError + Send + Sync + 'static, @@ -247,6 +277,7 @@ impl Error { let inner: Box> = Box::new(ErrorImpl { vtable, backtrace, + span, _object: error, }); // Erase the concrete type of E from the compile-time type system. This @@ -339,13 +370,18 @@ impl Error { any(std_backtrace, feature = "backtrace") ))] object_backtrace: context_backtrace::, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: context_span::, }; // As the cause is anyhow::Error, we already have a backtrace for it. let backtrace = None; + // As the cause is anyhow::Error, we already have a span for it. + let span = None; + // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { Error::construct(error, vtable, backtrace, span) } } /// Get the backtrace for this Error. @@ -380,6 +416,11 @@ impl Error { unsafe { ErrorImpl::backtrace(self.inner.by_ref()) } } + #[cfg(feature = "tracing")] + pub fn span(&self) -> &tracing::Span { + unsafe { ErrorImpl::span(self.inner.by_ref()) } + } + /// An iterator of the chain of source errors contained by this Error. /// /// This iterator will visit every error in the cause chain of this error @@ -562,7 +603,8 @@ where #[cold] fn from(error: E) -> Self { let backtrace = backtrace_if_absent!(&error); - Error::from_std(error, backtrace) + let span = span_if_absent!(&error); + Error::from_std(error, backtrace, span) } } @@ -618,6 +660,8 @@ struct ErrorVTable { any(std_backtrace, feature = "backtrace") ))] object_backtrace: unsafe fn(Ref) -> Option<&Backtrace>, + #[cfg(all(not(error_generic_member_access), feature = "tracing"))] + object_span: unsafe fn(Ref) -> Option<&tracing::Span>, } // Safety: requires layout of *e to match ErrorImpl. @@ -730,6 +774,12 @@ fn no_backtrace(e: Ref) -> Option<&Backtrace> { None } +#[cfg(all(not(error_generic_member_access), feature = "tracing"))] +fn no_span(e: Ref) -> Option<&tracing::Span> { + let _ = e; + None +} + // Safety: requires layout of *e to match ErrorImpl>. #[cfg(any(feature = "std", not(anyhow_no_core_error)))] unsafe fn context_downcast(e: Ref, target: TypeId) -> Option> @@ -860,6 +910,19 @@ where Some(backtrace) } +// Safety: requires layout of *e to match ErrorImpl>. +#[cfg(all(not(error_generic_member_access), feature = "tracing"))] +#[allow(clippy::unnecessary_wraps)] +unsafe fn context_span(e: Ref) -> Option<&tracing::Span> +where + C: 'static, +{ + let unerased_ref = e.cast::>>(); + let unerased = unsafe { unerased_ref.deref() }; + let span = unsafe { ErrorImpl::span(unerased._object.error.inner.by_ref()) }; + Some(span) +} + // NOTE: If working with `ErrorImpl<()>`, references should be avoided in favor // of raw pointers and `NonNull`. // repr C to ensure that E remains in the final position. @@ -867,6 +930,7 @@ where pub(crate) struct ErrorImpl { vtable: &'static ErrorVTable, backtrace: Option, + span: Option, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, @@ -936,11 +1000,31 @@ impl ErrorImpl { .expect("backtrace capture failed") } + #[cfg(feature = "tracing")] + pub(crate) unsafe fn span(this: Ref) -> &tracing::Span { + // This unwrap can only panic if the underlying error's backtrace method + // is nondeterministic, which would only happen in maliciously + // constructed code. + unsafe { this.deref() } + .span + .as_ref() + .or_else(|| { + #[cfg(error_generic_member_access)] + return error::request_ref::(unsafe { Self::error(this) }); + #[cfg(not(error_generic_member_access))] + return unsafe { (vtable(this.ptr).object_span)(this) }; + }) + .expect("span capture failed") + } + #[cfg(error_generic_member_access)] unsafe fn provide<'a>(this: Ref<'a, Self>, request: &mut Request<'a>) { if let Some(backtrace) = unsafe { &this.deref().backtrace } { request.provide_ref(backtrace); } + if let Some(span) = unsafe { &this.deref().span } { + request.provide_ref(span); + } unsafe { Self::error(this) }.provide(request); } diff --git a/src/kind.rs b/src/kind.rs index 042af32..1023ac4 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -70,7 +70,7 @@ impl Adhoc { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + Error::from_adhoc(message, backtrace!(), capture_span!()) } } @@ -116,6 +116,7 @@ impl Boxed { #[cold] pub fn new(self, error: Box) -> Error { let backtrace = backtrace_if_absent!(&*error); - Error::from_boxed(error, backtrace) + let span = span_if_absent!(&*error); + Error::from_boxed(error, backtrace, span) } } From ef11331c8343569f4e121ef60d685f06191ef67d Mon Sep 17 00:00:00 2001 From: Aik Kalantarian Date: Thu, 28 Nov 2024 14:57:57 -0800 Subject: [PATCH 2/2] fully hide behind feature flag --- src/backtrace.rs | 14 ------- src/context.rs | 17 ++++++++- src/error.rs | 99 ++++++++++++++++++++++++++++++++++++++++-------- src/kind.rs | 15 +++++++- 4 files changed, 112 insertions(+), 33 deletions(-) diff --git a/src/backtrace.rs b/src/backtrace.rs index a878cc8..b00f2bd 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -42,13 +42,6 @@ macro_rules! capture_span { }; } -#[cfg(not(feature = "tracing"))] -macro_rules! capture_span { - () => { - None - }; -} - #[cfg(error_generic_member_access)] macro_rules! backtrace_if_absent { ($err:expr) => { @@ -99,13 +92,6 @@ macro_rules! span_if_absent { }; } -#[cfg(not(feature = "tracing"))] -macro_rules! span_if_absent { - ($err:expr) => { - None - }; -} - #[cfg(all(not(std_backtrace), feature = "backtrace"))] mod capture { use alloc::borrow::{Cow, ToOwned as _}; diff --git a/src/context.rs b/src/context.rs index 8777ff2..466288f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -25,8 +25,15 @@ mod ext { C: Display + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(&self); + #[cfg(feature = "tracing")] let span = span_if_absent!(&self); - Error::from_context(context, self, backtrace, span) + Error::from_context( + context, + self, + backtrace, + #[cfg(feature = "tracing")] + span, + ) } } @@ -97,7 +104,12 @@ impl Context for Option { // backtrace. match self { Some(ok) => Ok(ok), - None => Err(Error::from_display(context, backtrace!(), capture_span!())), + None => Err(Error::from_display( + context, + backtrace!(), + #[cfg(feature = "tracing")] + capture_span!(), + )), } } @@ -111,6 +123,7 @@ impl Context for Option { None => Err(Error::from_display( context(), backtrace!(), + #[cfg(feature = "tracing")] capture_span!(), )), } diff --git a/src/error.rs b/src/error.rs index d40d54c..2610033 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,8 +36,14 @@ impl Error { E: StdError + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(&error); + #[cfg(feature = "tracing")] let span = span_if_absent!(&error); - Error::from_std(error, backtrace, span) + Error::from_std( + error, + backtrace, + #[cfg(feature = "tracing")] + span, + ) } /// Create a new error object from a printable error message. @@ -83,7 +89,12 @@ impl Error { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!(), capture_span!()) + Error::from_adhoc( + message, + backtrace!(), + #[cfg(feature = "tracing")] + capture_span!(), + ) } #[cfg(any(feature = "std", not(anyhow_no_core_error)))] @@ -91,7 +102,7 @@ impl Error { pub(crate) fn from_std( error: E, backtrace: Option, - span: Option, + #[cfg(feature = "tracing")] span: Option, ) -> Self where E: StdError + Send + Sync + 'static, @@ -116,14 +127,22 @@ impl Error { }; // Safety: passing vtable that operates on the right type E. - unsafe { Error::construct(error, vtable, backtrace, span) } + unsafe { + Error::construct( + error, + vtable, + backtrace, + #[cfg(feature = "tracing")] + span, + ) + } } #[cold] pub(crate) fn from_adhoc( message: M, backtrace: Option, - span: Option, + #[cfg(feature = "tracing")] span: Option, ) -> Self where M: Display + Debug + Send + Sync + 'static, @@ -151,14 +170,22 @@ impl Error { // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. - unsafe { Error::construct(error, vtable, backtrace, span) } + unsafe { + Error::construct( + error, + vtable, + backtrace, + #[cfg(feature = "tracing")] + span, + ) + } } #[cold] pub(crate) fn from_display( message: M, backtrace: Option, - span: Option, + #[cfg(feature = "tracing")] span: Option, ) -> Self where M: Display + Send + Sync + 'static, @@ -186,7 +213,15 @@ impl Error { // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. - unsafe { Error::construct(error, vtable, backtrace, span) } + unsafe { + Error::construct( + error, + vtable, + backtrace, + #[cfg(feature = "tracing")] + span, + ) + } } #[cfg(any(feature = "std", not(anyhow_no_core_error)))] @@ -195,7 +230,7 @@ impl Error { context: C, error: E, backtrace: Option, - span: Option, + #[cfg(feature = "tracing")] span: Option, ) -> Self where C: Display + Send + Sync + 'static, @@ -223,7 +258,15 @@ impl Error { }; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace, span) } + unsafe { + Error::construct( + error, + vtable, + backtrace, + #[cfg(feature = "tracing")] + span, + ) + } } #[cfg(any(feature = "std", not(anyhow_no_core_error)))] @@ -231,7 +274,7 @@ impl Error { pub(crate) fn from_boxed( error: Box, backtrace: Option, - span: Option, + #[cfg(feature = "tracing")] span: Option, ) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); @@ -256,7 +299,15 @@ impl Error { // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. - unsafe { Error::construct(error, vtable, backtrace, span) } + unsafe { + Error::construct( + error, + vtable, + backtrace, + #[cfg(feature = "tracing")] + span, + ) + } } // Takes backtrace as argument rather than capturing it here so that the @@ -269,7 +320,7 @@ impl Error { error: E, vtable: &'static ErrorVTable, backtrace: Option, - span: Option, + #[cfg(feature = "tracing")] span: Option, ) -> Self where E: StdError + Send + Sync + 'static, @@ -277,6 +328,7 @@ impl Error { let inner: Box> = Box::new(ErrorImpl { vtable, backtrace, + #[cfg(feature = "tracing")] span, _object: error, }); @@ -378,10 +430,19 @@ impl Error { let backtrace = None; // As the cause is anyhow::Error, we already have a span for it. + #[cfg(feature = "tracing")] let span = None; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace, span) } + unsafe { + Error::construct( + error, + vtable, + backtrace, + #[cfg(feature = "tracing")] + span, + ) + } } /// Get the backtrace for this Error. @@ -603,8 +664,14 @@ where #[cold] fn from(error: E) -> Self { let backtrace = backtrace_if_absent!(&error); + #[cfg(feature = "tracing")] let span = span_if_absent!(&error); - Error::from_std(error, backtrace, span) + Error::from_std( + error, + backtrace, + #[cfg(feature = "tracing")] + span, + ) } } @@ -930,6 +997,7 @@ where pub(crate) struct ErrorImpl { vtable: &'static ErrorVTable, backtrace: Option, + #[cfg(feature = "tracing")] span: Option, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. @@ -1022,6 +1090,7 @@ impl ErrorImpl { if let Some(backtrace) = unsafe { &this.deref().backtrace } { request.provide_ref(backtrace); } + #[cfg(feature = "tracing")] if let Some(span) = unsafe { &this.deref().span } { request.provide_ref(span); } diff --git a/src/kind.rs b/src/kind.rs index 1023ac4..e683094 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -70,7 +70,12 @@ impl Adhoc { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!(), capture_span!()) + Error::from_adhoc( + message, + backtrace!(), + #[cfg(feature = "tracing")] + capture_span!(), + ) } } @@ -116,7 +121,13 @@ impl Boxed { #[cold] pub fn new(self, error: Box) -> Error { let backtrace = backtrace_if_absent!(&*error); + #[cfg(feature = "tracing")] let span = span_if_absent!(&*error); - Error::from_boxed(error, backtrace, span) + Error::from_boxed( + error, + backtrace, + #[cfg(feature = "tracing")] + span, + ) } }