async_inspect/
telemetry.rs1use once_cell::sync::OnceCell;
17use std::sync::atomic::{AtomicBool, Ordering};
18
19static TELEMETRY: OnceCell<Telemetry> = OnceCell::new();
20static TELEMETRY_DISABLED: AtomicBool = AtomicBool::new(false);
21#[cfg(feature = "telemetry")]
22static OPT_OUT_TRACKED: AtomicBool = AtomicBool::new(false);
23
24pub struct Telemetry {
26 #[cfg(feature = "telemetry")]
27 inner: Option<telemetry_kit::TelemetryKit>,
28 #[cfg(not(feature = "telemetry"))]
29 _phantom: std::marker::PhantomData<()>,
30}
31
32impl Telemetry {
33 #[must_use]
35 pub fn is_enabled(&self) -> bool {
36 #[cfg(feature = "telemetry")]
37 {
38 self.inner.is_some() && !TELEMETRY_DISABLED.load(Ordering::Relaxed)
39 }
40 #[cfg(not(feature = "telemetry"))]
41 {
42 false
43 }
44 }
45}
46
47#[cfg(feature = "telemetry")]
48fn create_telemetry_kit() -> Option<telemetry_kit::TelemetryKit> {
49 telemetry_kit::TelemetryKit::builder()
50 .service_name("async-inspect")
51 .ok()
52 .and_then(|b| b.service_version(env!("CARGO_PKG_VERSION")).build().ok())
53}
54
55pub fn init() {
59 let _ = TELEMETRY.get_or_init(|| {
60 let no_telemetry = std::env::var("ASYNC_INSPECT_NO_TELEMETRY")
62 .map(|v| v == "1" || v.to_lowercase() == "true")
63 .unwrap_or(false);
64
65 let do_not_track = std::env::var("DO_NOT_TRACK")
66 .map(|v| v == "1" || v.to_lowercase() == "true")
67 .unwrap_or(false);
68
69 if no_telemetry || do_not_track {
70 TELEMETRY_DISABLED.store(true, Ordering::Relaxed);
71
72 #[cfg(feature = "telemetry")]
75 if !OPT_OUT_TRACKED.swap(true, Ordering::Relaxed) {
76 std::thread::spawn(|| {
78 let rt = tokio::runtime::Builder::new_current_thread()
79 .enable_all()
80 .build();
81 if let Ok(rt) = rt {
82 rt.block_on(async {
83 if let Some(tk) = create_telemetry_kit() {
84 let _ = tk
85 .track_feature("telemetry_opt_out", |event| event.success(true))
86 .await;
87 }
88 });
89 }
90 });
91 }
92
93 return Telemetry {
94 #[cfg(feature = "telemetry")]
95 inner: None,
96 #[cfg(not(feature = "telemetry"))]
97 _phantom: std::marker::PhantomData,
98 };
99 }
100
101 #[cfg(feature = "telemetry")]
102 {
103 Telemetry {
104 inner: create_telemetry_kit(),
105 }
106 }
107
108 #[cfg(not(feature = "telemetry"))]
109 {
110 Telemetry {
111 _phantom: std::marker::PhantomData,
112 }
113 }
114 });
115}
116
117#[cfg(feature = "telemetry")]
119fn get() -> Option<&'static Telemetry> {
120 TELEMETRY.get()
121}
122
123#[allow(unused_variables)]
125pub async fn track_command(command: &str, success: bool, duration_ms: Option<u64>) {
126 #[cfg(feature = "telemetry")]
127 {
128 if let Some(telemetry) = get() {
129 if let Some(ref tk) = telemetry.inner {
130 let _ = tk
131 .track_command(command, |event| {
132 let event = event.success(success);
133 if let Some(ms) = duration_ms {
134 event.duration_ms(ms)
135 } else {
136 event
137 }
138 })
139 .await;
140 }
141 }
142 }
143}
144
145#[allow(unused_variables)]
147pub async fn track_feature(feature: &str, metadata: Option<&str>) {
148 #[cfg(feature = "telemetry")]
149 {
150 if let Some(telemetry) = get() {
151 if let Some(ref tk) = telemetry.inner {
152 let _ = tk
153 .track_feature(feature, |event| {
154 let event = event.success(true);
155 if let Some(meta) = metadata {
156 event.method(meta)
157 } else {
158 event
159 }
160 })
161 .await;
162 }
163 }
164 }
165}
166
167#[allow(unused_variables)]
169pub fn track_feature_sync(feature: &'static str, metadata: Option<&'static str>) {
170 #[cfg(feature = "telemetry")]
171 {
172 if TELEMETRY_DISABLED.load(Ordering::Relaxed) {
173 return;
174 }
175 tokio::spawn(async move {
176 track_feature(feature, metadata).await;
177 });
178 }
179}
180
181#[allow(unused_variables)]
183pub fn track_command_sync(command: &'static str, success: bool, duration_ms: Option<u64>) {
184 #[cfg(feature = "telemetry")]
185 {
186 if TELEMETRY_DISABLED.load(Ordering::Relaxed) {
187 return;
188 }
189 tokio::spawn(async move {
190 track_command(command, success, duration_ms).await;
191 });
192 }
193}
194
195pub fn disable() {
197 TELEMETRY_DISABLED.store(true, Ordering::Relaxed);
198}
199
200#[must_use]
202pub fn is_enabled() -> bool {
203 #[cfg(feature = "telemetry")]
204 {
205 !TELEMETRY_DISABLED.load(Ordering::Relaxed) && get().is_some_and(Telemetry::is_enabled)
206 }
207 #[cfg(not(feature = "telemetry"))]
208 {
209 false
210 }
211}