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
#[cfg(doc)]
use crate::{shutdown, Shutdown};
use crate::{Event, Level, Value};
#[cfg(doc)]
use std::process::abort;
use std::{
collections::BTreeMap,
convert::TryFrom,
panic::{self, PanicInfo},
};
pub fn set_hook(
before_send: Option<Box<dyn Fn(Event) -> Event + 'static + Send + Sync>>,
hook: Option<Box<dyn Fn(&PanicInfo) + 'static + Send + Sync>>,
) {
panic::set_hook(Box::new(move |panic_info| {
let mut event = Event::new_message(
Level::Error,
Some("rust panic".into()),
panic_info.to_string(),
);
if let Some(location) = panic_info.location() {
let mut extra = BTreeMap::new();
extra.insert("file", Value::from(location.file()));
if let Ok(line) = i32::try_from(location.line()) {
extra.insert("line", line.into());
}
if let Ok(column) = i32::try_from(location.column()) {
extra.insert("column", column.into());
}
event.insert("extra", extra);
}
event.add_stacktrace(0);
if let Some(before_send) = &before_send {
event = before_send(event);
}
event.capture();
if let Some(hook) = &hook {
hook(panic_info);
}
}));
}
#[cfg(test)]
#[rusty_fork::fork_test(timeout_ms = 60000)]
fn hook() {
use std::{
sync::atomic::{AtomicBool, Ordering},
thread,
};
static BEFORE_SEND: AtomicBool = AtomicBool::new(false);
static HOOK: AtomicBool = AtomicBool::new(false);
set_hook(None, None);
set_hook(
Some(Box::new(|event| {
BEFORE_SEND.store(true, Ordering::SeqCst);
event
})),
Some(Box::new(|_| HOOK.store(true, Ordering::SeqCst))),
);
thread::spawn(|| panic!("this panic is a test"))
.join()
.unwrap_err();
assert!(BEFORE_SEND.load(Ordering::SeqCst));
assert!(HOOK.load(Ordering::SeqCst));
}