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