sentry_rust_minidump/
lib.rs1use minidumper_child::{ClientHandle, Error, MinidumperChild};
2use sentry::{
3 protocol::{Attachment, AttachmentType, Event, Value},
4 Level,
5};
6
7#[cfg(feature = "ipc")]
8use sentry::{Breadcrumb, User};
9
10pub struct Handle {
11 _handle: ClientHandle,
12}
13
14#[cfg(feature = "ipc")]
15#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, PartialEq)]
16pub enum ScopeUpdate {
17 AddBreadcrumb(Breadcrumb),
18 SetUser(Option<User>),
19 SetExtra(String, Option<Value>),
20 SetTag(String, Option<String>),
21}
22
23#[cfg(feature = "ipc")]
24impl Handle {
25 fn send_message(&self, update: &ScopeUpdate) {
26 let buffer = serde_json::to_vec(update).expect("could not serialize scope update");
27 self._handle.send_message(0, buffer).ok();
28 }
29
30 pub fn add_breadcrumb(&self, breadcrumb: Breadcrumb) {
31 self.send_message(&ScopeUpdate::AddBreadcrumb(breadcrumb));
32 }
33
34 pub fn set_user(&self, user: Option<User>) {
35 self.send_message(&ScopeUpdate::SetUser(user));
36 }
37
38 pub fn set_extra(&self, key: String, value: Option<Value>) {
39 self.send_message(&ScopeUpdate::SetExtra(key, value));
40 }
41
42 pub fn set_tag(&self, key: String, value: Option<String>) {
43 self.send_message(&ScopeUpdate::SetTag(key, value));
44 }
45}
46
47#[must_use = "The return value from init() should not be dropped until the program exits"]
48pub fn init(sentry_client: &sentry::Client) -> Result<Handle, Error> {
49 let sentry_client = sentry_client.clone();
50
51 let child = MinidumperChild::new();
52
53 #[cfg(feature = "ipc")]
54 let child = child.on_message(|_kind, buffer| {
55 if let Ok(update) = serde_json::from_slice::<ScopeUpdate>(&buffer[..]) {
56 match update {
57 ScopeUpdate::AddBreadcrumb(b) => sentry::add_breadcrumb(b),
58 ScopeUpdate::SetUser(u) => sentry::configure_scope(|scope| {
59 scope.set_user(u);
60 }),
61 ScopeUpdate::SetExtra(k, v) => sentry::configure_scope(|scope| match v {
62 Some(v) => scope.set_extra(&k, v),
63 None => scope.remove_extra(&k),
64 }),
65 ScopeUpdate::SetTag(k, v) => match v {
66 Some(v) => sentry::configure_scope(|scope| scope.set_tag(&k, &v)),
67 None => sentry::configure_scope(|scope| scope.remove_tag(&k)),
68 },
69 }
70 }
71 });
72
73 let child = child.on_minidump(move |buffer, path| {
74 sentry::with_scope(
75 |scope| {
76 scope.remove_extra("event.process");
79
80 let filename = path
81 .file_name()
82 .map(|s| s.to_string_lossy().into_owned())
83 .unwrap_or_else(|| "minidump.dmp".to_string());
84
85 scope.add_attachment(Attachment {
86 buffer,
87 filename,
88 ty: Some(AttachmentType::Minidump),
89 ..Default::default()
90 });
91 },
92 || {
93 sentry::capture_event(Event {
94 level: Level::Fatal,
95 ..Default::default()
96 })
97 },
98 );
99
100 sentry_client.flush(Some(std::time::Duration::from_secs(5)));
102 });
103
104 if child.is_crash_reporter_process() {
105 sentry::configure_scope(|scope| {
108 scope.set_extra("event.process", Value::String("crash-reporter".to_string()));
109 });
110 }
111
112 child.spawn().map(|handle| Handle { _handle: handle })
113}