sentry_failure/
lib.rs

1//! Adds support for capturing Sentry errors from `failure` types.
2//!
3//! Failure errors and `Fail` objects can be logged with the failure integration.
4//! This works really well if you use the `failure::Error` type or if you have
5//! `failure::Fail` objects that use the failure context internally to gain a
6//! backtrace.
7//!
8//! # Example
9//!
10//! ```no_run
11//! # use sentry_core as sentry;
12//! # fn function_that_might_fail() -> Result<(), failure::Error> { Ok(()) }
13//! use sentry_failure::capture_error;
14//! # fn test() -> Result<(), failure::Error> {
15//! let result = match function_that_might_fail() {
16//!     Ok(result) => result,
17//!     Err(err) => {
18//!         capture_error(&err);
19//!         return Err(err);
20//!     }
21//! };
22//! # Ok(()) }
23//! ```
24//!
25//! To capture fails and not errors use `capture_fail`.
26
27#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
28#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
29#![warn(missing_docs)]
30#![deny(unsafe_code)]
31#![warn(missing_doc_code_examples)]
32#![allow(deprecated)]
33
34use std::panic::PanicInfo;
35
36use failure::{Error, Fail};
37use sentry_backtrace::parse_stacktrace;
38use sentry_core::parse_type_from_debug;
39use sentry_core::protocol::{Event, Exception, Level};
40use sentry_core::types::Uuid;
41use sentry_core::{ClientOptions, Hub, Integration};
42
43/// The Sentry Failure Integration.
44#[derive(Debug, Default)]
45#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
46pub struct FailureIntegration;
47
48impl FailureIntegration {
49    /// Creates a new Failure Integration.
50    pub fn new() -> Self {
51        Self::default()
52    }
53}
54
55impl Integration for FailureIntegration {
56    fn name(&self) -> &'static str {
57        "failure"
58    }
59
60    fn setup(&self, cfg: &mut ClientOptions) {
61        cfg.in_app_exclude.push("failure::");
62        cfg.extra_border_frames.extend_from_slice(&[
63            "failure::error_message::err_msg",
64            "failure::backtrace::Backtrace::new",
65            "failure::backtrace::internal::InternalBacktrace::new",
66            "failure::Fail::context",
67        ]);
68    }
69}
70
71/// Extracts a Sentry `Event` from a `PanicInfo`.
72///
73/// Creates a new Sentry `Event` when the panic has a `failure::Error` payload.
74/// This is for use with the `sentry-panic` integration, and is enabled by
75/// default in `sentry`.
76///
77/// # Examples
78///
79/// ```
80/// let panic_integration =
81///     sentry_panic::PanicIntegration::new().add_extractor(sentry_failure::panic_extractor);
82/// ```
83#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
84pub fn panic_extractor(info: &PanicInfo<'_>) -> Option<Event<'static>> {
85    let error = info.payload().downcast_ref::<Error>()?;
86    Some(Event {
87        level: Level::Fatal,
88        ..event_from_error(error)
89    })
90}
91
92/// This converts a single fail instance into an exception.
93///
94/// This is typically not very useful as the `event_from_error` and
95/// `event_from_fail` methods will assemble an entire event with all the
96/// causes of a failure, however for certain more complex situations where
97/// fails are contained within a non fail error type that might also carry
98/// useful information it can be useful to call this method instead.
99#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
100pub fn exception_from_single_fail<F: Fail + ?Sized>(
101    f: &F,
102    bt: Option<&failure::Backtrace>,
103) -> Exception {
104    let dbg = format!("{:?}", f);
105    Exception {
106        ty: parse_type_from_debug(&dbg).to_owned(),
107        value: Some(f.to_string()),
108        stacktrace: bt
109            // format the stack trace with alternate debug to get addresses
110            .map(|bt| format!("{:#?}", bt))
111            .and_then(|x| parse_stacktrace(&x)),
112        ..Default::default()
113    }
114}
115
116/// Helper function to create an event from a `failure::Error`.
117#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
118pub fn event_from_error(err: &failure::Error) -> Event<'static> {
119    let mut exceptions: Vec<_> = err
120        .iter_chain()
121        .enumerate()
122        .map(|(idx, cause)| {
123            let bt = match cause.backtrace() {
124                Some(bt) => Some(bt),
125                None if idx == 0 => Some(err.backtrace()),
126                None => None,
127            };
128            exception_from_single_fail(cause, bt)
129        })
130        .collect();
131    exceptions.reverse();
132
133    Event {
134        exception: exceptions.into(),
135        level: Level::Error,
136        ..Default::default()
137    }
138}
139
140/// Helper function to create an event from a `failure::Fail`.
141#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
142pub fn event_from_fail<F: Fail + ?Sized>(fail: &F) -> Event<'static> {
143    let mut exceptions = vec![exception_from_single_fail(fail, fail.backtrace())];
144
145    let mut ptr: Option<&dyn Fail> = None;
146    while let Some(cause) = ptr.map(Fail::cause).unwrap_or_else(|| fail.cause()) {
147        exceptions.push(exception_from_single_fail(cause, cause.backtrace()));
148        ptr = Some(cause);
149    }
150
151    exceptions.reverse();
152    Event {
153        exception: exceptions.into(),
154        level: Level::Error,
155        ..Default::default()
156    }
157}
158
159/// Captures a boxed failure (`failure::Error`).
160///
161/// This dispatches to the current hub.
162#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
163pub fn capture_error(err: &Error) -> Uuid {
164    Hub::with_active(|hub| FailureHubExt::capture_error(hub.as_ref(), err))
165}
166
167/// Captures a `failure::Fail`.
168///
169/// This dispatches to the current hub.
170#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
171pub fn capture_fail<F: Fail + ?Sized>(fail: &F) -> Uuid {
172    Hub::with_active(|hub| hub.capture_fail(fail))
173}
174
175/// Hub extension methods for working with failure.
176#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
177pub trait FailureHubExt {
178    /// Captures a boxed failure (`failure::Error`).
179    fn capture_error(&self, err: &Error) -> Uuid;
180    /// Captures a `failure::Fail`.
181    fn capture_fail<F: Fail + ?Sized>(&self, fail: &F) -> Uuid;
182}
183
184impl FailureHubExt for Hub {
185    fn capture_error(&self, err: &Error) -> Uuid {
186        self.capture_event(event_from_error(err))
187    }
188
189    fn capture_fail<F: Fail + ?Sized>(&self, fail: &F) -> Uuid {
190        self.capture_event(event_from_fail(fail))
191    }
192}
193
194/// Extension trait providing methods to unwrap a result, preserving backtraces from the
195/// underlying error in the event of a panic.
196#[deprecated = "The `failure` integration is deprecated and will be removed in the future."]
197pub trait FailureResultExt {
198    /// Type of the success case
199    type Value;
200    /// Unwraps the result, panicking if it contains an error. Any backtrace attached to the
201    /// error will be preserved with the panic.
202    fn fallible_unwrap(self) -> Self::Value;
203}
204
205impl<T, E> FailureResultExt for Result<T, E>
206where
207    E: Into<Error>,
208{
209    type Value = T;
210    fn fallible_unwrap(self) -> Self::Value {
211        match self {
212            Ok(v) => v,
213            Err(e) => {
214                let e: Error = e.into();
215                panic!(e)
216            }
217        }
218    }
219}