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
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![warn(missing_doc_code_examples)]
use std::panic::{self, PanicInfo};
use std::sync::Once;
use sentry_backtrace::current_stacktrace;
use sentry_core::protocol::{Event, Exception, Level};
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))
});
}
type PanicExtractor = dyn Fn(&PanicInfo<'_>) -> Option<Event<'static>> + Send + Sync;
#[derive(Default)]
pub struct PanicIntegration {
extractors: Vec<Box<PanicExtractor>>,
}
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()
}
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(),
value: Some(msg.to_string()),
stacktrace: current_stacktrace(),
..Default::default()
}]
.into(),
level: Level::Fatal,
..Default::default()
}
}
}