Skip to main content

modo/tracing/
sentry.rs

1use crate::error::Result;
2use crate::runtime::Task;
3
4#[cfg(feature = "sentry")]
5use serde::Deserialize;
6
7/// Sentry error and performance reporting settings.
8///
9/// Requires the `sentry` feature. Embed in `Config::sentry` and supply
10/// a valid DSN to enable Sentry.
11///
12/// ```yaml
13/// tracing:
14///   sentry:
15///     dsn: "https://key@sentry.io/project"
16///     environment: production
17///     sample_rate: 1.0
18///     traces_sample_rate: 0.1
19/// ```
20#[cfg(feature = "sentry")]
21#[non_exhaustive]
22#[derive(Debug, Clone, Deserialize)]
23#[serde(default)]
24pub struct SentryConfig {
25    /// Sentry DSN. When empty, Sentry is not initialised.
26    pub dsn: String,
27
28    /// Environment tag reported to Sentry (e.g. `"production"`).
29    ///
30    /// Defaults to the value of `APP_ENV` (see [`crate::config::env`]).
31    pub environment: String,
32
33    /// Fraction of error events to send (0.0–1.0). Defaults to `1.0`.
34    pub sample_rate: f32,
35
36    /// Fraction of transactions to trace for performance monitoring (0.0–1.0).
37    /// Defaults to `0.1`.
38    pub traces_sample_rate: f32,
39}
40
41#[cfg(feature = "sentry")]
42impl Default for SentryConfig {
43    fn default() -> Self {
44        Self {
45            dsn: String::new(),
46            environment: crate::config::env(),
47            sample_rate: 1.0,
48            traces_sample_rate: 0.1,
49        }
50    }
51}
52
53/// RAII guard that keeps the tracing subscriber and Sentry client alive.
54///
55/// Returned by [`crate::tracing::init`]. Hold this value for the entire
56/// lifetime of the process — typically by passing it to [`crate::run!`]
57/// or calling [`Task::shutdown`] at the end of `main`.
58///
59/// Dropping the guard without calling `shutdown` is safe but may not flush
60/// all buffered Sentry events.
61pub struct TracingGuard {
62    #[cfg(feature = "sentry")]
63    _sentry: Option<sentry::ClientInitGuard>,
64}
65
66impl Default for TracingGuard {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl TracingGuard {
73    /// Create a guard with no active Sentry client.
74    pub fn new() -> Self {
75        Self {
76            #[cfg(feature = "sentry")]
77            _sentry: None,
78        }
79    }
80
81    /// Create a guard that owns an active Sentry client.
82    ///
83    /// Requires the `sentry` feature.
84    #[cfg(feature = "sentry")]
85    pub fn with_sentry(guard: sentry::ClientInitGuard) -> Self {
86        Self {
87            _sentry: Some(guard),
88        }
89    }
90}
91
92impl Task for TracingGuard {
93    /// Flush pending Sentry events (up to 5 seconds) and release the client.
94    async fn shutdown(self) -> Result<()> {
95        #[cfg(feature = "sentry")]
96        if let Some(guard) = self._sentry {
97            guard.close(Some(std::time::Duration::from_secs(5)));
98        }
99        Ok(())
100    }
101}