1#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
18#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
19#![warn(missing_docs)]
20#![deny(unsafe_code)]
21
22#[allow(deprecated)] use std::panic::{self, PanicInfo};
24use std::sync::Once;
25
26use sentry_backtrace::current_stacktrace;
27use sentry_core::protocol::{Event, Exception, Level, Mechanism};
28use sentry_core::{ClientOptions, Integration};
29
30#[allow(deprecated)] pub fn panic_handler(info: &PanicInfo<'_>) {
37 sentry_core::with_integration(|integration: &PanicIntegration, hub| {
38 hub.capture_event(integration.event_from_panic_info(info));
39 if let Some(client) = hub.client() {
40 client.flush(None);
41 }
42 });
43}
44
45#[allow(deprecated)] type PanicExtractor = dyn Fn(&PanicInfo<'_>) -> Option<Event<'static>> + Send + Sync;
47
48#[derive(Default)]
50pub struct PanicIntegration {
51 extractors: Vec<Box<PanicExtractor>>,
52}
53
54impl std::fmt::Debug for PanicIntegration {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.debug_struct("PanicIntegration")
57 .field("extractors", &self.extractors.len())
58 .finish()
59 }
60}
61
62static INIT: Once = Once::new();
63
64impl Integration for PanicIntegration {
65 fn name(&self) -> &'static str {
66 "panic"
67 }
68
69 fn setup(&self, _cfg: &mut ClientOptions) {
70 INIT.call_once(|| {
71 let next = panic::take_hook();
72 panic::set_hook(Box::new(move |info| {
73 panic_handler(info);
74 next(info);
75 }));
76 });
77 }
78}
79
80#[allow(deprecated)] pub fn message_from_panic_info<'a>(info: &'a PanicInfo<'_>) -> &'a str {
83 match info.payload().downcast_ref::<&'static str>() {
84 Some(s) => s,
85 None => match info.payload().downcast_ref::<String>() {
86 Some(s) => &s[..],
87 None => "Box<Any>",
88 },
89 }
90}
91
92impl PanicIntegration {
93 pub fn new() -> Self {
95 Self::default()
96 }
97
98 #[must_use]
100 #[allow(deprecated)] pub fn add_extractor<F>(mut self, f: F) -> Self
102 where
103 F: Fn(&PanicInfo<'_>) -> Option<Event<'static>> + Send + Sync + 'static,
104 {
105 self.extractors.push(Box::new(f));
106 self
107 }
108
109 #[allow(deprecated)] pub fn event_from_panic_info(&self, info: &PanicInfo<'_>) -> Event<'static> {
114 for extractor in &self.extractors {
115 if let Some(event) = extractor(info) {
116 return event;
117 }
118 }
119
120 let msg = message_from_panic_info(info);
125 Event {
126 exception: vec![Exception {
127 ty: "panic".into(),
128 mechanism: Some(Mechanism {
129 ty: "panic".into(),
130 handled: Some(false),
131 ..Default::default()
132 }),
133 value: Some(msg.to_string()),
134 stacktrace: current_stacktrace(),
135 ..Default::default()
136 }]
137 .into(),
138 level: Level::Fatal,
139 ..Default::default()
140 }
141 }
142}