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
//! Adds support for capturing Sentry errors from [`anyhow::Error`].
//!
//! This integration adds a new event *source*, which allows you to create events directly
//! from an [`anyhow::Error`] struct. As it is only an event source it only needs to be
//! enabled using the `anyhow` cargo feature, it does not need to be enabled in the call to
//! [`sentry::init`](https://docs.rs/sentry/*/sentry/fn.init.html).
//!
//! This integration does not need to be installed, instead it provides an extra function to
//! capture [`anyhow::Error`], optionally exposing it as a method on the
//! [`sentry::Hub`](https://docs.rs/sentry/*/sentry/struct.Hub.html) using the
//! [`AnyhowHubExt`] trait.
//!
//! Like a plain [`std::error::Error`] being captured, [`anyhow::Error`] is captured with a
//! chain of all error sources, if present. See
//! [`sentry::capture_error`](https://docs.rs/sentry/*/sentry/fn.capture_error.html) for
//! details of this.
//!
//! # Example
//!
//! ```no_run
//! use sentry_anyhow::capture_anyhow;
//!
//! fn function_that_might_fail() -> anyhow::Result<()> {
//! Err(anyhow::anyhow!("some kind of error"))
//! }
//!
//! if let Err(err) = function_that_might_fail() {
//! capture_anyhow(&err);
//! }
//! ```
//!
//! # Features
//!
//! The `backtrace` feature will enable the corresponding feature in anyhow and allow you to
//! capture backtraces with your events. It is enabled by default.
//!
//! [`anyhow::Error`]: https://docs.rs/anyhow/*/anyhow/struct.Error.html
#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
#![warn(missing_docs)]
#![deny(unsafe_code)]
use sentry_core::protocol::Event;
use sentry_core::types::Uuid;
use sentry_core::Hub;
/// Captures an [`anyhow::Error`].
///
/// This will capture an anyhow error as a sentry event if a
/// [`sentry::Client`](../../struct.Client.html) is initialised, otherwise it will be a
/// no-op. The event is dispatched to the thread-local hub, with semantics as described in
/// [`Hub::current`].
///
/// See [module level documentation](index.html) for more information.
///
/// [`anyhow::Error`]: https://docs.rs/anyhow/*/anyhow/struct.Error.html
pub fn capture_anyhow(e: &anyhow::Error) -> Uuid {
Hub::with_active(|hub| hub.capture_anyhow(e))
}
/// Helper function to create an event from a `anyhow::Error`.
pub fn event_from_error(err: &anyhow::Error) -> Event<'static> {
let dyn_err: &dyn std::error::Error = err.as_ref();
// It's not mutated for not(feature = "backtrace")
#[allow(unused_mut)]
let mut event = sentry_core::event_from_error(dyn_err);
#[cfg(feature = "backtrace")]
{
// exception records are sorted in reverse
if let Some(exc) = event.exception.iter_mut().last() {
let backtrace = err.backtrace();
exc.stacktrace = sentry_backtrace::parse_stacktrace(&format!("{backtrace:#}"));
}
}
event
}
/// Hub extension methods for working with [`anyhow`].
pub trait AnyhowHubExt {
/// Captures an [`anyhow::Error`] on a specific hub.
fn capture_anyhow(&self, e: &anyhow::Error) -> Uuid;
}
impl AnyhowHubExt for Hub {
fn capture_anyhow(&self, anyhow_error: &anyhow::Error) -> Uuid {
let event = event_from_error(anyhow_error);
self.capture_event(event)
}
}
#[cfg(all(feature = "backtrace", test))]
mod tests {
use super::*;
#[test]
fn test_event_from_error_with_backtrace() {
std::env::set_var("RUST_BACKTRACE", "1");
let event = event_from_error(&anyhow::anyhow!("Oh jeez"));
let stacktrace = event.exception[0].stacktrace.as_ref().unwrap();
let found_test_fn = stacktrace
.frames
.iter()
.find(|frame| match &frame.function {
Some(f) => f.contains("test_event_from_error_with_backtrace"),
None => false,
});
assert!(found_test_fn.is_some());
}
#[test]
fn test_capture_anyhow_uses_event_from_error_helper() {
std::env::set_var("RUST_BACKTRACE", "1");
let err = &anyhow::anyhow!("Oh jeez");
let event = event_from_error(err);
let events = sentry::test::with_captured_events(|| {
capture_anyhow(err);
});
assert_eq!(event.exception, events[0].exception);
}
}