sentry_panic/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! The Sentry Panic handler integration.
//!
//! The `PanicIntegration`, which is enabled by default in `sentry`, installs a
//! panic handler that will automatically dispatch all errors to Sentry that
//! are caused by a panic.
//! Additionally, panics are forwarded to the previously registered panic hook.
//!
//! # Configuration
//!
//! The panic integration can be configured with an additional extractor, which
//! might optionally create a sentry `Event` out of a `PanicInfo`.
//!
//! ```
//! let integration = sentry_panic::PanicIntegration::default().add_extractor(|info| None);
//! ```
#![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)]
#[allow(deprecated)] // `PanicHookInfo` is only available in Rust 1.81+.
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};
/// A panic handler that sends to Sentry.
///
/// This panic handler reports panics to Sentry. It also attempts to prevent
/// double faults in some cases where it's known to be unsafe to invoke the
/// Sentry panic handler.
#[allow(deprecated)] // `PanicHookInfo` is only available in Rust 1.81+.
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);
}
});
}
#[allow(deprecated)] // `PanicHookInfo` is only available in Rust 1.81+.
type PanicExtractor = dyn Fn(&PanicInfo<'_>) -> Option<Event<'static>> + Send + Sync;
/// The Sentry Panic handler Integration.
#[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);
}));
});
}
}
/// Extract the message of a panic.
#[allow(deprecated)] // `PanicHookInfo` is only available in Rust 1.81+.
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 {
/// Creates a new Panic Integration.
pub fn new() -> Self {
Self::default()
}
/// Registers a new extractor.
#[must_use]
#[allow(deprecated)] // `PanicHookInfo` is only available in Rust 1.81+.
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
}
/// Creates an event from the given panic info.
///
/// The stacktrace is calculated from the current frame.
#[allow(deprecated)] // `PanicHookInfo` is only available in Rust 1.81+.
pub fn event_from_panic_info(&self, info: &PanicInfo<'_>) -> Event<'static> {
for extractor in &self.extractors {
if let Some(event) = extractor(info) {
return event;
}
}
// TODO: We would ideally want to downcast to `std::error:Error` here
// and use `event_from_error`, but that way we won‘t get meaningful
// backtraces yet.
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()
}
}
}