shotover/
tracing_panic_handler.rs

1use backtrace::{Backtrace, BacktraceFmt, BytesOrWideString, PrintFmt};
2use std::fmt;
3
4pub fn setup() {
5    std::panic::set_hook(Box::new(|panic| {
6        let backtrace = BacktraceFormatter(Backtrace::new());
7        // If the panic has a source location, record it as structured fields.
8        if let Some(location) = panic.location() {
9            tracing::error!(
10                message = %panic,
11                panic.file = location.file(),
12                panic.line = location.line(),
13                panic.column = location.column(),
14                panic.backtrace = format!("{backtrace}"),
15            );
16        } else {
17            tracing::error!(
18                message = %panic,
19                panic.backtrace = format!("{backtrace}"),
20            );
21        }
22    }));
23}
24
25/// The std::backtrace::Backtrace formatting is really noisy because it includes all the pre-main and panic handling frames.
26/// Internal panics have logic to remove that but that is missing from std::backtrace::Backtrace.
27/// https://github.com/rust-lang/rust/issues/105413
28///
29/// As a workaround we use the backtrace crate and manually perform the required formatting
30struct BacktraceFormatter(Backtrace);
31
32// based on https://github.com/rust-lang/backtrace-rs/blob/5be2e8ba9cf6e391c5fa45219fc091b4075eb6be/src/capture.rs#L371
33impl fmt::Display for BacktraceFormatter {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        // When printing paths we try to strip the cwd if it exists, otherwise
36        // we just print the path as-is. Note that we also only do this for the
37        // short format, because if it's full we presumably want to print
38        // everything.
39        let cwd = std::env::current_dir();
40        let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
41            let path = path.into_path_buf();
42            if let Ok(cwd) = &cwd {
43                if let Ok(suffix) = path.strip_prefix(cwd) {
44                    return fmt::Display::fmt(&suffix.display(), fmt);
45                }
46            }
47            fmt::Display::fmt(&path.display(), fmt)
48        };
49
50        let mut f = BacktraceFmt::new(f, PrintFmt::Short, &mut print_path);
51        f.add_context()?;
52        for (frame, _) in backtrace_ext::short_frames_strict(&self.0) {
53            f.frame().backtrace_frame(frame)?;
54        }
55        f.finish()
56    }
57}