Skip to content

Commit deb3693

Browse files
authored
feat: Allow capturing backtraces from anyhow (#341)
This also fixes parsing of backtraces that have both line and column numbers
1 parent d7f5f2b commit deb3693

File tree

4 files changed

+39
-5
lines changed

4 files changed

+39
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- The minium supported Rust version was bumped to **1.46.0** due to requirements from dependencies.
88

9+
**Features**:
10+
11+
- Allow capturing backtraces from anyhow errors.
12+
913
**Fixes**:
1014

1115
- Honor the `attach_stacktrace` option correctly when capturing errors.

sentry-anyhow/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ Sentry integration for anyhow.
1212
edition = "2018"
1313

1414
[dependencies]
15+
sentry-backtrace = { version = "0.22.0", path = "../sentry-backtrace" }
1516
sentry-core = { version = "0.22.0", path = "../sentry-core" }
16-
anyhow = "1.0.30"
17+
anyhow = { version = "1.0.39", features = ["backtrace"] }
1718

1819
[dev-dependencies]
1920
sentry = { version = "0.22.0", path = "../sentry", default-features = false, features = ["test"] }

sentry-anyhow/src/lib.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,33 @@ pub trait AnyhowHubExt {
6464
}
6565

6666
impl AnyhowHubExt for Hub {
67-
fn capture_anyhow(&self, e: &anyhow::Error) -> Uuid {
68-
let e: &dyn std::error::Error = e.as_ref();
69-
self.capture_error(e)
67+
fn capture_anyhow(&self, anyhow_error: &anyhow::Error) -> Uuid {
68+
let dyn_err: &dyn std::error::Error = anyhow_error.as_ref();
69+
let mut event = sentry_core::event_from_error(dyn_err);
70+
71+
// exception records are sorted in reverse
72+
if let Some(exc) = event.exception.iter_mut().last() {
73+
let backtrace = anyhow_error.backtrace();
74+
exc.stacktrace = sentry_backtrace::parse_stacktrace(&format!("{:#}", backtrace));
75+
}
76+
77+
self.capture_event(event)
7078
}
7179
}
80+
81+
#[test]
82+
fn test_has_backtrace() {
83+
std::env::set_var("RUST_BACKTRACE", "1");
84+
85+
let events = sentry::test::with_captured_events(|| {
86+
capture_anyhow(&anyhow::anyhow!("Oh jeez"));
87+
});
88+
89+
let stacktrace = events[0].exception[0].stacktrace.as_ref().unwrap();
90+
let found_test_fn = stacktrace.frames.iter().any(|frame| match &frame.function {
91+
Some(f) => f.contains("test_has_backtrace"),
92+
None => false,
93+
});
94+
95+
assert!(found_test_fn);
96+
}

sentry-backtrace/src/parse.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ lazy_static::lazy_static! {
2424
\s+at\s # padded "at" in new line
2525
(?P<path>[^\r\n]+?) # path to source file
2626
(?::(?P<lineno>\d+))? # optional source line
27+
(?::(?P<colno>\d+))? # optional source column
2728
)?
2829
$
2930
"#).unwrap();
@@ -34,7 +35,7 @@ pub fn parse_stacktrace(bt: &str) -> Option<Stacktrace> {
3435
let mut last_address = None;
3536

3637
let frames = FRAME_RE
37-
.captures_iter(&bt)
38+
.captures_iter(bt)
3839
.map(|captures| {
3940
let abs_path = captures.name("path").map(|m| m.as_str().to_string());
4041
let filename = abs_path.as_ref().map(|p| filename(p).to_string());
@@ -63,6 +64,9 @@ pub fn parse_stacktrace(bt: &str) -> Option<Stacktrace> {
6364
lineno: captures
6465
.name("lineno")
6566
.map(|x| x.as_str().parse::<u64>().unwrap()),
67+
colno: captures
68+
.name("colno")
69+
.map(|x| x.as_str().parse::<u64>().unwrap()),
6670
..Default::default()
6771
}
6872
})

0 commit comments

Comments
 (0)