mirror std backtrace capture rules (#14)

This commit is contained in:
Jane Lusby 2020-05-17 06:45:19 -07:00 committed by GitHub
parent 496a17ec61
commit ad8d7cc415
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 164 additions and 10 deletions

View File

@ -21,6 +21,19 @@ jobs:
command: check
test:
name: Test Suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
test-features:
name: Test Suite
runs-on: ubuntu-latest
strategy:
@ -37,6 +50,11 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: test
args: --all-features
- uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features
fmt:
name: Rustfmt

View File

@ -27,3 +27,7 @@ color-spantrace = { version = "0.1.1", optional = true }
[dev-dependencies]
tracing-subscriber = "0.2.5"
tracing = "0.1.13"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

View File

@ -41,11 +41,21 @@
//! use color_eyre::{Help, Report};
//! use eyre::WrapErr;
//! use tracing::{info, instrument};
//! use tracing_error::ErrorLayer;
//! use tracing_subscriber::prelude::*;
//! use tracing_subscriber::{fmt, EnvFilter};
//!
//! #[instrument]
//! fn main() -> Result<(), Report> {
//! #[cfg(feature = "capture-spantrace")]
//! install_tracing();
//!
//! Ok(read_config()?)
//! }
//!
//! #[cfg(feature = "capture-spantrace")]
//! fn install_tracing() {
//! use tracing_error::ErrorLayer;
//! use tracing_subscriber::prelude::*;
//! use tracing_subscriber::{fmt, EnvFilter};
//!
//! let fmt_layer = fmt::layer().with_target(false);
//! let filter_layer = EnvFilter::try_from_default_env()
//! .or_else(|_| EnvFilter::try_new("info"))
@ -56,8 +66,6 @@
//! .with(fmt_layer)
//! .with(ErrorLayer::default())
//! .init();
//!
//! Ok(read_config()?)
//! }
//!
//! #[instrument]
@ -99,6 +107,8 @@
//! pub type Result<T, E = Report> = core::result::Result<T, E>;
//! ```
//!
//! Please refer to the [`Context`] type's docs for more details about its feature set.
//!
//! ## Features
//!
//! - captures a [`backtrace::Backtrace`] and prints using [`color-backtrace`]
@ -119,7 +129,9 @@
//! [`Help`]: trait.Help.html
//! [`eyre::Report`]: https://docs.rs/eyre/0.3.8/eyre/struct.Report.html
//! [`eyre::Result`]: https://docs.rs/eyre/0.3.8/eyre/type.Result.html
//! [`Context`]: struct.Context.html
#![doc(html_root_url = "https://docs.rs/color-eyre/0.3.0")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(
missing_debug_implementations,
missing_docs,
@ -151,7 +163,11 @@ use help::HelpInfo;
use indenter::{indented, Format};
#[cfg(feature = "capture-spantrace")]
use std::error::Error;
use std::fmt::Write as _;
use std::{
env,
fmt::Write as _,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
#[cfg(feature = "capture-spantrace")]
use tracing_error::{ExtractSpanTrace, SpanTrace, SpanTraceStatus};
@ -175,13 +191,108 @@ pub struct Context {
help: Vec<HelpInfo>,
}
impl Context {
/// Return a reference to the captured `Backtrace` type
///
/// # Examples
///
/// Backtrace capture can be enabled with the `RUST_BACKTRACE` env variable:
///
/// ```
/// use color_eyre::Report;
/// use eyre::eyre;
///
/// std::env::set_var("RUST_BACKTRACE", "1");
///
/// let report: Report = eyre!("an error occurred");
/// assert!(report.context().backtrace().is_some());
/// ```
///
/// Alternatively, if you don't want backtraces to be printed on panic, you can use
/// `RUST_LIB_BACKTRACE`:
///
/// ```
/// use color_eyre::Report;
/// use eyre::eyre;
///
/// std::env::set_var("RUST_LIB_BACKTRACE", "1");
///
/// let report: Report = eyre!("an error occurred");
/// assert!(report.context().backtrace().is_some());
/// ```
///
/// And if you don't want backtraces to be captured but you still want panics to print
/// backtraces you can explicitly set `RUST_LIB_BACKTRACE` to 0:
///
/// ```
/// use color_eyre::Report;
/// use eyre::eyre;
///
/// std::env::set_var("RUST_BACKTRACE", "1");
/// std::env::set_var("RUST_LIB_BACKTRACE", "0");
///
/// let report: Report = eyre!("an error occurred");
/// assert!(report.context().backtrace().is_none());
/// ```
///
pub fn backtrace(&self) -> Option<&Backtrace> {
self.backtrace.as_ref()
}
/// Return a reference to the captured `SpanTrace` type
///
/// # Examples
///
/// SpanTraces are always captured by default:
///
/// ```
/// use color_eyre::Report;
/// use eyre::eyre;
///
/// let report: Report = eyre!("an error occurred");
/// assert!(report.context().span_trace().is_some());
/// ```
///
/// However, `SpanTrace` is not captured if one of the source errors already captured a
/// `SpanTrace` via [`tracing_error::TracedError`]:
///
/// ```
/// use color_eyre::Report;
/// use eyre::eyre;
/// use tracing_error::{TracedError, InstrumentError};
///
/// #[derive(Debug)]
/// struct SourceError;
///
/// impl std::fmt::Display for SourceError {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// write!(f, "SourceError")
/// }
/// }
///
/// impl std::error::Error for SourceError {}
///
/// let error = SourceError;
///
/// // the type annotation here is unnecessary, I've only added it for demonstration purposes
/// let error: TracedError<SourceError> = error.in_current_span();
///
/// let report: Report = error.into();
/// assert!(report.context().span_trace().is_none());
/// ```
///
/// [`tracing_error::TracedError`]: https://docs.rs/tracing-error/0.1.2/tracing_error/struct.TracedError.html
#[cfg(feature = "capture-spantrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "capture-spantrace")))]
pub fn span_trace(&self) -> Option<&SpanTrace> {
self.span_trace.as_ref()
}
}
impl EyreContext for Context {
#[allow(unused_variables)]
fn default(error: &(dyn std::error::Error + 'static)) -> Self {
let backtrace = if std::env::var("RUST_LIB_BACKTRACE")
.or_else(|_| std::env::var("RUST_BACKTRACE"))
.is_ok()
{
let backtrace = if backtrace_enabled() {
Some(Backtrace::new())
} else {
None
@ -286,6 +397,27 @@ impl EyreContext for Context {
}
}
fn backtrace_enabled() -> bool {
// Cache the result of reading the environment variables to make
// backtrace captures speedy, because otherwise reading environment
// variables every time can be somewhat slow.
static ENABLED: AtomicUsize = AtomicUsize::new(0);
match ENABLED.load(SeqCst) {
0 => {}
1 => return false,
_ => return true,
}
let enabled = match env::var("RUST_LIB_BACKTRACE") {
Ok(s) => s != "0",
Err(_) => match env::var("RUST_BACKTRACE") {
Ok(s) => s != "0",
Err(_) => false,
},
};
ENABLED.store(enabled as usize + 1, SeqCst);
enabled
}
#[cfg(feature = "capture-spantrace")]
fn get_deepest_spantrace<'a>(error: &'a (dyn Error + 'static)) -> Option<&'a SpanTrace> {
Chain::new(error)