Logging std::error:Error::backtrace()

I'm trying to implement a GraphQL server in rust. I don't want to panic-kill the entire server any time some other API times out, so I have a bunch of Result<T, Box<dyn std::error::Error>> things that I get from external APIs, libraries, my own code, etc. But I'm getting all kinds of issues logging and managing these errors.

I've tried enabling #![feature(backtrace)], set RUST_LIB_BACKTRACE=1, set RUST_BACKTRACE=1, added std::backtrace::Backtrace::force_capture(); to the beginning of main. But no matter what I do err.backtrace() is always None... What's going on? What's a guy gutta do to get a backtrace around here?

If it helps any this error originates from reqwest, and is propagated though with ?s. I catch it and attempt to log it with the backtrace like so:

run_my_code().await.map_err(|err| {
  error!("error: {:?} {:?}", err, err.backtrace());
  juniper::FieldError::new("internal server error", juniper::Value::Null)

I'd like to avoid external deps like futures or error-chain if possible.

I'm running stable 1.41.0:

   stable-x86_64-apple-darwin unchanged - rustc 1.41.0 (5e1a79984 2020-01-27)
  nightly-x86_64-apple-darwin unchanged - rustc 1.43.0-nightly (8aa9d2014 2020-02-21)

You may want to investigate how the various error libraries do this.

You can look at code of https://github.com/sfackler/rust-log-panics

I'm using Result types instead of panics.

You should note that options such as RUST_BACKTRACE are not about backtraces on errors, but rather backtraces on panic.

Backtraces on errors will return Some specifically if and only if the library who wrote the error type supports backtraces. Since it's a nightly API, almost no error types will implement this functionality.

If you want a backtrace, I would recommend turning your error into an error type which does support backtraces, such as failure::Error. This won't get a backtrace all the way to the start of an error, but it will construct a backtrace up to the point when you convert to failure::Error.

Note that even then, std::error::Error::backtrace() will return None, as failure::Error has not implemented it. (and it would be unreasonable for it to implement it, as it's unstable and that would mean failure would need a dependency on nightly rust). Instead, failure::Error::backtrace gets the backtrace that failure specifically creates.

There are other solutions, but none will get backtraces into the library - and all will involve someone manually constructing the backtrace. The rust stdlib will only ever construct a backtrace for you on panic, no other time.

1 Like

Instead of failure I would recommend the anyhow crate because it handles backtrace correctly.

1 Like

You should note that options such as RUST_BACKTRACE are not about backtraces on errors, but rather backtraces on panic.

My understanding here was based on the RFC: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md#backtrace-controlling-environment-variables which says that RUST_BACKTRACE now has some role in Error backtraces. Though using this env var setting is clearly still confusing. More info in the module docs too: https://doc.rust-lang.org/std/backtrace/index.html

This is super helpful info... A lot of this doesn't seem to be documented anywhere else.

What's the relation between failure and anyhow? It seems like failure is more widely used, but anyhow is more up-to-date... I've started with failure but some of the language in the README makes me worry that it won't be maintained in the future (https://github.com/rust-lang-nursery/failure#evolution).

failure has its own trait. anyhow sticks to the std Error trait, which has recently incorporated features from failure's trait into it.