| 1 | //! Utilities for enriching error handling with [`tracing`] diagnostic |
| 2 | //! information. |
| 3 | //! |
| 4 | //! # Overview |
| 5 | //! |
| 6 | //! [`tracing`] is a framework for instrumenting Rust programs to collect |
| 7 | //! scoped, structured, and async-aware diagnostics. This crate provides |
| 8 | //! integrations between [`tracing`] instrumentation and Rust error handling. It |
| 9 | //! enables enriching error types with diagnostic information from `tracing` |
| 10 | //! [span] contexts, formatting those contexts when errors are displayed, and |
| 11 | //! automatically generate `tracing` [events] when errors occur. |
| 12 | //! |
| 13 | //! The crate provides the following: |
| 14 | //! |
| 15 | //! * [`SpanTrace`], a captured trace of the current `tracing` [span] context |
| 16 | //! |
| 17 | //! * [`ErrorLayer`], a [subscriber layer] which enables capturing `SpanTrace`s |
| 18 | //! |
| 19 | //! **Note**: This crate is currently experimental. |
| 20 | //! |
| 21 | //! *Compiler support: [requires `rustc` 1.63+][msrv]* |
| 22 | //! |
| 23 | //! [msrv]: #supported-rust-versions |
| 24 | //! |
| 25 | //! ## Feature Flags |
| 26 | //! |
| 27 | //! - `traced-error` - Enables the [`TracedError`] type and related Traits |
| 28 | //! - [`InstrumentResult`] and [`InstrumentError`] extension traits, which |
| 29 | //! provide an [`in_current_span()`] method for bundling errors with a |
| 30 | //! [`SpanTrace`]. |
| 31 | //! - [`ExtractSpanTrace`] extension trait, for extracting `SpanTrace`s from |
| 32 | //! behind `dyn Error` trait objects. |
| 33 | //! |
| 34 | //! ## Usage |
| 35 | //! |
| 36 | //! `tracing-error` provides the [`SpanTrace`] type, which captures the current |
| 37 | //! `tracing` span context when it is constructed and allows it to be displayed |
| 38 | //! at a later time. |
| 39 | //! |
| 40 | //! For example: |
| 41 | //! |
| 42 | //! ```rust |
| 43 | //! use std::{fmt, error::Error}; |
| 44 | //! use tracing_error::SpanTrace; |
| 45 | //! |
| 46 | //! #[derive(Debug)] |
| 47 | //! pub struct MyError { |
| 48 | //! context: SpanTrace, |
| 49 | //! // ... |
| 50 | //! } |
| 51 | //! |
| 52 | //! impl fmt::Display for MyError { |
| 53 | //! fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 54 | //! // ... format other parts of the error ... |
| 55 | //! |
| 56 | //! self.context.fmt(f)?; |
| 57 | //! |
| 58 | //! // ... format other error context information, cause chain, etc ... |
| 59 | //! # Ok(()) |
| 60 | //! } |
| 61 | //! } |
| 62 | //! |
| 63 | //! impl Error for MyError {} |
| 64 | //! |
| 65 | //! impl MyError { |
| 66 | //! pub fn new() -> Self { |
| 67 | //! Self { |
| 68 | //! context: SpanTrace::capture(), |
| 69 | //! // ... other error information ... |
| 70 | //! } |
| 71 | //! } |
| 72 | //! } |
| 73 | //! ``` |
| 74 | //! |
| 75 | //! This crate also provides [`TracedError`], for attaching a [`SpanTrace`] to |
| 76 | //! an existing error. The easiest way to wrap errors in `TracedError` is to |
| 77 | //! either use the [`InstrumentResult`] and [`InstrumentError`] traits or the |
| 78 | //! `From`/`Into` traits. |
| 79 | //! |
| 80 | //! ```rust |
| 81 | //! # use std::error::Error; |
| 82 | //! use tracing_error::prelude::*; |
| 83 | //! |
| 84 | //! # fn fake_main() -> Result<(), Box<dyn Error>> { |
| 85 | //! std::fs::read_to_string("myfile.txt" ).in_current_span()?; |
| 86 | //! # Ok(()) |
| 87 | //! # } |
| 88 | //! ``` |
| 89 | //! |
| 90 | //! Once an error has been wrapped with with a [`TracedError`] the [`SpanTrace`] |
| 91 | //! can be extracted one of 3 ways: either via [`TracedError`]'s |
| 92 | //! `Display`/`Debug` implementations, or via the [`ExtractSpanTrace`] trait. |
| 93 | //! |
| 94 | //! For example, here is how one might print the errors but specialize the |
| 95 | //! printing when the error is a placeholder for a wrapping [`SpanTrace`]: |
| 96 | //! |
| 97 | //! ```rust |
| 98 | //! use std::error::Error; |
| 99 | //! use tracing_error::ExtractSpanTrace as _; |
| 100 | //! |
| 101 | //! fn print_extracted_spantraces(error: &(dyn Error + 'static)) { |
| 102 | //! let mut error = Some(error); |
| 103 | //! let mut ind = 0; |
| 104 | //! |
| 105 | //! eprintln!("Error:" ); |
| 106 | //! |
| 107 | //! while let Some(err) = error { |
| 108 | //! if let Some(spantrace) = err.span_trace() { |
| 109 | //! eprintln!("found a spantrace: \n{}" , spantrace); |
| 110 | //! } else { |
| 111 | //! eprintln!("{:>4}: {}" , ind, err); |
| 112 | //! } |
| 113 | //! |
| 114 | //! error = err.source(); |
| 115 | //! ind += 1; |
| 116 | //! } |
| 117 | //! } |
| 118 | //! |
| 119 | //! ``` |
| 120 | //! |
| 121 | //! Whereas here, we can still display the content of the `SpanTraces` without |
| 122 | //! any special casing by simply printing all errors in our error chain. |
| 123 | //! |
| 124 | //! ```rust |
| 125 | //! use std::error::Error; |
| 126 | //! |
| 127 | //! fn print_naive_spantraces(error: &(dyn Error + 'static)) { |
| 128 | //! let mut error = Some(error); |
| 129 | //! let mut ind = 0; |
| 130 | //! |
| 131 | //! eprintln!("Error:" ); |
| 132 | //! |
| 133 | //! while let Some(err) = error { |
| 134 | //! eprintln!("{:>4}: {}" , ind, err); |
| 135 | //! error = err.source(); |
| 136 | //! ind += 1; |
| 137 | //! } |
| 138 | //! } |
| 139 | //! ``` |
| 140 | //! |
| 141 | //! Applications that wish to use `tracing-error`-enabled errors should |
| 142 | //! construct an [`ErrorLayer`] and add it to their [`Subscriber`] in order to |
| 143 | //! enable capturing [`SpanTrace`]s. For example: |
| 144 | //! |
| 145 | //! ```rust |
| 146 | //! use tracing_error::ErrorLayer; |
| 147 | //! use tracing_subscriber::prelude::*; |
| 148 | //! |
| 149 | //! fn main() { |
| 150 | //! let subscriber = tracing_subscriber::Registry::default() |
| 151 | //! // any number of other subscriber layers may be added before or |
| 152 | //! // after the `ErrorLayer`... |
| 153 | //! .with(ErrorLayer::default()); |
| 154 | //! |
| 155 | //! // set the subscriber as the default for the application |
| 156 | //! tracing::subscriber::set_global_default(subscriber); |
| 157 | //! } |
| 158 | //! ``` |
| 159 | //! |
| 160 | //! [`in_current_span()`]: InstrumentResult#tymethod.in_current_span |
| 161 | //! [span]: mod@tracing::span |
| 162 | //! [events]: tracing::Event |
| 163 | //! [`Subscriber`]: tracing::Subscriber |
| 164 | //! [subscriber layer]: tracing_subscriber::layer::Layer |
| 165 | //! [`tracing`]: tracing |
| 166 | //! |
| 167 | //! ## Supported Rust Versions |
| 168 | //! |
| 169 | //! Tracing is built against the latest stable release. The minimum supported |
| 170 | //! version is 1.63. The current Tracing version is not guaranteed to build on |
| 171 | //! Rust versions earlier than the minimum supported version. |
| 172 | //! |
| 173 | //! Tracing follows the same compiler support policies as the rest of the Tokio |
| 174 | //! project. The current stable Rust compiler and the three most recent minor |
| 175 | //! versions before it will always be supported. For example, if the current |
| 176 | //! stable compiler version is 1.69, the minimum supported version will not be |
| 177 | //! increased past 1.66, three minor versions prior. Increasing the minimum |
| 178 | //! supported compiler version is not considered a semver breaking change as |
| 179 | //! long as doing so complies with this policy. |
| 180 | //! |
| 181 | #![cfg_attr (docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))] |
| 182 | #![doc ( |
| 183 | html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png" , |
| 184 | issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/" |
| 185 | )] |
| 186 | #![allow (clippy::needless_doctest_main)] |
| 187 | #![warn ( |
| 188 | missing_debug_implementations, |
| 189 | missing_docs, |
| 190 | rust_2018_idioms, |
| 191 | unreachable_pub, |
| 192 | bad_style, |
| 193 | dead_code, |
| 194 | improper_ctypes, |
| 195 | non_shorthand_field_patterns, |
| 196 | no_mangle_generic_items, |
| 197 | overflowing_literals, |
| 198 | path_statements, |
| 199 | patterns_in_fns_without_body, |
| 200 | private_interfaces, |
| 201 | private_bounds, |
| 202 | unconditional_recursion, |
| 203 | unused, |
| 204 | unused_allocation, |
| 205 | unused_comparisons, |
| 206 | unused_parens, |
| 207 | while_true |
| 208 | )] |
| 209 | mod backtrace; |
| 210 | #[cfg (feature = "traced-error" )] |
| 211 | mod error; |
| 212 | mod layer; |
| 213 | |
| 214 | pub use self::backtrace::{SpanTrace, SpanTraceStatus}; |
| 215 | #[cfg (feature = "traced-error" )] |
| 216 | pub use self::error::{ExtractSpanTrace, InstrumentError, InstrumentResult, TracedError}; |
| 217 | pub use self::layer::ErrorLayer; |
| 218 | |
| 219 | #[cfg (feature = "traced-error" )] |
| 220 | #[cfg_attr (docsrs, doc(cfg(feature = "traced-error" )))] |
| 221 | pub mod prelude { |
| 222 | //! The `tracing-error` prelude. |
| 223 | //! |
| 224 | //! This brings into scope the `InstrumentError`, `InstrumentResult`, and `ExtractSpanTrace` |
| 225 | //! extension traits. These traits allow attaching `SpanTrace`s to errors and |
| 226 | //! subsequently retrieving them from `dyn Error` trait objects. |
| 227 | |
| 228 | // apparently `as _` reexpoorts now generate `unreachable_pub` linting? which |
| 229 | // seems wrong to me... |
| 230 | #![allow (unreachable_pub)] |
| 231 | pub use crate::{ExtractSpanTrace as _, InstrumentError as _, InstrumentResult as _}; |
| 232 | } |
| 233 | |