Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tracing: add an example of tracing in a panic hook #1375

Merged
merged 2 commits into from
Apr 28, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions examples/examples/panic_hook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! This example demonstrates how `tracing` events can be recorded from within a
//! panic hook, capturing the span context in which the program panicked.

fn main() {
let collector = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.finish();

// NOTE: Using `tracing` in a panic hook requires the use of the *global*
// trace dispatcher (`tracing::collect::set_global_default`), rather than
// the per-thread scoped dispatcher
// (`tracing::collect::with_default`/`set_default`). With the scoped trace
// dispatcher, the collector's thread-local context may already have been
// torn down by unwinding by the time the panic handler is reached.
tracing::collect::set_global_default(collector).unwrap();

// Set a panic hook that records the panic as a `tracing` event at the
// `ERROR` verbosity level.
//
// If we are currently in a span when the panic occurred, the logged event
// will include the current span, allowing the context in which the panic
// occurred to be recorded.
std::panic::set_hook(Box::new(|panic| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this built-in?

tracing::set_panic_hook()

and could fail if no global one is set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, might be worth a follow-up change to add something like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest opening an issue to centralize discussion: https://github.com/tokio-rs/tracing/issues/new?assignees=&labels=&template=feature_request.md.

However, I'm personally not inclined to have this in tracing for the following reasons:

  • Users might want to handle panics in different ways, with different formatting, levels, and so forth. They might not want to involve tracing in the first place!
  • Other users might not want tracing to change their panic hooks or they might want tracing and some other functionality in their panic hooks. If the data passed to the panic hook is paramaterized, then I'm not sure what the benefit of a tracing-vended panic hook is over std:panic::set_hook.
  • Failing/panicking if tracing::set_panic_hook (or another panic hook) is way too opinionated for tracing, IMO.

// If the panic has a source location, record it as structured fields.
if let Some(location) = panic.location() {
// On nightly Rust, where the `PanicInfo` type also exposes a
// `message()` method returning just the message, we could record
// just the message instead of the entire `fmt::Display`
// implementation, avoiding the duplciated location
tracing::error!(
message = %panic,
panic.file = location.file(),
panic.line = location.line(),
panic.column = location.column(),
);
} else {
tracing::error!(message = %panic);
}
}));

for i in 0..10 {
check_number(i);
}
}

#[tracing::instrument]
fn check_number(x: i32) {
if x % 2 == 0 {
panic!("I don't work with even numbers!");
}
}