sentry_rust_minidump/
lib.rs

1use 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                // Remove event.process because this event came from the
77                // main app process
78                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        // We need to flush because the server will exit after this closure returns
101        sentry_client.flush(Some(std::time::Duration::from_secs(5)));
102    });
103
104    if child.is_crash_reporter_process() {
105        // Set the event.origin so that it's obvious when Rust events come from
106        // the crash reporter process rather than the main app process
107        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}