panic_log/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::{backtrace, panic, thread};
4
5pub struct Configuration {
6    /// Always force capture a backtrace.
7    ///
8    /// If false, the presence of a backtrace will depend on the value of `RUST_BACKTRACE`.
9    /// See [`std::backtrace::Backtrace`] for more info
10    pub force_capture: bool,
11
12    /// Keep the originally set panic hook, continuing any normal panic behaviour
13    /// and custom panic behaviour set.
14    pub keep_original_hook: bool,
15}
16
17impl Default for Configuration {
18    fn default() -> Self {
19        Self {
20            force_capture: false,
21            keep_original_hook: true,
22        }
23    }
24}
25
26pub fn initialize_hook(config: Configuration) {
27    let original_hook = if config.keep_original_hook {
28        Some(panic::take_hook())
29    } else {
30        None
31    };
32    panic::set_hook(Box::new(move |info| {
33        let thread_name = thread::current()
34            .name()
35            .unwrap_or("<unnamed thread>")
36            .to_owned();
37
38        let location = if let Some(panic_location) = info.location() {
39            format!(
40                "{}:{}:{}",
41                panic_location.file(),
42                panic_location.line(),
43                panic_location.column()
44            )
45        } else {
46            "<unknown location>".to_owned()
47        };
48        let message = info.payload().downcast_ref::<&str>().unwrap_or(&"");
49
50        let backtrace = if config.force_capture {
51            backtrace::Backtrace::force_capture()
52        } else {
53            backtrace::Backtrace::capture()
54        };
55
56        log::error!("thread '{thread_name}' panicked at {location}:\n{message}\nstack bactrace:\n{backtrace}");
57
58        if let Some(original_hook) = &original_hook {
59            original_hook(info);
60        }
61    }));
62}