-
-
Notifications
You must be signed in to change notification settings - Fork 159
/
lib.rs
118 lines (103 loc) · 3.49 KB
/
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
//! 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);
//! ```
#![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};
/// 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.
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;
/// The Sentry Panic handler Integration.
#[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);
}));
});
}
}
/// Extract the message of a panic.
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.
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.
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(),
value: Some(msg.to_string()),
stacktrace: current_stacktrace(),
..Default::default()
}]
.into(),
level: Level::Fatal,
..Default::default()
}
}
}