relay_crash/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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! Native crash reporting for Relay.
//!
//! Use [`CrashHandler`] to configure and install a crash handler.
use std::fmt;
use std::path::Path;
#[cfg(unix)]
mod native {
// Code generated by bindgen contains some warnings and also offends the linter. Ignore all of
// that since they do not have a consequence on the functions used for relay-crash.
#![allow(warnings, clippy::all)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
/// A transport function used to send envelopes to Sentry.
pub type Transport = fn(envelope: &[u8]);
/// Serializes a sentry_native envelope and passes the buffer to the [`Transport`] function.
#[cfg(unix)]
unsafe extern "C" fn transport_proxy(
envelope: *const native::sentry_envelope_s,
tx_pointer: *mut std::ffi::c_void,
) {
if envelope.is_null() || tx_pointer.is_null() {
return;
}
let mut len = 0;
let buf = native::sentry_envelope_serialize(envelope, &mut len);
if !buf.is_null() && len > 0 {
let transport: Transport = std::mem::transmute(tx_pointer);
transport(std::slice::from_raw_parts(buf as *const u8, len));
}
native::sentry_free(buf as _);
}
/// Captures process crashes and reports them to Sentry.
///
/// Internally, this uses the Breakpad client to capture crash signals and create minidumps. If no
/// DSN is configured, the crash handler is not initialized.
///
/// To send crashes to Sentry, configure a [`transport` function](Self::transport). Otherwise, the
/// crash reporter writes crashes to a local database folder, where they can be handled manually.
#[derive(Default)]
#[cfg_attr(not(unix), allow(dead_code))]
pub struct CrashHandler<'a> {
dsn: &'a str,
database: &'a str,
transport: Option<Transport>,
release: Option<&'a str>,
environment: Option<&'a str>,
}
impl fmt::Debug for CrashHandler<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let transport = match self.transport {
Some(_) => "Some(fn)",
None => "None",
};
f.debug_struct("CrashHandler")
.field("dsn", &self.dsn)
.field("database", &self.database)
.field("transport", &format_args!("{transport}"))
.field("release", &self.release)
.field("environment", &self.environment)
.finish()
}
}
impl<'a> CrashHandler<'a> {
/// Creates a new crash handler.
///
/// Panics if there is are non UTF-8 characters in the path.
pub fn new(dsn: &'a str, database: &'a Path) -> Self {
Self {
dsn,
database: database.to_str().unwrap(),
transport: None,
release: None,
environment: None,
}
}
/// Set a transport function that sends data to Sentry.
///
/// Instead of using the disabled built-in transport, the crash reporter uses this function to
/// send envelopes to Sentry. Without this function, envelopes will not be sent and remain in
/// the crash database folder for manual retrieval.
pub fn transport(&mut self, transport: Transport) -> &mut Self {
self.transport = Some(transport);
self
}
/// Set the crash handler's Sentry release.
///
/// Defaults to no release.
pub fn release(&mut self, release: Option<&'a str>) -> &mut Self {
self.release = release;
self
}
/// Set the crash handler's Sentry environment.
///
/// Defaults to no environment
pub fn environment(&mut self, environment: Option<&'a str>) -> &mut Self {
self.environment = environment;
self
}
/// Installs the crash handler in the process if a Sentry DSN is set.
#[cfg(unix)]
pub fn install(&self) {
use std::ffi::CString;
unsafe {
let options = native::sentry_options_new();
let dsn_cstr = CString::new(self.dsn).unwrap();
native::sentry_options_set_dsn(options, dsn_cstr.as_ptr());
let db_cstr = CString::new(self.database).unwrap();
native::sentry_options_set_database_path(options, db_cstr.as_ptr());
if let Some(release) = self.release {
let release_cstr = CString::new(release).unwrap();
native::sentry_options_set_release(options, release_cstr.as_ptr());
}
if let Some(environment) = self.environment {
let env_cstr = CString::new(environment).unwrap();
native::sentry_options_set_environment(options, env_cstr.as_ptr());
}
if let Some(f) = self.transport {
let tx = native::sentry_new_function_transport(Some(transport_proxy), f as _);
native::sentry_options_set_transport(options, tx);
}
native::sentry_init(options);
}
}
#[cfg(not(unix))]
pub fn install(&self) {
unimplemented!("crash handler not supported on this platform");
}
}