Skip to main content

mas_axum_utils/
sentry.rs

1// Copyright 2025, 2026 Element Creations Ltd.
2// Copyright 2024, 2025 New Vector Ltd.
3// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
4//
5// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6// Please see LICENSE files in the repository root for full details.
7
8use std::convert::Infallible;
9
10use axum::response::{IntoResponseParts, ResponseParts};
11use sentry::types::Uuid;
12
13/// A wrapper to include a Sentry event ID in the response headers.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct SentryEventID(Uuid);
16
17impl SentryEventID {
18    /// Create a new Sentry event ID header for the last event on the hub.
19    ///
20    /// Returns `None` if the last event was not sent, e.g. because it was
21    /// sampled out, in which case Sentry records a nil ID.
22    pub fn for_last_event() -> Option<Self> {
23        sentry::last_event_id().filter(|id| !id.is_nil()).map(Self)
24    }
25}
26
27impl IntoResponseParts for SentryEventID {
28    type Error = Infallible;
29    fn into_response_parts(self, mut res: ResponseParts) -> Result<ResponseParts, Self::Error> {
30        res.headers_mut()
31            .insert("X-Sentry-Event-ID", self.0.to_string().parse().unwrap());
32
33        Ok(res)
34    }
35}
36
37/// Record an error. It will emit a tracing event with the error level if
38/// matches the pattern, warning otherwise. It also returns the Sentry event ID
39/// if the error was recorded.
40#[macro_export]
41macro_rules! record_error {
42    ($error:expr, !) => {{
43        let error = &$error;
44        tracing::warn!(error = error as &dyn std::error::Error, "{error}");
45        Option::<$crate::sentry::SentryEventID>::None
46    }};
47
48    ($error:expr) => {{
49        let error = &$error;
50        tracing::error!(error = error as &dyn std::error::Error, "{error}");
51
52        // With the `sentry-tracing` integration, Sentry should have
53        // captured an error, so let's extract the last event ID from the
54        // current hub
55        $crate::sentry::SentryEventID::for_last_event()
56    }};
57
58    ($error:expr, $pattern:pat) => {
59        if let $pattern = $error {
60            record_error!($error)
61        } else {
62            record_error!($error, !)
63        }
64    };
65}