From 761c603f1702ad49da8191346f6863538c7956e4 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 7 Jun 2021 12:01:22 +0200 Subject: [PATCH 1/2] feat: Allow capturing backtraces from anyhow This also fixes parsing of backtraces that have both line and column numbers --- CHANGELOG.md | 4 ++++ sentry-anyhow/Cargo.toml | 3 ++- sentry-anyhow/src/lib.rs | 31 ++++++++++++++++++++++++++++--- sentry-backtrace/src/parse.rs | 6 +++++- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ae54860..6e84ad946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - The minium supported Rust version was bumped to **1.46.0** due to requirements from dependencies. +**Features**: + +- Allow capturing backtraces from anyhow errors. + ## 0.22.0 **Breaking Changes**: diff --git a/sentry-anyhow/Cargo.toml b/sentry-anyhow/Cargo.toml index 55677be29..5da0f56ea 100644 --- a/sentry-anyhow/Cargo.toml +++ b/sentry-anyhow/Cargo.toml @@ -12,8 +12,9 @@ Sentry integration for anyhow. edition = "2018" [dependencies] +sentry-backtrace = { version = "0.22.0", path = "../sentry-backtrace" } sentry-core = { version = "0.22.0", path = "../sentry-core" } -anyhow = "1.0.30" +anyhow = { version = "1.0.39", features = ["backtrace"] } [dev-dependencies] sentry = { version = "0.22.0", path = "../sentry", default-features = false, features = ["test"] } diff --git a/sentry-anyhow/src/lib.rs b/sentry-anyhow/src/lib.rs index ddfd7c235..c6eb12824 100644 --- a/sentry-anyhow/src/lib.rs +++ b/sentry-anyhow/src/lib.rs @@ -64,8 +64,33 @@ pub trait AnyhowHubExt { } impl AnyhowHubExt for Hub { - fn capture_anyhow(&self, e: &anyhow::Error) -> Uuid { - let e: &dyn std::error::Error = e.as_ref(); - self.capture_error(e) + fn capture_anyhow(&self, anyhow_error: &anyhow::Error) -> Uuid { + let dyn_err: &dyn std::error::Error = anyhow_error.as_ref(); + let mut event = sentry_core::event_from_error(dyn_err); + + // exception records are sorted in reverse + if let Some(exc) = event.exception.iter_mut().last() { + let backtrace = anyhow_error.backtrace(); + exc.stacktrace = sentry_backtrace::parse_stacktrace(&format!("{:#}", backtrace)); + } + + self.capture_event(event) } } + +#[test] +fn test_has_backtrace() { + std::env::set_var("RUST_BACKTRACE", "1"); + + let events = sentry::test::with_captured_events(|| { + capture_anyhow(&anyhow::anyhow!("Oh jeez")); + }); + + let stacktrace = events[0].exception[0].stacktrace.as_ref().unwrap(); + let found_test_fn = stacktrace + .frames + .iter() + .any(|frame| frame.function.as_deref() == Some("sentry_anyhow::test_has_backtrace")); + + assert!(found_test_fn); +} diff --git a/sentry-backtrace/src/parse.rs b/sentry-backtrace/src/parse.rs index 44677e405..1657996a9 100644 --- a/sentry-backtrace/src/parse.rs +++ b/sentry-backtrace/src/parse.rs @@ -24,6 +24,7 @@ lazy_static::lazy_static! { \s+at\s # padded "at" in new line (?P[^\r\n]+?) # path to source file (?::(?P\d+))? # optional source line + (?::(?P\d+))? # optional source column )? $ "#).unwrap(); @@ -34,7 +35,7 @@ pub fn parse_stacktrace(bt: &str) -> Option { let mut last_address = None; let frames = FRAME_RE - .captures_iter(&bt) + .captures_iter(bt) .map(|captures| { let abs_path = captures.name("path").map(|m| m.as_str().to_string()); let filename = abs_path.as_ref().map(|p| filename(p).to_string()); @@ -63,6 +64,9 @@ pub fn parse_stacktrace(bt: &str) -> Option { lineno: captures .name("lineno") .map(|x| x.as_str().parse::().unwrap()), + colno: captures + .name("colno") + .map(|x| x.as_str().parse::().unwrap()), ..Default::default() } }) From c76d93e1600a28181225d51e9470f386c950b2fb Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 7 Jun 2021 12:26:49 +0200 Subject: [PATCH 2/2] relax stackframe found condition a bit --- sentry-anyhow/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sentry-anyhow/src/lib.rs b/sentry-anyhow/src/lib.rs index c6eb12824..5163b6480 100644 --- a/sentry-anyhow/src/lib.rs +++ b/sentry-anyhow/src/lib.rs @@ -87,10 +87,10 @@ fn test_has_backtrace() { }); let stacktrace = events[0].exception[0].stacktrace.as_ref().unwrap(); - let found_test_fn = stacktrace - .frames - .iter() - .any(|frame| frame.function.as_deref() == Some("sentry_anyhow::test_has_backtrace")); + let found_test_fn = stacktrace.frames.iter().any(|frame| match &frame.function { + Some(f) => f.contains("test_has_backtrace"), + None => false, + }); assert!(found_test_fn); }