Overwrites the default panic handler with an enhanced panic handler. The enhanced panic handler: Always emits a backtrace, regardless of how `RUST_BACKTRACE` was configured. Writes to stderr as atomically as possible, to minimize interleaving with concurrent log messages. Reports panics to Sentry. Sentry installs its own panic hook by default that reports the panic and then forwards it to the
()
| 132 | /// recover. Note that the `catch_unwind` function in the standard library is |
| 133 | /// **not** compatible with this improved panic handler. |
| 134 | pub fn install_enhanced_handler() { |
| 135 | panic::set_hook(Box::new(move |panic_info| { |
| 136 | // If we're catching an unwind, do nothing to let the unwind handler |
| 137 | // run. |
| 138 | let catching_unwind = CATCHING_UNWIND.with(|v| *v.borrow()); |
| 139 | #[cfg(feature = "async")] |
| 140 | let catching_unwind_async = CATCHING_UNWIND_ASYNC.try_with(|v| *v).unwrap_or(false); |
| 141 | #[cfg(not(feature = "async"))] |
| 142 | let catching_unwind_async = false; |
| 143 | if catching_unwind != 0 || catching_unwind_async { |
| 144 | // We're letting the unwind proceed without printing or reporting the |
| 145 | // panic. The location and backtrace would normally be lost here, |
| 146 | // because `catch_unwind` only recovers the panic payload (the |
| 147 | // message). If a caller has opted in via `catch_unwind_with_details`, |
| 148 | // stash those details now so they can be attached to the resulting |
| 149 | // error. We only pay the cost of capturing a backtrace when a panic |
| 150 | // actually occurs, which is the exceptional case. |
| 151 | let capture_details = CAPTURE_PANIC_DETAILS.with(|v| *v.borrow()) != 0; |
| 152 | if capture_details { |
| 153 | let location = panic_info.location().map(|loc| loc.to_string()); |
| 154 | let backtrace = Backtrace::force_capture().to_string(); |
| 155 | CAUGHT_PANIC_DETAILS.with(|details| { |
| 156 | *details.borrow_mut() = Some(PanicDetails { |
| 157 | location, |
| 158 | backtrace, |
| 159 | }); |
| 160 | }); |
| 161 | } |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | // Report the panic to Sentry. |
| 166 | // Note that we can't use `sentry_panic::panic_handler` because that requires the panic |
| 167 | // integration to be enabled. |
| 168 | sentry::Hub::with_active(|hub| { |
| 169 | let event = sentry_panic::PanicIntegration::new().event_from_panic_info(panic_info); |
| 170 | hub.capture_event(event); |
| 171 | if let Some(client) = hub.client() { |
| 172 | client.flush(None); |
| 173 | } |
| 174 | }); |
| 175 | |
| 176 | // can't use if cfg!() here because that will require chrono::Utc import |
| 177 | #[cfg(feature = "chrono")] |
| 178 | let timestamp = Utc::now().format("%Y-%m-%dT%H:%M:%S%.6fZ ").to_string(); |
| 179 | #[cfg(not(feature = "chrono"))] |
| 180 | let timestamp = String::new(); |
| 181 | |
| 182 | let thread = thread::current(); |
| 183 | let thread_name = thread.name().unwrap_or("<unnamed>"); |
| 184 | |
| 185 | let msg = match panic_info.payload().downcast_ref::<&'static str>() { |
| 186 | Some(s) => *s, |
| 187 | None => match panic_info.payload().downcast_ref::<String>() { |
| 188 | Some(s) => &s[..], |
| 189 | None => "Box<Any>", |
| 190 | }, |
| 191 | }; |