Skip to main content

modo/tracing/
sentry.rs

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