hook0_sentry_integration/
lib.rs

1// Force exposed items to be documented
2#![deny(missing_docs)]
3
4//! This is a collection of helpers related to Sentry.
5
6use log::{info, warn};
7use sentry::protocol::Value;
8use sentry::{configure_scope, ClientInitGuard, User};
9use std::collections::BTreeMap;
10
11/// Initialise a logger with default level at INFO
12fn mk_log_builder() -> env_logger::Builder {
13    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
14}
15
16/// Register Sentry logger as the global logger
17fn init_sentry_logger(crate_name: &'static str) {
18    let logger = sentry::integrations::log::SentryLogger::with_dest(mk_log_builder().build())
19        .filter(move |md| match (md.target(), md.level()) {
20            (_, log::Level::Error) => sentry::integrations::log::LogFilter::Event,
21            (target, _) if target == crate_name => sentry::integrations::log::LogFilter::Breadcrumb,
22            (_, log::Level::Warn) | (_, log::Level::Info) => {
23                sentry::integrations::log::LogFilter::Breadcrumb
24            }
25            (_, log::Level::Debug) | (_, log::Level::Trace) => {
26                sentry::integrations::log::LogFilter::Ignore
27            }
28        });
29
30    log::set_boxed_logger(Box::new(logger)).unwrap();
31    log::set_max_level(log::LevelFilter::Trace);
32}
33
34/// Initialize Sentry integration
35pub fn init(
36    crate_name: &'static str,
37    sentry_dsn: &Option<String>,
38    traces_sample_rate: &Option<f32>,
39) -> Option<ClientInitGuard> {
40    let client;
41    match sentry_dsn {
42        Some(dsn) => {
43            init_sentry_logger(crate_name);
44
45            client = sentry::init((
46                dsn.as_str(),
47                sentry::ClientOptions {
48                    send_default_pii: true,
49                    attach_stacktrace: true,
50                    debug: true,
51                    traces_sample_rate: traces_sample_rate.unwrap_or(0.0),
52                    ..Default::default()
53                },
54            ));
55
56            if client.is_enabled() {
57                info!("Sentry integration initialized");
58            } else {
59                unreachable!();
60            }
61
62            Some(client)
63        }
64        None => {
65            mk_log_builder().init();
66            warn!("Could not initialize Sentry integration");
67            None
68        }
69    }
70}
71
72const AUTH_TYPE_PROPERTY: &str = "auth_type";
73
74/// Use JWT claims to set the user to be used in reports
75pub fn set_user_from_jwt(id: &str) {
76    configure_scope(|scope| {
77        scope.set_user(Some(User {
78            id: Some(id.to_owned()),
79            other: BTreeMap::from_iter([(
80                AUTH_TYPE_PROPERTY.to_owned(),
81                Value::String("jwt".to_owned()),
82            )]),
83            ..Default::default()
84        }));
85    });
86}
87
88/// Use an application secret to set the user to be used in reports
89pub fn set_user_from_application_secret(application_id: &str) {
90    configure_scope(|scope| {
91        scope.set_user(Some(User {
92            id: Some(application_id.to_owned()),
93            other: BTreeMap::from_iter([(
94                AUTH_TYPE_PROPERTY.to_owned(),
95                Value::String("application_secret".to_owned()),
96            )]),
97            ..Default::default()
98        }));
99    });
100}
101
102/// Use a token ID to set the user to be used in reports
103pub fn set_user_from_token(token_id: &str) {
104    configure_scope(|scope| {
105        scope.set_user(Some(User {
106            id: Some(token_id.to_owned()),
107            other: BTreeMap::from_iter([(
108                AUTH_TYPE_PROPERTY.to_owned(),
109                Value::String("token".to_owned()),
110            )]),
111            ..Default::default()
112        }));
113    });
114}