diff --git a/.gitmodules b/.gitmodules index a98f409242b..836ccf671d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "general/uap-core"] path = relay-ua/uap-core url = https://github.com/ua-parser/uap-core.git -[submodule "sentry-native"] - path = relay-crash/sentry-native - url = https://github.com/getsentry/sentry-native.git [submodule "sentry-conventions"] path = relay-conventions/sentry-conventions url = https://github.com/getsentry/sentry-conventions diff --git a/CHANGELOG.md b/CHANGELOG.md index 013d35e3cd8..9b69b576173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Infer span descriptions via `sentry-conventions`. ([#6093](https://github.com/getsentry/relay/pull/6093)) - Raises the size limit for the flags context to 64KiB. ([#6137](https://github.com/getsentry/relay/pull/6137)) - Add segment_names field to Replay events. ([#6134](https://github.com/getsentry/relay/pull/6134)) +- Use a out of process solution for catching crashes. ([#6158](https://github.com/getsentry/relay/pull/6158)) - Support PEM file format for signing / verification keys. ([#6155](https://github.com/getsentry/relay/pull/6155)) **Bug Fixes**: diff --git a/Cargo.lock b/Cargo.lock index 0bd1af91c21..4e7c8c27d99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,26 +403,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -572,15 +552,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -632,17 +603,6 @@ dependencies = [ "half", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.6.1" @@ -856,6 +816,19 @@ dependencies = [ "mach2", ] +[[package]] +name = "crash-handler" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df5c9639f4942eb7702b964b3f9adf03a55724a57558cc177407388a8b936e2" +dependencies = [ + "cfg-if", + "crash-context", + "libc", + "mach2", + "parking_lot", +] + [[package]] name = "crc" version = "3.4.0" @@ -1415,7 +1388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1740,12 +1713,6 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "globset" version = "0.4.18" @@ -2322,7 +2289,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2349,15 +2316,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -2493,16 +2451,6 @@ dependencies = [ "cc", ] -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link 0.2.1", -] - [[package]] name = "liblzma" version = "0.4.6" @@ -2694,15 +2642,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "metrics" version = "0.24.5" @@ -2785,7 +2724,7 @@ dependencies = [ "memmap2", "minidump-common", "num-traits", - "procfs-core", + "procfs-core 0.17.0", "prost 0.13.5", "range-map", "scroll", @@ -2812,9 +2751,9 @@ dependencies = [ [[package]] name = "minidump-writer" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1fc14d6ded915b8e850801465e7096f77ed60bf87e4e85878d463720d9dc4d" +checksum = "48ef5778fc71c10d6bac1ec6b971ba0cc4b67cd8f3f0f7689dfdaab75884082f" dependencies = [ "bitflags", "byteorder", @@ -2827,17 +2766,45 @@ dependencies = [ "log", "mach2", "memmap2", - "memoffset", "minidump-common", - "nix 0.29.0", - "procfs-core", + "nix", + "procfs-core 0.18.0", "scroll", "serde", "serde_json", - "tempfile", "thiserror 2.0.18", ] +[[package]] +name = "minidumper" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d1ab45c501f311e5e4c22232ecdd7e31120174261c42e170d41c6afbd60cc41" +dependencies = [ + "cfg-if", + "crash-context", + "libc", + "log", + "minidump-writer", + "parking_lot", + "polling", + "scroll", + "thiserror 2.0.18", + "uds", +] + +[[package]] +name = "minidumper-child" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7615d83d636600b331883635f136665b4a75cf8031e29be1521bb271325dfc3" +dependencies = [ + "crash-handler", + "minidumper", + "thiserror 2.0.18", + "uuid", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2931,18 +2898,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nix" version = "0.30.1" @@ -3496,7 +3451,7 @@ checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" dependencies = [ "android_system_properties", "log", - "nix 0.30.1", + "nix", "objc2", "objc2-foundation", "objc2-ui-kit", @@ -3799,6 +3754,20 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -3841,16 +3810,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a65843dfefbafd3c879c683306959a6de478443ffe9c9adf02f5976432402d7" -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "primeorder" version = "0.13.6" @@ -3907,6 +3866,16 @@ name = "procfs-core" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" +dependencies = [ + "bitflags", + "hex", +] + +[[package]] +name = "procfs-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ "bitflags", "hex", @@ -4467,14 +4436,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "relay-crash" -version = "26.6.0" -dependencies = [ - "bindgen", - "cmake", -] - [[package]] name = "relay-dynamic-config" version = "26.6.0" @@ -4631,8 +4592,8 @@ name = "relay-log" version = "26.6.0" dependencies = [ "console 0.15.11", + "minidumper-child", "relay-common", - "relay-crash", "sentry", "sentry-core", "serde", @@ -5214,12 +5175,6 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - [[package]] name = "rustc_version" version = "0.4.1" @@ -5239,7 +5194,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5394,7 +5349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6271,7 +6226,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6716,6 +6671,15 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uds" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885c31f06fce836457fe3ef09a59f83fe8db95d270b11cd78f40a4666c4d1661" +dependencies = [ + "libc", +] + [[package]] name = "uname" version = "0.1.1" @@ -7068,7 +7032,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index aedea8a1498..55df1e3da4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,6 @@ relay-cogs = { path = "relay-cogs" } relay-common = { path = "relay-common" } relay-config = { path = "relay-config" } relay-conventions = { path = "relay-conventions" } -relay-crash = { path = "relay-crash" } relay-dynamic-config = { path = "relay-dynamic-config" } relay-event-derive = { path = "relay-event-derive" } relay-event-normalization = { path = "relay-event-normalization" } @@ -96,7 +95,6 @@ axum = "0.8" axum-extra = "0.12" axum-server = "0.8" backon = { version = "1", default-features = false } -bindgen = "0.72" brotli = "7" bytecount = "0.6" bytes = "1" @@ -107,7 +105,6 @@ chrono = { version = "0.4", default-features = false, features = [ ] } clap = "4" clap_complete = "4" -cmake = "0.1" console = "0.15" cookie = "0.18" criterion = "0.5" @@ -148,6 +145,7 @@ memchr = "2" mimalloc = { version = "0.1", features = ["v3"] } mime = "0.3" minidump = "0.26" +minidumper-child = "0.4" multer = "3" metrics = "0.24" metrics-exporter-dogstatsd = "0.9" diff --git a/relay-crash/Cargo.toml b/relay-crash/Cargo.toml deleted file mode 100644 index 4ffa4b6515e..00000000000 --- a/relay-crash/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "relay-crash" -authors = ["Sentry "] -description = "Native crash reporting for Relay" -homepage = "https://getsentry.github.io/relay/" -repository = "https://github.com/getsentry/relay" -version = "26.6.0" -edition = "2024" -build = "build.rs" -license-file = "../LICENSE.md" -publish = false - -[lints] -workspace = true - -[dependencies] - -[build-dependencies] -bindgen = { workspace = true } -cmake = { workspace = true } diff --git a/relay-crash/build.rs b/relay-crash/build.rs deleted file mode 100644 index f5f14eb90dd..00000000000 --- a/relay-crash/build.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::path::{Path, PathBuf}; -use std::process::Command; - -fn main() { - // sentry-native dependencies - match std::env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { - "macos" => println!("cargo:rustc-link-lib=dylib=c++"), - "linux" => println!("cargo:rustc-link-lib=dylib=stdc++"), - _ => return, // allow building with --all-features, fail during runtime - } - - if !Path::new("sentry-native/.git").exists() { - let _ = Command::new("git") - .args(["submodule", "update", "--init", "--recursive"]) - .status(); - } - - let destination = cmake::Config::new("sentry-native") - // we never need a debug build of sentry-native - .profile("RelWithDebInfo") - // always build breakpad regardless of platform defaults - .define("SENTRY_BACKEND", "breakpad") - // inject a custom transport instead of curl - .define("SENTRY_TRANSPORT", "none") - // build a static library - .define("BUILD_SHARED_LIBS", "OFF") - // disable additional targets - .define("SENTRY_BUILD_TESTS", "OFF") - .define("SENTRY_BUILD_EXAMPLES", "OFF") - // cmake sets the install dir to `lib64` on some platforms. since we are not installing to - // `/usr` or `/usr/local`, we can choose a stable directory for the link-search below. - .define("CMAKE_INSTALL_LIBDIR", "lib") - .build(); - - println!( - "cargo:rustc-link-search=native={}", - destination.join("lib").display() - ); - println!("cargo:rustc-link-lib=static=breakpad_client"); - println!("cargo:rustc-link-lib=static=sentry"); - - let bindings = bindgen::Builder::default() - .header("sentry-native/include/sentry.h") - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - .generate() - .expect("Unable to generate bindings"); - - let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_dir.join("bindings.rs")) - .expect("Couldn't write bindings"); -} diff --git a/relay-crash/sentry-native b/relay-crash/sentry-native deleted file mode 160000 index ccef7125b37..00000000000 --- a/relay-crash/sentry-native +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ccef7125b3783210f44ee6b100df7278e6ba3eff diff --git a/relay-crash/src/lib.rs b/relay-crash/src/lib.rs deleted file mode 100644 index 949e59d8fb6..00000000000 --- a/relay-crash/src/lib.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! 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 = unsafe { native::sentry_envelope_serialize(envelope, &mut len) }; - - if !buf.is_null() && len > 0 { - let transport: Transport = unsafe { std::mem::transmute(tx_pointer) }; - transport(unsafe { std::slice::from_raw_parts(buf as *const u8, len) }); - } - - unsafe { - 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, - 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"); - } -} diff --git a/relay-log/Cargo.toml b/relay-log/Cargo.toml index b78c0c5bd9d..7cae6e789f9 100644 --- a/relay-log/Cargo.toml +++ b/relay-log/Cargo.toml @@ -13,10 +13,18 @@ build = "build.rs" [lints] workspace = true +[features] +default = [] +test = ["dep:tracing-subscriber"] +init = ["dep:console", "dep:sentry", "dep:serde", "dep:tracing-subscriber"] +crash-handler = ["init", "dep:minidumper-child"] +sentry = ["dep:sentry"] + [dependencies] -console = { workspace = true, optional = true } relay-common = { workspace = true } -relay-crash = { workspace = true, optional = true } + +console = { workspace = true, optional = true } +minidumper-child = { workspace = true, optional = true } sentry = { workspace = true, features = [ "debug-images", "tracing", @@ -30,10 +38,3 @@ tracing-subscriber = { workspace = true, features = [ "json", "time", ], optional = true } - -[features] -default = [] -test = ["dep:tracing-subscriber"] -init = ["dep:console", "dep:sentry", "dep:serde", "dep:tracing-subscriber"] -crash-handler = ["init", "dep:relay-crash"] -sentry = ["dep:sentry"] diff --git a/relay-log/src/crash.rs b/relay-log/src/crash.rs new file mode 100644 index 00000000000..073ce0a6617 --- /dev/null +++ b/relay-log/src/crash.rs @@ -0,0 +1,96 @@ +const CRASH_REPORTER_PROCESS_ENV_VAR: &str = "_RELAY_CRASH_REPORTER_PROCESS"; + +/// Returns `true` if the current process is the crash reporting process. +pub fn is_crash_reporter_process() -> bool { + cfg!(feature = "crash-handler") && std::env::var_os(CRASH_REPORTER_PROCESS_ENV_VAR).is_some() +} + +/// Initializes a secondary watcher process for reporting fatal crashes. +/// +/// This creates a secondary process, spawned from the same binary, communicating +/// with the original process via IPC. If the primary process crashes, +/// the secondary process will capture a minidump and report it to Sentry, delaying +/// the shutdown of the crashed process until the minidump has been uploaded. +#[cfg(feature = "crash-handler")] +pub fn init(config: &crate::SentryConfig, client: crate::sentry::Client) { + use crate::sentry::{self, protocol}; + + let Some(db) = config._crash_db.clone() else { + return; + }; + + let child = minidumper_child::MinidumperChild::new() + .with_server_env_var(CRASH_REPORTER_PROCESS_ENV_VAR.to_owned()) + .with_crashes_dir(db.clone()) + .on_process(|command| { + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + command.arg0("relay-crash"); + } + // Skip arg0, the process name. + command.args(std::env::args().skip(1)); + }) + .on_minidump(move |buffer, path| { + crate::info!("Minidump captured ({})", path.display()); + + crate::with_scope( + |scope| { + // Remove event.process because this event came from the + // main app process + scope.remove_extra("event.process"); + + let filename = path + .file_name() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_else(|| "minidump.dmp".to_owned()); + + scope.add_attachment(protocol::Attachment { + buffer, + filename, + ty: Some(protocol::AttachmentType::Minidump), + ..Default::default() + }); + }, + || { + sentry::capture_event(protocol::Event { + level: protocol::Level::Fatal, + ..Default::default() + }) + }, + ); + + // We need to flush because the server will exit after this closure returns + client.flush(Some(std::time::Duration::from_secs(15))); + }); + + // Make sure the implementations agree on the detection. + debug_assert_eq!( + is_crash_reporter_process(), + child.is_crash_reporter_process() + ); + + if child.is_crash_reporter_process() { + // Set the `event.process` so that it's obvious when Rust events come from + // the crash reporter process rather than the main app process + sentry::configure_scope(|scope| { + scope.set_extra("event.process", "crash-reporter".to_owned().into()); + }); + } else { + crate::info!("Initializing crash handler in {}", db.display()); + } + + match child.spawn() { + Ok(handle) => { + crate::info!("Crash handler initialized"); + // Dropping the handle would detach the crash handler not catching any crashes. + std::mem::forget(handle); + } + Err(err) => { + crate::warn!( + error = &err as &dyn std::error::Error, + "Failed to initialize crash reporting process" + ); + } + } +} diff --git a/relay-log/src/lib.rs b/relay-log/src/lib.rs index c85b2861a2a..b289c7451d4 100644 --- a/relay-log/src/lib.rs +++ b/relay-log/src/lib.rs @@ -122,6 +122,7 @@ mod test; #[cfg(feature = "test")] pub use test::*; +mod crash; mod utils; // Expose the minimal log facade. #[doc(inline)] diff --git a/relay-log/src/setup.rs b/relay-log/src/setup.rs index 5ee602e6b52..ec2c92abc0f 100644 --- a/relay-log/src/setup.rs +++ b/relay-log/src/setup.rs @@ -14,6 +14,8 @@ use serde::{Deserialize, Serialize}; use tracing::level_filters::LevelFilter; use tracing_subscriber::{EnvFilter, Layer, prelude::*}; +use crate::crash; + /// The full release name including the Relay version and SHA. const RELEASE: &str = std::env!("RELAY_RELEASE"); @@ -244,22 +246,6 @@ impl Default for SentryConfig { } } -/// Captures an envelope from the native crash reporter using the main Sentry SDK. -#[cfg(feature = "crash-handler")] -fn capture_native_envelope(data: &[u8]) { - if let Some(client) = sentry::Hub::main().client() { - match sentry::Envelope::from_bytes_raw(data.to_owned()) { - Ok(envelope) => client.send_envelope(envelope), - Err(error) => { - let error = &error as &dyn std::error::Error; - crate::error!(error, "failed to capture crash") - } - } - } else { - crate::error!("failed to capture crash: no sentry client registered"); - } -} - /// Configures the given log level for all of Relay's crates. fn get_default_filters() -> EnvFilter { // Configure INFO as default, except for crates that are very spammy on INFO level. @@ -382,33 +368,26 @@ pub unsafe fn init(config: &LogConfig, sentry: &SentryConfig) { })); } - crate::info!( - release = RELEASE, - server_name = sentry.server_name.as_deref(), - environment = sentry.environment.as_deref(), - traces_sample_rate, - "Initialized Sentry client options" - ); + if !crash::is_crash_reporter_process() { + crate::info!( + release = RELEASE, + server_name = sentry.server_name.as_deref(), + environment = sentry.environment.as_deref(), + traces_sample_rate, + "Initialized Sentry client options" + ); + } let guard = sentry::init(options); - // Keep the client initialized. The client is flushed manually in `main`. - std::mem::forget(guard); - } - - // Initialize native crash reporting after the Rust SDK, so that `capture_native_envelope` has - // access to an initialized Hub to capture crashes from the previous run. - #[cfg(feature = "crash-handler")] - { - if let Some(dsn) = sentry.enabled_dsn().map(|d| d.to_string()) - && let Some(db) = sentry._crash_db.as_deref() + // Initialize native crash reporting after the Rust SDK, so that `capture_native_envelope` has + // access to an initialized Hub to capture crashes from the previous run. + #[cfg(feature = "crash-handler")] { - crate::info!("initializing crash handler in {}", db.display()); - relay_crash::CrashHandler::new(dsn.as_str(), db) - .transport(capture_native_envelope) - .release(Some(RELEASE)) - .environment(sentry.environment.as_deref()) - .install(); + crate::crash::init(sentry, (*guard).clone()); } + + // Keep the client initialized. The client is flushed manually in `main`. + std::mem::forget(guard); } } diff --git a/relay/src/main.rs b/relay/src/main.rs index 486b8cd04f4..aa359dc72da 100644 --- a/relay/src/main.rs +++ b/relay/src/main.rs @@ -38,7 +38,6 @@ //! - [`relay-common`]: Common utilities and crate re-exports. //! - [`relay-config`]: Static configuration for the CLI and server. //! - `relay-conventions`: Attribute definitions extracted from [`sentry-conventions`](https://github.com/getsentry/sentry-conventions). -//! - [`relay-crash`]: Crash reporting for the Relay server. //! - [`relay-dynamic-config`]: Dynamic configuration passed from Sentry. //! - [`relay-event-derive`]: Derive for visitor traits on the Event schema. //! - [`relay-event-normalization`]: Event normalization and processing. @@ -81,7 +80,6 @@ //! [`relay-common`]: ../relay_common/index.html //! [`relay-config`]: ../relay_config/index.html //! [`relay-cogs`]: ../relay_cogs/index.html -//! [`relay-crash`]: ../relay_crash/index.html //! [`relay-dynamic-config`]: ../relay_dynamic_config/index.html //! [`relay-event-derive`]: ../relay_event_derive/index.html //! [`relay-event-normalization`]: ../relay_event_normalization/index.html