#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
#![warn(missing_docs)]
#![deny(unsafe_code)]
use std::panic::{self, PanicInfo};
use std::sync::Once;
use sentry_backtrace::current_stacktrace;
use sentry_core::protocol::{Event, Exception, Level, Mechanism};
use sentry_core::{ClientOptions, Integration};
pub fn panic_handler(info: &PanicInfo<'_>) {
sentry_core::with_integration(|integration: &PanicIntegration, hub| {
hub.capture_event(integration.event_from_panic_info(info));
if let Some(client) = hub.client() {
client.flush(None);
}
});
}
type PanicExtractor = dyn Fn(&PanicInfo<'_>) -> Option<Event<'static>> + Send + Sync;
#[derive(Default)]
pub struct PanicIntegration {
extractors: Vec<Box<PanicExtractor>>,
}
impl std::fmt::Debug for PanicIntegration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PanicIntegration")
.field("extractors", &self.extractors.len())
.finish()
}
}
static INIT: Once = Once::new();
impl Integration for PanicIntegration {
fn name(&self) -> &'static str {
"panic"
}
fn setup(&self, _cfg: &mut ClientOptions) {
INIT.call_once(|| {
let next = panic::take_hook();
panic::set_hook(Box::new(move |info| {
panic_handler(info);
next(info);
}));
});
}
}
pub fn message_from_panic_info<'a>(info: &'a PanicInfo<'_>) -> &'a str {
match info.payload().downcast_ref::<&'static str>() {
Some(s) => s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
},
}
}
impl PanicIntegration {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn add_extractor<F>(mut self, f: F) -> Self
where
F: Fn(&PanicInfo<'_>) -> Option<Event<'static>> + Send + Sync + 'static,
{
self.extractors.push(Box::new(f));
self
}
pub fn event_from_panic_info(&self, info: &PanicInfo<'_>) -> Event<'static> {
for extractor in &self.extractors {
if let Some(event) = extractor(info) {
return event;
}
}
let msg = message_from_panic_info(info);
Event {
exception: vec![Exception {
ty: "panic".into(),
mechanism: Some(Mechanism {
ty: "panic".into(),
handled: Some(false),
..Default::default()
}),
value: Some(msg.to_string()),
stacktrace: current_stacktrace(),
..Default::default()
}]
.into(),
level: Level::Fatal,
..Default::default()
}
}
}