1use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
7use std::sync::Arc;
8
9static CONFIG: once_cell::sync::Lazy<Config> = once_cell::sync::Lazy::new(Config::default);
11
12#[derive(Clone)]
14pub struct Config {
15 inner: Arc<ConfigInner>,
16}
17
18struct ConfigInner {
19 sampling_rate: AtomicUsize,
21
22 max_events: AtomicUsize,
24
25 max_tasks: AtomicUsize,
27
28 sample_counter: AtomicU64,
30
31 track_awaits: AtomicUsize,
33
34 track_polls: AtomicUsize,
36
37 enable_html: AtomicUsize,
39
40 overhead_ns: AtomicU64,
42
43 instrumentation_calls: AtomicU64,
45}
46
47impl Config {
48 #[must_use]
50 pub fn global() -> &'static Config {
51 &CONFIG
52 }
53
54 #[must_use]
56 pub fn new() -> Self {
57 Self {
58 inner: Arc::new(ConfigInner {
59 sampling_rate: AtomicUsize::new(1), max_events: AtomicUsize::new(10_000), max_tasks: AtomicUsize::new(1_000), sample_counter: AtomicU64::new(0),
63 track_awaits: AtomicUsize::new(1), track_polls: AtomicUsize::new(1), enable_html: AtomicUsize::new(1), overhead_ns: AtomicU64::new(0),
67 instrumentation_calls: AtomicU64::new(0),
68 }),
69 }
70 }
71
72 pub fn set_sampling_rate(&self, rate: usize) {
74 self.inner
75 .sampling_rate
76 .store(rate.max(1), Ordering::Relaxed);
77 }
78
79 #[must_use]
81 pub fn sampling_rate(&self) -> usize {
82 self.inner.sampling_rate.load(Ordering::Relaxed)
83 }
84
85 pub fn set_max_events(&self, max: usize) {
87 self.inner.max_events.store(max, Ordering::Relaxed);
88 }
89
90 #[must_use]
92 pub fn max_events(&self) -> usize {
93 self.inner.max_events.load(Ordering::Relaxed)
94 }
95
96 pub fn set_max_tasks(&self, max: usize) {
98 self.inner.max_tasks.store(max, Ordering::Relaxed);
99 }
100
101 #[must_use]
103 pub fn max_tasks(&self) -> usize {
104 self.inner.max_tasks.load(Ordering::Relaxed)
105 }
106
107 pub fn set_track_awaits(&self, enabled: bool) {
109 self.inner
110 .track_awaits
111 .store(usize::from(enabled), Ordering::Relaxed);
112 }
113
114 #[must_use]
116 pub fn track_awaits(&self) -> bool {
117 self.inner.track_awaits.load(Ordering::Relaxed) != 0
118 }
119
120 pub fn set_track_polls(&self, enabled: bool) {
122 self.inner
123 .track_polls
124 .store(usize::from(enabled), Ordering::Relaxed);
125 }
126
127 #[must_use]
129 pub fn track_polls(&self) -> bool {
130 self.inner.track_polls.load(Ordering::Relaxed) != 0
131 }
132
133 pub fn set_enable_html(&self, enabled: bool) {
135 self.inner
136 .enable_html
137 .store(usize::from(enabled), Ordering::Relaxed);
138 }
139
140 #[must_use]
142 pub fn enable_html(&self) -> bool {
143 self.inner.enable_html.load(Ordering::Relaxed) != 0
144 }
145
146 #[must_use]
148 pub fn should_sample(&self) -> bool {
149 let rate = self.sampling_rate();
150 if rate <= 1 {
151 return true;
152 }
153
154 let count = self.inner.sample_counter.fetch_add(1, Ordering::Relaxed);
155 count % rate as u64 == 0
156 }
157
158 pub fn record_overhead(&self, nanos: u64) {
160 self.inner.overhead_ns.fetch_add(nanos, Ordering::Relaxed);
161 self.inner
162 .instrumentation_calls
163 .fetch_add(1, Ordering::Relaxed);
164 }
165
166 #[must_use]
168 pub fn total_overhead_ns(&self) -> u64 {
169 self.inner.overhead_ns.load(Ordering::Relaxed)
170 }
171
172 #[must_use]
174 pub fn instrumentation_calls(&self) -> u64 {
175 self.inner.instrumentation_calls.load(Ordering::Relaxed)
176 }
177
178 #[must_use]
180 pub fn avg_overhead_ns(&self) -> f64 {
181 let calls = self.instrumentation_calls();
182 if calls == 0 {
183 return 0.0;
184 }
185 self.total_overhead_ns() as f64 / calls as f64
186 }
187
188 pub fn production_mode(&self) {
190 self.set_sampling_rate(100); self.set_max_events(1_000); self.set_max_tasks(500); self.set_track_awaits(false); self.set_enable_html(false); }
196
197 pub fn development_mode(&self) {
199 self.set_sampling_rate(1); self.set_max_events(10_000); self.set_max_tasks(1_000); self.set_track_awaits(true); self.set_enable_html(true); }
205
206 pub fn debug_mode(&self) {
208 self.set_sampling_rate(1); self.set_max_events(0); self.set_max_tasks(0); self.set_track_awaits(true); self.set_enable_html(true); }
214
215 #[must_use]
217 pub fn overhead_stats(&self) -> OverheadStats {
218 OverheadStats {
219 total_ns: self.total_overhead_ns(),
220 calls: self.instrumentation_calls(),
221 avg_ns: self.avg_overhead_ns(),
222 }
223 }
224
225 pub fn reset_overhead(&self) {
227 self.inner.overhead_ns.store(0, Ordering::Relaxed);
228 self.inner.instrumentation_calls.store(0, Ordering::Relaxed);
229 }
230}
231
232impl Default for Config {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238#[derive(Debug, Clone, Copy)]
240pub struct OverheadStats {
241 pub total_ns: u64,
243
244 pub calls: u64,
246
247 pub avg_ns: f64,
249}
250
251impl OverheadStats {
252 #[must_use]
254 pub fn total_ms(&self) -> f64 {
255 self.total_ns as f64 / 1_000_000.0
256 }
257
258 #[must_use]
260 pub fn avg_us(&self) -> f64 {
261 self.avg_ns / 1_000.0
262 }
263}
264
265#[macro_export]
267macro_rules! measure_overhead {
268 ($expr:expr) => {{
269 let start = std::time::Instant::now();
270 let result = $expr;
271 let elapsed = start.elapsed().as_nanos() as u64;
272 $crate::config::Config::global().record_overhead(elapsed);
273 result
274 }};
275}
276
277#[macro_export]
279macro_rules! if_sampled {
280 ($body:block) => {
281 if $crate::config::Config::global().should_sample() $body
282 };
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_sampling_rate() {
291 let config = Config::new();
292 config.set_sampling_rate(10);
293 assert_eq!(config.sampling_rate(), 10);
294
295 let mut sampled = 0;
297 for _ in 0..100 {
298 if config.should_sample() {
299 sampled += 1;
300 }
301 }
302 assert!((8..=12).contains(&sampled));
304 }
305
306 #[test]
307 fn test_overhead_tracking() {
308 let config = Config::new();
309 config.reset_overhead();
310
311 config.record_overhead(1000);
312 config.record_overhead(2000);
313
314 let stats = config.overhead_stats();
315 assert_eq!(stats.total_ns, 3000);
316 assert_eq!(stats.calls, 2);
317 assert_eq!(stats.avg_ns, 1500.0);
318 }
319
320 #[test]
321 fn test_production_mode() {
322 let config = Config::new();
323 config.production_mode();
324
325 assert_eq!(config.sampling_rate(), 100);
326 assert!(!config.track_awaits());
327 assert!(!config.enable_html());
328 }
329}