sentry_contrib_breakpad/
transport.rs

1use sentry_core::{ClientOptions, Envelope, Transport, TransportFactory};
2use std::{sync::Arc, time::Duration};
3
4/// Determines how crashes are sent to Sentry after they have been captured.
5#[derive(Copy, Clone)]
6pub enum CrashSendStyle {
7    /// Attempts to send crash envelopes immediately, in the same session that
8    /// crashed, which may be unreliable depending on the overall state of the
9    /// session. Use with care.
10    SendImmediately,
11    /// Serializes the envelope to disk instead of forwarding it to the final
12    /// [`Transport`], initializing the BreakpadIntegration with the same path
13    /// for crashes will send any existing crashes from previous sessions.
14    SendNextSession,
15}
16
17/// The [`TransportFactory`](https://docs.rs/sentry-core/0.23.0/sentry_core/trait.TransportFactory.html) implementation that must be used in concert with
18/// [`BreakpadIntegration`](crate::BreakpadIntegration) to report crash events to
19/// Sentry
20pub struct BreakpadTransportFactory {
21    inner: Arc<dyn TransportFactory>,
22    style: CrashSendStyle,
23}
24
25impl BreakpadTransportFactory {
26    pub fn new(style: CrashSendStyle, transport: Arc<dyn TransportFactory>) -> Self {
27        Self {
28            style,
29            inner: transport,
30        }
31    }
32}
33
34impl TransportFactory for BreakpadTransportFactory {
35    fn create_transport(&self, options: &ClientOptions) -> Arc<dyn Transport> {
36        Arc::new(BreakpadTransport {
37            inner: self.inner.create_transport(options),
38            style: self.style,
39        })
40    }
41}
42
43struct BreakpadTransport {
44    inner: Arc<dyn Transport>,
45    style: CrashSendStyle,
46}
47
48impl BreakpadTransport {
49    fn process(&self, envelope: Envelope) -> Option<Envelope> {
50        use sentry_core::protocol as proto;
51
52        match envelope.event() {
53            // Check if this is actually a crash event
54            Some(eve) if !eve.extra.contains_key("__breakpad_minidump_path") => Some(envelope),
55            None => Some(envelope),
56            Some(eve) => {
57                let mut event = eve.clone();
58
59                // Clear the exceptions array, Sentry will automatically fill this
60                // in for the event due to it having a minidump attachment
61                event.exception.values.clear();
62
63                let mut minidump_path = match event.extra.remove("__breakpad_minidump_path") {
64                    Some(sentry_core::protocol::Value::String(s)) => std::path::PathBuf::from(s),
65                    other => unreachable!(
66                        "__breakpad_minidump_path should be a String, but was {:?}",
67                        other
68                    ),
69                };
70
71                let session_update = envelope.items().find_map(|ei| match ei {
72                    proto::EnvelopeItem::SessionUpdate(su) => {
73                        let mut su = su.clone();
74                        su.status = proto::SessionStatus::Crashed;
75
76                        Some(su)
77                    }
78                    _ => None,
79                });
80
81                let md = crate::shared::CrashMetadata {
82                    event: Some(event),
83                    session_update,
84                };
85
86                match self.style {
87                    CrashSendStyle::SendImmediately => {
88                        let envelope = crate::shared::assemble_envelope(md, &minidump_path);
89
90                        if let Err(e) = std::fs::remove_file(&minidump_path) {
91                            debug_print!(
92                                "failed to remove crashdump {}: {}",
93                                minidump_path.display(),
94                                e
95                            );
96                        }
97
98                        Some(envelope)
99                    }
100                    CrashSendStyle::SendNextSession => {
101                        let serialized = md.serialize();
102
103                        minidump_path.set_extension("metadata");
104                        if let Err(e) = std::fs::write(&minidump_path, serialized) {
105                            debug_print!(
106                                "failed to write crash metadata {}: {}",
107                                minidump_path.display(),
108                                e
109                            );
110                        }
111
112                        None
113                    }
114                }
115            }
116        }
117    }
118}
119
120impl Transport for BreakpadTransport {
121    fn send_envelope(&self, envelope: Envelope) {
122        if let Some(envelope) = self.process(envelope) {
123            self.inner.send_envelope(envelope);
124        }
125    }
126
127    fn flush(&self, timeout: Duration) -> bool {
128        self.inner.flush(timeout)
129    }
130
131    fn shutdown(&self, timeout: Duration) -> bool {
132        self.inner.shutdown(timeout)
133    }
134}