forge_core_utils/
sentry.rs

1use std::sync::OnceLock;
2
3use sentry_tracing::{EventFilter, SentryLayer};
4use tracing::Level;
5
6const SENTRY_DSN: &str = "https://1065a1d276a581316999a07d5dffee26@o4509603705192449.ingest.de.sentry.io/4509605576441937";
7
8static INIT_GUARD: OnceLock<sentry::ClientInitGuard> = OnceLock::new();
9
10#[derive(Clone, Copy, Debug)]
11pub enum SentrySource {
12    Backend,
13    Mcp,
14}
15
16impl SentrySource {
17    fn tag(self) -> &'static str {
18        match self {
19            SentrySource::Backend => "backend",
20            SentrySource::Mcp => "mcp",
21        }
22    }
23}
24
25fn environment() -> &'static str {
26    if cfg!(debug_assertions) {
27        "dev"
28    } else {
29        "production"
30    }
31}
32
33pub fn init_once(source: SentrySource) {
34    INIT_GUARD.get_or_init(|| {
35        sentry::init((
36            SENTRY_DSN,
37            sentry::ClientOptions {
38                release: sentry::release_name!(),
39                environment: Some(environment().into()),
40                ..Default::default()
41            },
42        ))
43    });
44
45    sentry::configure_scope(|scope| {
46        scope.set_tag("source", source.tag());
47    });
48}
49
50pub fn configure_user_scope(user_id: &str, username: Option<&str>, email: Option<&str>) {
51    let mut sentry_user = sentry::User {
52        id: Some(user_id.to_string()),
53        ..Default::default()
54    };
55
56    if let Some(username) = username {
57        sentry_user.username = Some(username.to_string());
58    }
59
60    if let Some(email) = email {
61        sentry_user.email = Some(email.to_string());
62    }
63
64    sentry::configure_scope(|scope| {
65        scope.set_user(Some(sentry_user));
66    });
67}
68
69pub fn sentry_layer<S>() -> SentryLayer<S>
70where
71    S: tracing::Subscriber,
72    S: for<'a> tracing_subscriber::registry::LookupSpan<'a>,
73{
74    SentryLayer::default()
75        .span_filter(|meta| {
76            matches!(
77                *meta.level(),
78                Level::DEBUG | Level::INFO | Level::WARN | Level::ERROR
79            )
80        })
81        .event_filter(|meta| match *meta.level() {
82            Level::ERROR => EventFilter::Event,
83            Level::DEBUG | Level::INFO | Level::WARN => EventFilter::Breadcrumb,
84            Level::TRACE => EventFilter::Ignore,
85        })
86}