autopush_common/
sentry.rs

1use std::{collections::BTreeMap, error::Error};
2
3use crate::errors::ReportableError;
4
5/// Return a `sentry::::ClientOptions` w/ the `debug-images` integration
6/// disabled
7pub fn client_options() -> sentry::ClientOptions {
8    // debug-images conflicts w/ our debug = 1 rustc build option:
9    // https://github.com/getsentry/sentry-rust/issues/574
10    let mut opts = sentry::apply_defaults(sentry::ClientOptions::default());
11    opts.integrations.retain(|i| i.name() != "debug-images");
12    opts.default_integrations = false;
13    opts
14}
15
16/// Custom `sentry::event_from_error` for `ReportableError`
17///
18/// `std::error::Error` doesn't support backtraces, thus `sentry::event_from_error`
19/// doesn't either. This function works against `ReportableError` instead to
20/// extract backtraces, etc. from it and its chain of `reportable_source's.
21///
22/// A caveat of this function is that it cannot extract
23/// `ReportableError`s/backtraces, etc. that occur in a chain after a
24/// `std::error::Error` occurs: as `std::error::Error::source` only allows
25/// downcasting to a concrete type, not `dyn ReportableError`.
26pub fn event_from_error(
27    mut reportable_err: &dyn ReportableError,
28) -> sentry::protocol::Event<'static> {
29    let mut exceptions = vec![];
30    let mut tags = BTreeMap::new();
31    let mut extra = BTreeMap::new();
32
33    // Gather reportable_source()'s for their backtraces, etc
34    loop {
35        exceptions.push(exception_from_reportable_error(reportable_err));
36        for (k, v) in reportable_err.tags() {
37            // NOTE: potentially overwrites other tags/extras from this chain
38            tags.insert(k.to_owned(), v);
39        }
40        for (k, v) in reportable_err.extras() {
41            extra.insert(k.to_owned(), v.into());
42        }
43        reportable_err = match reportable_err.reportable_source() {
44            Some(reportable_err) => reportable_err,
45            None => break,
46        };
47    }
48
49    // Then fallback to source() for remaining Errors
50    let mut source = reportable_err.source();
51    while let Some(err) = source {
52        exceptions.push(exception_from_error(err));
53        source = err.source();
54    }
55
56    exceptions.reverse();
57    sentry::protocol::Event {
58        exception: exceptions.into(),
59        level: sentry::protocol::Level::Error,
60        tags,
61        extra,
62        ..Default::default()
63    }
64}
65
66/// Custom `exception_from_error` support function for `ReportableError`
67///
68/// Based moreso on sentry_failure's `exception_from_single_fail`. Includes a
69/// stacktrace if available.
70fn exception_from_reportable_error(err: &dyn ReportableError) -> sentry::protocol::Exception {
71    let mut exception = exception_from_error(err);
72    if let Some(backtrace) = err.backtrace() {
73        exception.stacktrace = sentry_backtrace::backtrace_to_stacktrace(backtrace)
74    }
75    exception
76}
77
78/// Exact copy of sentry's unfortunately private `exception_from_error`
79fn exception_from_error<E>(err: &E) -> sentry::protocol::Exception
80where
81    E: Error + ?Sized,
82{
83    let dbg = format!("{err:?}");
84    sentry::protocol::Exception {
85        ty: sentry::parse_type_from_debug(&dbg).to_owned(),
86        value: Some(err.to_string()),
87        ..Default::default()
88    }
89}