Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you see an error's backtrace when using SNAFU?

How do I get Backtrace working with SNAFU? I tried, but I just get empty backtraces. The documentation is sparse on that it seems.

return Error::SampleError {
    msg: "foo".to_string(),
    backtrace: Backtrace::generate(),
};

prints

SampleError { msg: "foo", backtrace: Backtrace(()) }

This is being thrown from a function that is very deep in the call stack.

like image 585
user855 Avatar asked Sep 05 '25 17:09

user855


1 Answers

Let's start with this minimal, reproducible example:

use snafu::Snafu;

#[derive(Debug, Snafu)]
enum Error {
    SampleError { msg: String },
}

type Result<T, E = Error> = std::result::Result<T, E>;

fn alpha() -> Result<()> {
    beta()
}

fn beta() -> Result<()> {
    gamma()
}

fn gamma() -> Result<()> {
    SampleError { msg: "foo" }.fail()
}

Note that it uses the context selector SampleError and the method fail instead of directly using the enum variant to construct the error.

Now we import snafu::Backtrace and add it to our error, naming it backtrace (see controlling backtraces if you must call it something else).

use snafu::{Snafu, Backtrace};

#[derive(Debug, Snafu)]
enum Error {
    SampleError { msg: String, backtrace: Backtrace },
}

If this were a library, that's where you should stop. Your error now will optionally have a backtrace enabled if the binary decides that backtraces are worth it. This is done as backtraces aren't yet stabilized in Rust, so SNAFU has to be compatible with multiple possible implementations.

If you are controlling the binary, you will need to decide how the backtraces will be implemented. There are three main implementations selected by a feature flag:

  • backtraces — Provides an opaque Backtrace type
  • backtraces-impl-backtrace-crate — uses the third-party backtrace crate. snafu::Backtrace is just an alias to backtrace::Backtrace.
  • unstable-backtraces-impl-std — uses the unstable standard library Backtrace. snafu::Backtrace is just an alias to std::backtrace::Backtrace.

Once you've picked an implementation feature flag, add it to your Cargo.toml:

[dependencies]
snafu = { version = "0.6.3", features = ["backtraces"] }

Then, you will need to handle the error somewhere high up in your program and get the backtrace and print it out. This uses the ErrorCompat trait, which I encourage you to use in a verbose manner so it's easier to remove it later, when it's stabilized in the standard library:

use snafu::ErrorCompat;

fn main() {
    if let Err(e) = alpha() {
        if let Some(bt) = ErrorCompat::backtrace(&e) {
            println!("{:?}", bt);
        }
    }
}
   0: backtrace::backtrace::trace_unsynchronized
   1: backtrace::backtrace::trace
   2: backtrace::capture::Backtrace::create
   3: backtrace::capture::Backtrace::new
   4: <backtrace::capture::Backtrace as snafu::GenerateBacktrace>::generate
   5: so::SampleError<__T0>::fail
   6: so::gamma
   7: so::beta
   8: so::alpha
   9: so::main
  10: std::rt::lang_start::{{closure}}
  11: std::panicking::try::do_call
  12: __rust_maybe_catch_panic
  13: std::rt::lang_start_internal
  14: std::rt::lang_start
  15: main

Disclaimer: I'm the primary author of SNAFU.


You are correct that this isn't thoroughly described in the user's guide and I've created an issue to improve that. The most relevant section is the one about feature flags.

There are multiple tests for backtraces in the SNAFU repository that you could look to:

  • backtrace-shim
  • backtraces-impl-backtrace-crate
  • backtraces-impl-std
like image 134
Shepmaster Avatar answered Sep 07 '25 16:09

Shepmaster