use std::panic::PanicHookInfo;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use crate::backtrace;
use crate::event::Event;
use crate::level::Level;
static REGISTERED: AtomicBool = AtomicBool::new(false);
pub fn register() {
if REGISTERED.swap(true, Ordering::SeqCst) {
return; }
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| capture_panic(info)));
previous(info);
}));
}
fn capture_panic(info: &PanicHookInfo<'_>) {
if std::thread::current().name() == Some("errsight-worker") {
return;
}
let bt = ::backtrace::Backtrace::new();
let captured = crate::with_client(|client| {
let cfg = client.config();
if !cfg.enabled() {
return false;
}
let message = panic_message(info);
let frames = backtrace::frames_from(&bt, cfg);
let mut event = Event::new(Level::Fatal, format!("panic: {message}"));
event.set_metadata("exception_class", "panic");
if let Some(loc) = info.location() {
let location = format!("{}:{}:{}", loc.file(), loc.line(), loc.column());
event.set_metadata("panic_location", location);
}
if !frames.is_empty() {
event.backtrace = Some(backtrace::frames_to_string(&frames));
if let Ok(v) = serde_json::to_value(&frames) {
event.set_metadata("exception_frames", v);
}
}
crate::finalize_and_capture(client, event).is_some()
});
if captured == Some(true) {
let _ = crate::flush(Some(Duration::from_secs(2)));
}
}
fn panic_message(info: &PanicHookInfo<'_>) -> String {
let payload = info.payload();
if let Some(s) = payload.downcast_ref::<&str>() {
(*s).to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
let rendered = info.to_string();
if rendered.is_empty() {
"panic".to_string()
} else {
rendered
}
}
}