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()?.expose_secret().as_str();
22
23    let traces_sample_rate = config.traces_sample_rate;
24    let inherit_sampling_decision = config.inherit_sampling_decision;
25    let mut options = sentry::ClientOptions::new()
26        .release(RELEASE)
27        .sample_rate(config.sample_rate)
28        .traces_sampler(move |ctx| {
29            if let Some(sampled) = ctx.sampled()
30                && inherit_sampling_decision
31            {
32                f32::from(sampled)
33            } else {
34                traces_sample_rate
35            }
36        })
37        .attach_stacktrace(config.attach_stacktrace)
38        .debug(config.debug)
39        .transport_channel_capacity(config.transport_channel_capacity);
40
41    match dsn.parse::<sentry::types::Dsn>() {
42        Ok(_) => options = options.dsn(dsn),
43        Err(error) => {
44            // Sentry is initialized before the tracing subscriber, so a `warn!` here would be
45            // dropped. Write to stderr instead to make the misconfiguration visible.
46            eprintln!("WARN: invalid Sentry DSN, error reporting is disabled: {error}");
47        }
48    }
49    if let Some(environment) = &config.environment {
50        options = options.environment(environment.clone());
51    }
52    if let Some(server_name) = &config.server_name {
53        options = options.server_name(server_name.clone());
54    }
55
56    let guard = sentry::init(options);
57
58    sentry::configure_scope(|scope| {
59        for (k, v) in &config.tags {
60            scope.set_tag(k, v);
61        }
62    });
63
64    Some(guard)
65}