docspec_http/telemetry/
mod.rs1pub mod sentry;
7
8pub struct TelemetryGuard {
10 inner: Option<::sentry::ClientInitGuard>,
11}
12
13impl Drop for TelemetryGuard {
14 #[inline]
15 fn drop(&mut self) {
16 drop(self.inner.take());
17 }
18}
19
20#[must_use]
22#[inline]
23pub fn init() -> TelemetryGuard {
24 let guard = configured_dsn()
25 .and_then(|data_source_name| crate::telemetry::sentry::init_sentry(&data_source_name));
26 TelemetryGuard { inner: guard }
27}
28
29#[must_use]
31#[inline]
32pub fn tracing_layer<S>() -> Option<::sentry::integrations::tracing::SentryLayer<S>>
33where
34 S: tracing::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
35{
36 ::sentry::Hub::current()
37 .client()
38 .map(|_| crate::telemetry::sentry::tracing_layer())
39}
40
41#[must_use]
43#[inline]
44pub fn tower_new_layer(
45) -> Option<::sentry::integrations::tower::NewSentryLayer<axum::http::Request<axum::body::Body>>> {
46 ::sentry::Hub::current()
47 .client()
48 .map(|_| crate::telemetry::sentry::tower_new_layer())
49}
50
51#[must_use]
53#[inline]
54pub fn tower_http_layer() -> Option<::sentry::integrations::tower::SentryHttpLayer> {
55 ::sentry::Hub::current()
56 .client()
57 .map(|_| crate::telemetry::sentry::tower_http_layer())
58}
59
60fn configured_dsn() -> Option<String> {
61 for name in ["DOCSPEC_SENTRY_DSN", "SENTRY_DSN"] {
62 match std::env::var(name) {
63 Ok(value) if !value.is_empty() => return Some(value),
64 _ => {}
65 }
66 }
67 None
68}
69
70#[cfg(test)]
71mod tests {
72 use std::sync::Mutex;
73
74 static ENV_MUTEX: Mutex<()> = Mutex::new(());
75
76 fn lock_env() -> std::sync::MutexGuard<'static, ()> {
77 match ENV_MUTEX.lock() {
78 Ok(guard) => guard,
79 Err(poisoned) => poisoned.into_inner(),
80 }
81 }
82
83 #[test]
84 fn telemetry_init_returns_noop_guard_when_dsn_absent() {
85 let _env_guard = lock_env();
86 std::env::remove_var("DOCSPEC_SENTRY_DSN");
87 std::env::remove_var("SENTRY_DSN");
88
89 let _telemetry = crate::telemetry::init();
90
91 assert!(crate::telemetry::tracing_layer::<tracing_subscriber::Registry>().is_none());
92 assert!(crate::telemetry::tower_new_layer().is_none());
93 }
94
95 #[test]
96 fn resolve_dsn_picks_docspec_over_sentry() {
97 let _env_guard = lock_env();
98 std::env::set_var("DOCSPEC_SENTRY_DSN", "https://docspec.example/1");
99 std::env::set_var("SENTRY_DSN", "https://sentry.example/1");
100
101 assert_eq!(
102 super::configured_dsn(),
103 Some("https://docspec.example/1".to_string())
104 );
105 }
106
107 #[test]
108 fn resolve_dsn_treats_empty_string_as_absent() {
109 let _env_guard = lock_env();
110 std::env::set_var("DOCSPEC_SENTRY_DSN", "");
111 std::env::remove_var("SENTRY_DSN");
112
113 assert_eq!(super::configured_dsn(), None);
114 }
115}