swh_graph_grpc_server/
sentry.rs

1// Copyright (C) 2024  The Software Heritage developers
2// See the AUTHORS file at the top-level directory of this distribution
3// License: GNU General Public License version 3, or any later version
4// See top-level LICENSE file for more information
5
6use std::env::VarError;
7
8use anyhow::Result;
9use sentry::ClientInitGuard;
10use sentry_tracing::EventFilter;
11use tracing::{Level, Subscriber};
12
13/// Parses an environment variable as a boolean, like `swh.core.sentry.override_with_bool_envvar`
14fn parse_bool_env_var(var_name: &'static str, default: bool) -> bool {
15    _parse_bool_env_var(var_name, std::env::var(var_name), default)
16}
17
18/// Testable variant of [`parse_bool_env_var`] that takes both the variable name and its value
19fn _parse_bool_env_var(
20    var_name: &'static str,
21    value: Result<String, VarError>,
22    default: bool,
23) -> bool {
24    match value.as_deref() {
25        Ok("t" | "true" | "y" | "yes" | "1") => true,
26        Ok("f" | "false" | "n" | "no" | "0") => false,
27        Ok(value) => {
28            log::warn!("Could not interpret environment variable {var_name}={value} as boolean, using default value {default}");
29            default
30        }
31        Err(VarError::NotPresent) => default,
32        Err(e) => {
33            log::warn!("Could not interpret environment variable {var_name} ({e}), using default value {default}");
34            default
35        }
36    }
37}
38
39pub fn setup<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>>() -> (
40    Result<ClientInitGuard, VarError>,
41    impl tracing_subscriber::layer::Layer<S>,
42) {
43    let guard = std::env::var("SWH_SENTRY_DSN").map(|sentry_dsn| {
44        sentry::init((
45            sentry_dsn,
46            sentry::ClientOptions {
47                release: sentry::release_name!(),
48                environment: std::env::var("SWH_SENTRY_ENVIRONMENT").ok().map(Into::into),
49                ..Default::default()
50            },
51        ))
52    });
53
54    if let Err(ref e) = guard {
55        log::error!("Could not initialize Sentry: {e}");
56    }
57
58    let mut sentry_layer = sentry_tracing::layer();
59
60    if parse_bool_env_var("SWH_SENTRY_DISABLE_LOGGING_EVENTS", false) {
61        sentry_layer = sentry_layer.event_filter(|md| match *md.level() {
62            Level::ERROR => EventFilter::Breadcrumb, // replaces the default (LogFilter::Exception)
63            Level::WARN | Level::INFO => EventFilter::Breadcrumb,
64            Level::DEBUG | Level::TRACE => EventFilter::Ignore,
65        });
66    }
67    (guard, sentry_layer)
68}
69
70#[test]
71fn test_parse_bool_env_var() {
72    assert!(_parse_bool_env_var("foo", Ok("t".to_owned()), false));
73    assert!(_parse_bool_env_var("foo", Ok("t".to_owned()), true));
74    assert!(_parse_bool_env_var("foo", Ok("yes".to_owned()), false));
75    assert!(_parse_bool_env_var("foo", Ok("yes".to_owned()), true));
76
77    assert!(!_parse_bool_env_var("foo", Ok("f".to_owned()), false));
78    assert!(!_parse_bool_env_var("foo", Ok("f".to_owned()), true));
79    assert!(!_parse_bool_env_var("foo", Ok("no".to_owned()), false));
80    assert!(!_parse_bool_env_var("foo", Ok("no".to_owned()), true));
81
82    assert!(!_parse_bool_env_var("foo", Ok("invalid".to_owned()), false));
83    assert!(_parse_bool_env_var("foo", Ok("invalid".to_owned()), true));
84    assert!(!_parse_bool_env_var("foo", Ok("invalid".to_owned()), false));
85    assert!(_parse_bool_env_var("foo", Ok("invalid".to_owned()), true));
86
87    assert!(!_parse_bool_env_var(
88        "foo",
89        Err(VarError::NotPresent),
90        false
91    ));
92    assert!(_parse_bool_env_var("foo", Err(VarError::NotPresent), true));
93    assert!(!_parse_bool_env_var(
94        "foo",
95        Err(VarError::NotPresent),
96        false
97    ));
98    assert!(_parse_bool_env_var("foo", Err(VarError::NotPresent), true));
99}