diff options
Diffstat (limited to 'vendor/tracing-error/src')
-rw-r--r-- | vendor/tracing-error/src/backtrace.rs | 320 | ||||
-rw-r--r-- | vendor/tracing-error/src/error.rs | 289 | ||||
-rw-r--r-- | vendor/tracing-error/src/layer.rs | 132 | ||||
-rw-r--r-- | vendor/tracing-error/src/lib.rs | 236 |
4 files changed, 977 insertions, 0 deletions
diff --git a/vendor/tracing-error/src/backtrace.rs b/vendor/tracing-error/src/backtrace.rs new file mode 100644 index 000000000..0a2c31d29 --- /dev/null +++ b/vendor/tracing-error/src/backtrace.rs @@ -0,0 +1,320 @@ +use crate::layer::WithContext; +use std::fmt; +use tracing::{Metadata, Span}; + +/// A captured trace of [`tracing`] spans. +/// +/// This type can be thought of as a relative of +/// [`std::backtrace::Backtrace`][`Backtrace`]. +/// However, rather than capturing the current call stack when it is +/// constructed, a `SpanTrace` instead captures the current [span] and its +/// [parents]. +/// +/// In many cases, span traces may be as useful as stack backtraces useful in +/// pinpointing where an error occurred and why, if not moreso: +/// +/// * A span trace captures only the user-defined, human-readable `tracing` +/// spans, rather than _every_ frame in the call stack, often cutting out a +/// lot of noise. +/// * Span traces include the [fields] recorded by each span in the trace, as +/// well as their names and source code location, so different invocations of +/// a function can be distinguished, +/// * In asynchronous code, backtraces for errors that occur in [futures] often +/// consist not of the stack frames that _spawned_ a future, but the stack +/// frames of the executor that is responsible for running that future. This +/// means that if an `async fn` calls another `async fn` which generates an +/// error, the calling async function will not appear in the stack trace (and +/// often, the callee won't either!). On the other hand, when the +/// [`tracing-futures`] crate is used to instrument async code, the span trace +/// will represent the logical application context a future was running in, +/// rather than the stack trace of the executor that was polling a future when +/// an error occurred. +/// +/// Finally, unlike stack [`Backtrace`]s, capturing a `SpanTrace` is fairly +/// lightweight, and the resulting struct is not large. The `SpanTrace` struct +/// is formatted lazily; instead, it simply stores a copy of the current span, +/// and allows visiting the spans in that span's trace tree by calling the +/// [`with_spans` method][`with_spans`]. +/// +/// # Formatting +/// +/// The `SpanTrace` type implements `fmt::Display`, formatting the span trace +/// similarly to how Rust formats panics. For example: +/// +/// ```text +/// 0: custom_error::do_another_thing +/// with answer=42 will_succeed=false +/// at examples/examples/custom_error.rs:42 +/// 1: custom_error::do_something +/// with foo="hello world" +/// at examples/examples/custom_error.rs:37 +/// ``` +/// +/// Additionally, if custom formatting is desired, the [`with_spans`] method can +/// be used to visit each span in the trace, formatting them in order. +/// +/// [`tracing`]: https://docs.rs/tracing +/// [`Backtrace`]: https://doc.rust-lang.org/std/backtrace/struct.Backtrace.html +/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html +/// [parents]: https://docs.rs/tracing/latest/tracing/span/index.html#span-relationships +/// [fields]: https://docs.rs/tracing/latest/tracing/field/index.html +/// [futures]: https://doc.rust-lang.org/std/future/trait.Future.html +/// [`tracing-futures`]: https://docs.rs/tracing-futures/ +/// [`with_spans`]: #method.with_spans +#[derive(Clone)] +pub struct SpanTrace { + span: Span, +} + +// === impl SpanTrace === + +impl SpanTrace { + /// Create a new span trace with the given span as the innermost span. + pub fn new(span: Span) -> Self { + SpanTrace { span } + } + + /// Capture the current span trace. + /// + /// # Examples + /// ```rust + /// use tracing_error::SpanTrace; + /// + /// pub struct MyError { + /// span_trace: SpanTrace, + /// // ... + /// } + /// + /// # fn some_error_condition() -> bool { true } + /// + /// #[tracing::instrument] + /// pub fn my_function(arg: &str) -> Result<(), MyError> { + /// if some_error_condition() { + /// return Err(MyError { + /// span_trace: SpanTrace::capture(), + /// // ... + /// }); + /// } + /// + /// // ... + /// # Ok(()) + /// } + /// ``` + pub fn capture() -> Self { + SpanTrace::new(Span::current()) + } + + /// Apply a function to all captured spans in the trace until it returns + /// `false`. + /// + /// This will call the provided function with a reference to the + /// [`Metadata`] and a formatted representation of the [fields] of each span + /// captured in the trace, starting with the span that was current when the + /// trace was captured. The function may return `true` or `false` to + /// indicate whether to continue iterating over spans; if it returns + /// `false`, no additional spans will be visited. + /// + /// [fields]: https://docs.rs/tracing/latest/tracing/field/index.html + /// [`Metadata`]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html + pub fn with_spans(&self, f: impl FnMut(&'static Metadata<'static>, &str) -> bool) { + self.span.with_subscriber(|(id, s)| { + if let Some(getcx) = s.downcast_ref::<WithContext>() { + getcx.with_context(s, id, f); + } + }); + } + + /// Returns the status of this `SpanTrace`. + /// + /// The status indicates one of the following: + /// * the current subscriber does not support capturing `SpanTrace`s + /// * there was no current span, so a trace was not captured + /// * a span trace was successfully captured + pub fn status(&self) -> SpanTraceStatus { + let inner = if self.span.is_none() { + SpanTraceStatusInner::Empty + } else { + let mut status = None; + self.span.with_subscriber(|(_, s)| { + if s.downcast_ref::<WithContext>().is_some() { + status = Some(SpanTraceStatusInner::Captured); + } + }); + + status.unwrap_or(SpanTraceStatusInner::Unsupported) + }; + + SpanTraceStatus(inner) + } +} + +/// The current status of a SpanTrace, indicating whether it was captured or +/// whether it is empty for some other reason. +#[derive(Debug, PartialEq, Eq)] +pub struct SpanTraceStatus(SpanTraceStatusInner); + +impl SpanTraceStatus { + /// Formatting a SpanTrace is not supported, likely because there is no + /// ErrorLayer or the ErrorLayer is from a different version of + /// tracing_error + pub const UNSUPPORTED: SpanTraceStatus = SpanTraceStatus(SpanTraceStatusInner::Unsupported); + + /// The SpanTrace is empty, likely because it was captured outside of any + /// `span`s + pub const EMPTY: SpanTraceStatus = SpanTraceStatus(SpanTraceStatusInner::Empty); + + /// A span trace has been captured and the `SpanTrace` should print + /// reasonable information when rendered. + pub const CAPTURED: SpanTraceStatus = SpanTraceStatus(SpanTraceStatusInner::Captured); +} + +#[derive(Debug, PartialEq, Eq)] +enum SpanTraceStatusInner { + Unsupported, + Empty, + Captured, +} + +macro_rules! try_bool { + ($e:expr, $dest:ident) => {{ + let ret = $e.unwrap_or_else(|e| $dest = Err(e)); + + if $dest.is_err() { + return false; + } + + ret + }}; +} + +impl fmt::Display for SpanTrace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut err = Ok(()); + let mut span = 0; + + self.with_spans(|metadata, fields| { + if span > 0 { + try_bool!(write!(f, "\n",), err); + } + + try_bool!( + write!(f, "{:>4}: {}::{}", span, metadata.target(), metadata.name()), + err + ); + + if !fields.is_empty() { + try_bool!(write!(f, "\n with {}", fields), err); + } + + if let Some((file, line)) = metadata + .file() + .and_then(|file| metadata.line().map(|line| (file, line))) + { + try_bool!(write!(f, "\n at {}:{}", file, line), err); + } + + span += 1; + true + }); + + err + } +} + +impl fmt::Debug for SpanTrace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugSpan<'a> { + metadata: &'a Metadata<'a>, + fields: &'a str, + } + + impl<'a> fmt::Debug for DebugSpan<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{{ target: {:?}, name: {:?}", + self.metadata.target(), + self.metadata.name() + )?; + + if !self.fields.is_empty() { + write!(f, ", fields: {:?}", self.fields)?; + } + + if let Some((file, line)) = self + .metadata + .file() + .and_then(|file| self.metadata.line().map(|line| (file, line))) + { + write!(f, ", file: {:?}, line: {:?}", file, line)?; + } + + write!(f, " }}")?; + + Ok(()) + } + } + + write!(f, "SpanTrace ")?; + let mut dbg = f.debug_list(); + self.with_spans(|metadata, fields| { + dbg.entry(&DebugSpan { metadata, fields }); + true + }); + dbg.finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ErrorLayer; + use tracing::subscriber::with_default; + use tracing::{span, Level}; + use tracing_subscriber::{prelude::*, registry::Registry}; + + #[test] + fn capture_supported() { + let subscriber = Registry::default().with(ErrorLayer::default()); + + with_default(subscriber, || { + let span = span!(Level::ERROR, "test span"); + let _guard = span.enter(); + + let span_trace = SpanTrace::capture(); + + dbg!(&span_trace); + + assert_eq!(SpanTraceStatus::CAPTURED, span_trace.status()) + }); + } + + #[test] + fn capture_empty() { + let subscriber = Registry::default().with(ErrorLayer::default()); + + with_default(subscriber, || { + let span_trace = SpanTrace::capture(); + + dbg!(&span_trace); + + assert_eq!(SpanTraceStatus::EMPTY, span_trace.status()) + }); + } + + #[test] + fn capture_unsupported() { + let subscriber = Registry::default(); + + with_default(subscriber, || { + let span = span!(Level::ERROR, "test span"); + let _guard = span.enter(); + + let span_trace = SpanTrace::capture(); + + dbg!(&span_trace); + + assert_eq!(SpanTraceStatus::UNSUPPORTED, span_trace.status()) + }); + } +} diff --git a/vendor/tracing-error/src/error.rs b/vendor/tracing-error/src/error.rs new file mode 100644 index 000000000..fea729625 --- /dev/null +++ b/vendor/tracing-error/src/error.rs @@ -0,0 +1,289 @@ +use crate::SpanTrace; +use std::error::Error; +use std::fmt::{self, Debug, Display}; + +struct Erased; + +/// A wrapper type for `Error`s that bundles a `SpanTrace` with an inner `Error` +/// type. +/// +/// This type is a good match for the error-kind pattern where you have an error +/// type with an inner enum of error variants and you would like to capture a +/// span trace that can be extracted during printing without formatting the span +/// trace as part of your display impl. +/// +/// An example of implementing an error type for a library using `TracedError` +/// might look like this +/// +/// ```rust,compile_fail +/// #[derive(Debug, thiserror::Error)] +/// enum Kind { +/// // ... +/// } +/// +/// #[derive(Debug)] +/// pub struct Error { +/// source: TracedError<Kind>, +/// backtrace: Backtrace, +/// } +/// +/// impl std::error::Error for Error { +/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { +/// self.source.source() +/// } +/// +/// fn backtrace(&self) -> Option<&Backtrace> { +/// Some(&self.backtrace) +/// } +/// } +/// +/// impl fmt::Display for Error { +/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { +/// fmt::Display::fmt(&self.source, fmt) +/// } +/// } +/// +/// impl<E> From<E> for Error +/// where +/// Kind: From<E>, +/// { +/// fn from(source: E) -> Self { +/// Self { +/// source: Kind::from(source).into(), +/// backtrace: Backtrace::capture(), +/// } +/// } +/// } +/// ``` +#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))] +pub struct TracedError<E> { + inner: ErrorImpl<E>, +} + +impl<E> From<E> for TracedError<E> +where + E: Error + Send + Sync + 'static, +{ + fn from(error: E) -> Self { + // # SAFETY + // + // This function + the repr(C) on the ErrorImpl make the type erasure throughout the rest + // of this struct's methods safe. This saves a function pointer that is parameterized on the Error type + // being stored inside the ErrorImpl. This lets the object_ref function safely cast a type + // erased `ErrorImpl` back to its original type, which is needed in order to forward our + // error/display/debug impls to the internal error type from the type erased error type. + // + // The repr(C) is necessary to ensure that the struct is layed out in the order we + // specified it so that we can safely access the vtable and spantrace fields thru a type + // erased pointer to the original object. + let vtable = &ErrorVTable { + object_ref: object_ref::<E>, + }; + + Self { + inner: ErrorImpl { + vtable, + span_trace: SpanTrace::capture(), + error, + }, + } + } +} + +#[repr(C)] +struct ErrorImpl<E> { + vtable: &'static ErrorVTable, + span_trace: SpanTrace, + // NOTE: Don't use directly. Use only through vtable. Erased type may have + // different alignment. + error: E, +} + +impl ErrorImpl<Erased> { + pub(crate) fn error(&self) -> &(dyn Error + Send + Sync + 'static) { + // # SAFETY + // + // this function is used to cast a type-erased pointer to a pointer to error's + // original type. the `ErrorImpl::error` method, which calls this function, requires that + // the type this function casts to be the original erased type of the error; failure to + // uphold this is UB. since the `From` impl is parameterized over the original error type, + // the function pointer we construct here will also retain the original type. therefore, + // when this is consumed by the `error` method, it will be safe to call. + unsafe { &*(self.vtable.object_ref)(self) } + } +} + +struct ErrorVTable { + object_ref: unsafe fn(&ErrorImpl<Erased>) -> &(dyn Error + Send + Sync + 'static), +} + +// # SAFETY +// +// This function must be parameterized on the type E of the original error that is being stored +// inside of the `ErrorImpl`. When it is parameterized by the correct type, it safely +// casts the erased `ErrorImpl` pointer type back to the original pointer type. +unsafe fn object_ref<E>(e: &ErrorImpl<Erased>) -> &(dyn Error + Send + Sync + 'static) +where + E: Error + Send + Sync + 'static, +{ + // Attach E's native Error vtable onto a pointer to e.error. + &(*(e as *const ErrorImpl<Erased> as *const ErrorImpl<E>)).error +} + +impl<E> Error for TracedError<E> +where + E: std::error::Error + 'static, +{ + // # SAFETY + // + // This function is safe so long as all functions on `ErrorImpl<Erased>` uphold the invariant + // that the wrapped error is only ever accessed by the `error` method. This method uses the + // function in the vtable to safely convert the pointer type back to the original type, and + // then returns the reference to the erased error. + // + // This function is necessary for the `downcast_ref` in `ExtractSpanTrace` to work, because it + // needs a concrete type to downcast to and we cannot downcast to ErrorImpls parameterized on + // errors defined in other crates. By erasing the type here we can always cast back to the + // Erased version of the ErrorImpl pointer, and still access the internal error type safely + // through the vtable. + fn source<'a>(&'a self) -> Option<&'a (dyn Error + 'static)> { + let erased = unsafe { &*(&self.inner as *const ErrorImpl<E> as *const ErrorImpl<Erased>) }; + Some(erased) + } +} + +impl<E> Debug for TracedError<E> +where + E: std::error::Error, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.inner.error, f) + } +} + +impl<E> Display for TracedError<E> +where + E: std::error::Error, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.inner.error, f) + } +} + +impl Error for ErrorImpl<Erased> { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error().source() + } +} + +impl Debug for ErrorImpl<Erased> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("span backtrace:\n")?; + Debug::fmt(&self.span_trace, f) + } +} + +impl Display for ErrorImpl<Erased> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("span backtrace:\n")?; + Display::fmt(&self.span_trace, f) + } +} + +/// Extension trait for instrumenting errors with `SpanTrace`s +#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))] +pub trait InstrumentError { + /// The type of the wrapped error after instrumentation + type Instrumented; + + /// Instrument an Error by bundling it with a SpanTrace + /// + /// # Examples + /// + /// ```rust + /// use tracing_error::{TracedError, InstrumentError}; + /// + /// fn wrap_error<E>(e: E) -> TracedError<E> + /// where + /// E: std::error::Error + Send + Sync + 'static + /// { + /// e.in_current_span() + /// } + /// ``` + fn in_current_span(self) -> Self::Instrumented; +} + +/// Extension trait for instrumenting errors in `Result`s with `SpanTrace`s +#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))] +pub trait InstrumentResult<T> { + /// The type of the wrapped error after instrumentation + type Instrumented; + + /// Instrument an Error by bundling it with a SpanTrace + /// + /// # Examples + /// + /// ```rust + /// # use std::{io, fs}; + /// use tracing_error::{TracedError, InstrumentResult}; + /// + /// # fn fallible_fn() -> io::Result<()> { fs::read_dir("......").map(drop) }; + /// + /// fn do_thing() -> Result<(), TracedError<io::Error>> { + /// fallible_fn().in_current_span() + /// } + /// ``` + fn in_current_span(self) -> Result<T, Self::Instrumented>; +} + +impl<T, E> InstrumentResult<T> for Result<T, E> +where + E: InstrumentError, +{ + type Instrumented = <E as InstrumentError>::Instrumented; + + fn in_current_span(self) -> Result<T, Self::Instrumented> { + self.map_err(E::in_current_span) + } +} + +/// A trait for extracting SpanTraces created by `in_current_span()` from `dyn +/// Error` trait objects +#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))] +pub trait ExtractSpanTrace { + /// Attempts to downcast to a `TracedError` and return a reference to its + /// SpanTrace + /// + /// # Examples + /// + /// ```rust + /// use tracing_error::ExtractSpanTrace; + /// use std::error::Error; + /// + /// fn print_span_trace(e: &(dyn Error + 'static)) { + /// let span_trace = e.span_trace(); + /// if let Some(span_trace) = span_trace { + /// println!("{}", span_trace); + /// } + /// } + /// ``` + fn span_trace(&self) -> Option<&SpanTrace>; +} + +impl<E> InstrumentError for E +where + TracedError<E>: From<E>, +{ + type Instrumented = TracedError<E>; + + fn in_current_span(self) -> Self::Instrumented { + TracedError::from(self) + } +} + +impl ExtractSpanTrace for dyn Error + 'static { + fn span_trace(&self) -> Option<&SpanTrace> { + self.downcast_ref::<ErrorImpl<Erased>>() + .map(|inner| &inner.span_trace) + } +} diff --git a/vendor/tracing-error/src/layer.rs b/vendor/tracing-error/src/layer.rs new file mode 100644 index 000000000..14c019258 --- /dev/null +++ b/vendor/tracing-error/src/layer.rs @@ -0,0 +1,132 @@ +use std::any::{type_name, TypeId}; +use std::fmt; +use std::marker::PhantomData; +use tracing::{span, Dispatch, Metadata, Subscriber}; +use tracing_subscriber::fmt::format::{DefaultFields, FormatFields}; +use tracing_subscriber::{ + fmt::FormattedFields, + layer::{self, Layer}, + registry::LookupSpan, +}; + +/// A subscriber [`Layer`] that enables capturing [`SpanTrace`]s. +/// +/// Optionally, this type may be constructed with a [field formatter] to use +/// when formatting the fields of each span in a trace. When no formatter is +/// provided, the [default format] is used instead. +/// +/// [`Layer`]: https://docs.rs/tracing-subscriber/0.3/tracing_subscriber/layer/trait.Layer.html +/// [`SpanTrace`]: ../struct.SpanTrace.html +/// [field formatter]: https://docs.rs/tracing-subscriber/0.3/tracing_subscriber/fmt/trait.FormatFields.html +/// [default format]: https://docs.rs/tracing-subscriber/0.3/tracing_subscriber/fmt/format/struct.DefaultFields.html +pub struct ErrorLayer<S, F = DefaultFields> { + format: F, + + get_context: WithContext, + _subscriber: PhantomData<fn(S)>, +} + +// this function "remembers" the types of the subscriber and the formatter, +// so that we can downcast to something aware of them without knowing those +// types at the callsite. +pub(crate) struct WithContext( + fn(&Dispatch, &span::Id, f: &mut dyn FnMut(&'static Metadata<'static>, &str) -> bool), +); + +impl<S, F> Layer<S> for ErrorLayer<S, F> +where + S: Subscriber + for<'span> LookupSpan<'span>, + F: for<'writer> FormatFields<'writer> + 'static, +{ + /// Notifies this layer that a new span was constructed with the given + /// `Attributes` and `Id`. + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) { + let span = ctx.span(id).expect("span must already exist!"); + if span.extensions().get::<FormattedFields<F>>().is_some() { + return; + } + let mut fields = FormattedFields::<F>::new(String::new()); + if self.format.format_fields(fields.as_writer(), attrs).is_ok() { + span.extensions_mut().insert(fields); + } + } + + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + match id { + id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()), + id if id == TypeId::of::<WithContext>() => { + Some(&self.get_context as *const _ as *const ()) + } + _ => None, + } + } +} + +impl<S, F> ErrorLayer<S, F> +where + F: for<'writer> FormatFields<'writer> + 'static, + S: Subscriber + for<'span> LookupSpan<'span>, +{ + /// Returns a new `ErrorLayer` with the provided [field formatter]. + /// + /// [field formatter]: https://docs.rs/tracing-subscriber/0.2.10/tracing_subscriber/fmt/trait.FormatFields.html + pub fn new(format: F) -> Self { + Self { + format, + get_context: WithContext(Self::get_context), + _subscriber: PhantomData, + } + } + + fn get_context( + dispatch: &Dispatch, + id: &span::Id, + f: &mut dyn FnMut(&'static Metadata<'static>, &str) -> bool, + ) { + let subscriber = dispatch + .downcast_ref::<S>() + .expect("subscriber should downcast to expected type; this is a bug!"); + let span = subscriber + .span(id) + .expect("registry should have a span for the current ID"); + for span in span.scope() { + let cont = if let Some(fields) = span.extensions().get::<FormattedFields<F>>() { + f(span.metadata(), fields.fields.as_str()) + } else { + f(span.metadata(), "") + }; + if !cont { + break; + } + } + } +} + +impl WithContext { + pub(crate) fn with_context<'a>( + &self, + dispatch: &'a Dispatch, + id: &span::Id, + mut f: impl FnMut(&'static Metadata<'static>, &str) -> bool, + ) { + (self.0)(dispatch, id, &mut f) + } +} + +impl<S> Default for ErrorLayer<S> +where + S: Subscriber + for<'span> LookupSpan<'span>, +{ + fn default() -> Self { + Self::new(DefaultFields::default()) + } +} + +impl<S, F: fmt::Debug> fmt::Debug for ErrorLayer<S, F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ErrorLayer") + .field("format", &self.format) + .field("subscriber", &format_args!("{}", type_name::<S>())) + .finish() + } +} diff --git a/vendor/tracing-error/src/lib.rs b/vendor/tracing-error/src/lib.rs new file mode 100644 index 000000000..611bc9f79 --- /dev/null +++ b/vendor/tracing-error/src/lib.rs @@ -0,0 +1,236 @@ +//! Utilities for enriching error handling with [`tracing`] diagnostic +//! information. +//! +//! # Overview +//! +//! [`tracing`] is a framework for instrumenting Rust programs to collect +//! scoped, structured, and async-aware diagnostics. This crate provides +//! integrations between [`tracing`] instrumentation and Rust error handling. It +//! enables enriching error types with diagnostic information from `tracing` +//! [span] contexts, formatting those contexts when errors are displayed, and +//! automatically generate `tracing` [events] when errors occur. +//! +//! The crate provides the following: +//! +//! * [`SpanTrace`], a captured trace of the current `tracing` [span] context +//! +//! * [`ErrorLayer`], a [subscriber layer] which enables capturing `SpanTrace`s +//! +//! **Note**: This crate is currently experimental. +//! +//! *Compiler support: [requires `rustc` 1.42+][msrv]* +//! +//! [msrv]: #supported-rust-versions +//! +//! ## Feature Flags +//! +//! - `traced-error` - Enables the [`TracedError`] type and related Traits +//! - [`InstrumentResult`] and [`InstrumentError`] extension traits, which +//! provide an [`in_current_span()`] method for bundling errors with a +//! [`SpanTrace`]. +//! - [`ExtractSpanTrace`] extension trait, for extracting `SpanTrace`s from +//! behind `dyn Error` trait objects. +//! +//! ## Usage +//! +//! `tracing-error` provides the [`SpanTrace`] type, which captures the current +//! `tracing` span context when it is constructed and allows it to be displayed +//! at a later time. +//! +//! For example: +//! +//! ```rust +//! use std::{fmt, error::Error}; +//! use tracing_error::SpanTrace; +//! +//! #[derive(Debug)] +//! pub struct MyError { +//! context: SpanTrace, +//! // ... +//! } +//! +//! impl fmt::Display for MyError { +//! fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +//! // ... format other parts of the error ... +//! +//! self.context.fmt(f)?; +//! +//! // ... format other error context information, cause chain, etc ... +//! # Ok(()) +//! } +//! } +//! +//! impl Error for MyError {} +//! +//! impl MyError { +//! pub fn new() -> Self { +//! Self { +//! context: SpanTrace::capture(), +//! // ... other error information ... +//! } +//! } +//! } +//! ``` +//! +//! This crate also provides [`TracedError`], for attaching a [`SpanTrace`] to +//! an existing error. The easiest way to wrap errors in `TracedError` is to +//! either use the [`InstrumentResult`] and [`InstrumentError`] traits or the +//! `From`/`Into` traits. +//! +//! ```rust +//! # use std::error::Error; +//! use tracing_error::prelude::*; +//! +//! # fn fake_main() -> Result<(), Box<dyn Error>> { +//! std::fs::read_to_string("myfile.txt").in_current_span()?; +//! # Ok(()) +//! # } +//! ``` +//! +//! Once an error has been wrapped with with a [`TracedError`] the [`SpanTrace`] +//! can be extracted one of 3 ways: either via [`TracedError`]'s +//! `Display`/`Debug` implementations, or via the [`ExtractSpanTrace`] trait. +//! +//! For example, here is how one might print the errors but specialize the +//! printing when the error is a placeholder for a wrapping [`SpanTrace`]: +//! +//! ```rust +//! use std::error::Error; +//! use tracing_error::ExtractSpanTrace as _; +//! +//! fn print_extracted_spantraces(error: &(dyn Error + 'static)) { +//! let mut error = Some(error); +//! let mut ind = 0; +//! +//! eprintln!("Error:"); +//! +//! while let Some(err) = error { +//! if let Some(spantrace) = err.span_trace() { +//! eprintln!("found a spantrace:\n{}", spantrace); +//! } else { +//! eprintln!("{:>4}: {}", ind, err); +//! } +//! +//! error = err.source(); +//! ind += 1; +//! } +//! } +//! +//! ``` +//! +//! Whereas here, we can still display the content of the `SpanTraces` without +//! any special casing by simply printing all errors in our error chain. +//! +//! ```rust +//! use std::error::Error; +//! +//! fn print_naive_spantraces(error: &(dyn Error + 'static)) { +//! let mut error = Some(error); +//! let mut ind = 0; +//! +//! eprintln!("Error:"); +//! +//! while let Some(err) = error { +//! eprintln!("{:>4}: {}", ind, err); +//! error = err.source(); +//! ind += 1; +//! } +//! } +//! ``` +//! +//! Applications that wish to use `tracing-error`-enabled errors should +//! construct an [`ErrorLayer`] and add it to their [`Subscriber`] in order to +//! enable capturing [`SpanTrace`]s. For example: +//! +//! ```rust +//! use tracing_error::ErrorLayer; +//! use tracing_subscriber::prelude::*; +//! +//! fn main() { +//! let subscriber = tracing_subscriber::Registry::default() +//! // any number of other subscriber layers may be added before or +//! // after the `ErrorLayer`... +//! .with(ErrorLayer::default()); +//! +//! // set the subscriber as the default for the application +//! tracing::subscriber::set_global_default(subscriber); +//! } +//! ``` +//! +//! [`SpanTrace`]: struct.SpanTrace.html +//! [`ErrorLayer`]: struct.ErrorLayer.html +//! [`TracedError`]: struct.TracedError.html +//! [`InstrumentResult`]: trait.InstrumentResult.html +//! [`InstrumentError`]: trait.InstrumentError.html +//! [`ExtractSpanTrace`]: trait.ExtractSpanTrace.html +//! [`in_current_span()`]: trait.InstrumentResult.html#tymethod.in_current_span +//! [span]: https://docs.rs/tracing/latest/tracing/span/index.html +//! [events]: https://docs.rs/tracing/latest/tracing/struct.Event.html +//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html +//! [subscriber layer]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html +//! [`tracing`]: https://docs.rs/tracing +//! [`std::error::Error`]: https://doc.rust-lang.org/stable/std/error/trait.Error.html +//! +//! ## Supported Rust Versions +//! +//! Tracing is built against the latest stable release. The minimum supported +//! version is 1.42. The current Tracing version is not guaranteed to build on +//! Rust versions earlier than the minimum supported version. +//! +//! Tracing follows the same compiler support policies as the rest of the Tokio +//! project. The current stable Rust compiler and the three most recent minor +//! versions before it will always be supported. For example, if the current +//! stable compiler version is 1.45, the minimum supported version will not be +//! increased past 1.42, three minor versions prior. Increasing the minimum +//! supported compiler version is not considered a semver breaking change as +//! long as doing so complies with this policy. +//! +#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))] +#![doc(html_root_url = "https://docs.rs/tracing-error/0.2.0")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", + issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/" +)] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + unreachable_pub, + bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true +)] +mod backtrace; +#[cfg(feature = "traced-error")] +mod error; +mod layer; + +pub use self::backtrace::{SpanTrace, SpanTraceStatus}; +#[cfg(feature = "traced-error")] +pub use self::error::{ExtractSpanTrace, InstrumentError, InstrumentResult, TracedError}; +pub use self::layer::ErrorLayer; + +#[cfg(feature = "traced-error")] +#[cfg_attr(docsrs, doc(cfg(feature = "traced-error")))] +pub mod prelude { + //! The `tracing-error` prelude. + //! + //! This brings into scope the `InstrumentError, `InstrumentResult`, and `ExtractSpanTrace` + //! extension traits. These traits allow attaching `SpanTrace`s to errors and + //! subsequently retrieving them from `dyn Error` trait objects. + + pub use crate::{ExtractSpanTrace as _, InstrumentError as _, InstrumentResult as _}; +} |