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}