Anyhow backtraces

Did something change recently in anyhow which causes its Debug output to contain a full backtrace?
Suddenly my log files are full of unwanted backtraces.

Do you have the RUST_BACKTRACE=1 or RUST_LIB_BACKTRACE=1 environment variables set?

According to the docs, that's the only reason it'll capture a backtrace:

  • If using Rust ≥ 1.65, a backtrace is captured and printed with the error if the underlying error type does not already provide its own. In order to see backtraces, they must be enabled through the environment variables described in std::backtrace:
    • If you want panics and errors to both have backtraces, set RUST_BACKTRACE=1;
    • If you want only errors to have backtraces, set RUST_LIB_BACKTRACE=1;
    • If you want only panics to have backtraces, set RUST_BACKTRACE=1 and RUST_LIB_BACKTRACE=0.

Oh, this went in at Rust ≥ 1.65 and I hadn't noticed.

Is there some way in code to stop anyhow from capturing a backtrace, while still keeping it enabled for panics? I don't see one.

Directly from the previous comment:

1 Like

I want to set this in the code, not the environment.
If the user sets this, the program slows down.

You can call std::env::set_var("RUST_LIB_BACKTRACE", "0") at the start of the program before the first backtrace would be captured. (I believe the env var value is cached after the first time)

Make sure to read this UB hazard if you go the set_var route.

4 Likes

What a hack. I have "unsafe" locked out, as policy, anyway.

The whole idea that non-panic library behavior depends on an environment variable is just wrong.

1 Like

I agree. And I assume setting an env variable in a library changes behavior for the application and its use of other libraries.

I'm not saying this would be easy or even possible for you, but I wonder if switching from anyhow to thiserror would give you control over backtraces, since with thiserror there should only be a backtrace if you declare a backtrace field in your error, as far as I can tell from the docs. Perhaps someone who is more familiar with backtrace generation in these crates can help.

1 Like

There's an anyhow issue for this. Many people don't like it, especially on embedded platforms.

1 Like

Thanks, good to know, and glad you got to the bottom of this.

Yeah. Please go over to Github and comment on the issue.

John, I wanted to discuss your use case a little more here before commenting further on github. To be fair I think even Errors that are intended to be handled by the program could often benefit from backtraces for debugging purposes. If you agree, wouldn't you want your library to enable them in debug mode? If so, you would need a way for anyhow to support enabling them in debug mode as well.

I tried to figure out a solution for you, but I don't think I did, but I felt I might post what I thought of anyway.

The docs say "a backtrace is captured and printed with the error if the underlying error type does not already provide its own", and I thought there might be a possibility there. But it seems pretty esoteric. It looks like whether a backtrace is created is controlled by one of these 3 macros (source):

#[cfg(error_generic_member_access)]
macro_rules! backtrace_if_absent {
    ($err:expr) => {
        match std::error::request_ref::<std::backtrace::Backtrace>($err as &dyn std::error::Error) {
            Some(_) => None,
            None => backtrace!(),
        }
    };
}

#[cfg(all(
    feature = "std",
    not(error_generic_member_access),
    any(std_backtrace, feature = "backtrace")
))]
macro_rules! backtrace_if_absent {
    ($err:expr) => {
        backtrace!()
    };
}

#[cfg(all(feature = "std", not(std_backtrace), not(feature = "backtrace")))]
macro_rules! backtrace_if_absent {
    ($err:expr) => {
        None
    };
}

So if you could get std_backtrace set to false, then it wouldn't generate; or if you could enable the error_generic_member_access, then you could wrap your errors in something that returns Some(Backtrace::disabled()) from request_ref (but that's annoying for something that's supposed to be a simple error library). But, enabling those flags happens in the build.rs in some rather (to me) esoteric ways: https://github.com/dtolnay/anyhow/blob/54437197ee79c20678db433d98616fab7ddff1a5/build.rs. I don't know if it's possible to override the values printed out by a build.rs? But then doing something even hackier than environment vars IMO. But it might be a way forward.

I don't really recommend this but I spent like 15 minutes trying to figure it out and didn't want to throw it away

Backtraces are an expensive operation. "Capturing a backtrace can be a quite expensive runtime operation, so the environment variables allow either forcibly disabling this runtime performance hit or allow selectively enabling it in some programs." Not clear how expensive, but it involves reading the executable file.

Backtraces are not machine-readable. Backtraces are designed to be printed on the console by a command line program. BacktraceFrame is still experimental. If your backtraces go to logs, they add huge log verbosity and tend not to be useful. If you need to trim a backtrace down to the non-library items, which I do in for a panic popup dialog box, you have to look at text strings and guess.

Backtrace on panic works fine, but that has nothing to do with the "anyhow" crate.