Skip to main content

objectstore_server/
observability.rs

1//! Initialization of Sentry error reporting.
2//!
3//! Call [`init_sentry`] during server startup before creating the Tokio runtime so it can
4//! instrument async tasks from the start. Tracing subscriber initialization (including the
5//! Sentry tracing layer) is handled by [`objectstore_log::init`].
6
7use secrecy::ExposeSecret;
8
9use crate::config::Config;
10
11/// The full release name including the objectstore version and SHA.
12const RELEASE: &str = std::env!("OBJECTSTORE_RELEASE");
13
14/// Initializes the Sentry error-reporting client, if a DSN is configured.
15///
16/// Returns `None` when `config.sentry.dsn` is not set. The returned
17/// [`sentry::ClientInitGuard`] must be kept alive for the duration of the process;
18/// dropping it flushes the event queue and shuts down the Sentry client.
19pub fn init_sentry(config: &Config) -> Option<sentry::ClientInitGuard> {
20    let config = &config.sentry;
21    let dsn = config.dsn.as_ref()?;
22
23    let dsn = match dsn.expose_secret().parse() {
24        Ok(dsn) => Some(dsn),
25        Err(error) => {
26            // Sentry is initialized before the tracing subscriber, so a `warn!` here would be
27            // dropped. Write to stderr instead to make the misconfiguration visible.
28            eprintln!("WARN: invalid Sentry DSN, error reporting is disabled: {error}");
29            None
30        }
31    };
32
33    let guard = sentry::init(sentry::ClientOptions {
34        dsn,
35        release: Some(RELEASE.into()),
36        environment: config.environment.clone(),
37        server_name: config.server_name.clone(),
38        sample_rate: config.sample_rate,
39        traces_sampler: {
40            let traces_sample_rate = config.traces_sample_rate;
41            let inherit_sampling_decision = config.inherit_sampling_decision;
42            Some(std::sync::Arc::new(move |ctx| {
43                if let Some(sampled) = ctx.sampled()
44                    && inherit_sampling_decision
45                {
46                    f32::from(sampled)
47                } else {
48                    traces_sample_rate
49                }
50            }))
51        },
52        enable_logs: true,
53        debug: config.debug,
54        ..Default::default()
55    });
56
57    sentry::configure_scope(|scope| {
58        for (k, v) in &config.tags {
59            scope.set_tag(k, v);
60        }
61    });
62
63    Some(guard)
64}