8

I am running a Rust warp webserver and I need more descriptive error messages. I'd like to print a backtrace or something similar so I can tell where the error started.

I was using the Failure crate, but it is now deprecated so I migrated to thiserror.

Is it possible (without using nightly), to print a backtrace without panicking?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Marty
  • 1,077
  • 1
  • 10
  • 14
  • 2
    I also thought this was a duplicate initially, but I suspect the OP wants to know how to get the backtrace etc out of a `thiserror` object and print that, rather than just print the backtrace at some arbitrary point in the code. Turns out that is not always possible, and not particularly well documented (IMO). – Michael Anderson May 12 '20 at 05:53
  • 1
    Correct - my question was specific to `thiserror` – Marty May 12 '20 at 08:10
  • 3
    Thiserror structs can contain a backtrace field, which can be used to print the backtrace like the suggested duplicate shows. – hellow May 12 '20 at 08:11
  • 4
    Since this is closed I can't give you an official answer, but here's a rough example that I put together: https://gist.github.com/mikeando/c03fd62e552ccabcdb894251b59038c7 - `thiserror` is not in the playground crates, so I can't give a useful playground link - sorry. – Michael Anderson May 12 '20 at 23:52
  • Hi, I’ve reopened it! This is a great answer, if you can post it I’ll choose I’ll choose it. – Marty May 13 '20 at 11:05
  • @MichaelAnderson this is great, but requires nightly doesn't it? – Anatoly Bugakov Apr 21 '22 at 18:07
  • @АнатолийБугаков I think you're right. It looks like all the backtrace machinery in `thiserror` and `std::Error` are only available on nightly. – Michael Anderson Apr 22 '22 at 00:49
  • Since the question has been reopened (a few years ago, and I didn't notice!) and the playground now supports `thiserror`, I've provided the previous gist as a working answer. – Michael Anderson Apr 22 '22 at 00:54

1 Answers1

4

There are ways to get to the backtrace information - but it relies on what are currently "nightly only" APIs. If you're happy to use nightly and stick with thiserror, here's what you do... (If not then see the ASIDE at the end for other ideas).

If you're creating the error from scratch the steps are:

  1. add a backtrace: Backtrace field to your error type.
  2. set the backtrace when you create an error using backtrace::force_captue()

If you're creating this error due to another error and want to use its backtrace instead, then just add #[backtrace] to the source field where you keep the original error.

Then to get the backtrace information you can just use the backtrace() function on std::Error.

An example looks like this:

#![feature(backtrace)]

extern crate thiserror;

use std::backtrace::Backtrace;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    //#[error("data store disconnected")]
    //Disconnect(#[from] io::Error),
    #[error("the data for key `{0}` is not available")]
    Redaction(String),
    #[error("invalid header (expected {expected:?}, found {found:?})")]
    InvalidHeader {
        expected: String,
        found: String,
        backtrace: Backtrace,
    },
    #[error("unknown data store error")]
    Unknown,
}

pub fn explode() -> Result<(), DataStoreError> {
    Err(DataStoreError::InvalidHeader {
        expected: "A".to_owned(),
        found: "B".to_owned(),
        backtrace: Backtrace::force_capture(),
    })
}

fn main() {
    use std::error::Error;
    let e = explode().err().unwrap();
    let b = e.backtrace();
    println!("e = {}", e);
    println!("b = {:#?}", b);
}

This outputs something like:

e = invalid header (expected "A", found "B")
b = Some(
    Backtrace [
        { fn: "playground::explode", file: "./src/main.rs", line: 28 },
        { fn: "playground::main", file: "./src/main.rs", line: 34 },
        { fn: "core::ops::function::FnOnce::call_once", file: "/rustc/879aff385a5fe0af78f3d45fd2f0b8762934e41e/library/core/src/ops/function.rs", line: 248 },
        ...

You can see a working version in the playground


ASIDE

If you're not tied to thiserror and need to use the stable version of the compiler, you could instead use snafu which has support for backtraces using the backtrace crate, rather than std::backtrace, and so works on stable.

There may also be ways to make thiserror work with the backtrace crate rather than std::backtrace but I've not tried that.

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187