1use serde::{Deserialize, Serialize};
6use std::cell::RefCell;
7use std::rc::Rc;
8use std::sync::atomic::{AtomicU64, Ordering};
9use std::time::{Duration, Instant};
10
11static NEXT_SPAN_ID: AtomicU64 = AtomicU64::new(1);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct SpanId(u64);
16
17impl SpanId {
18 #[must_use]
20 pub fn new() -> Self {
21 Self(NEXT_SPAN_ID.fetch_add(1, Ordering::Relaxed))
22 }
23
24 #[must_use]
26 pub fn as_u64(&self) -> u64 {
27 self.0
28 }
29}
30
31impl Default for SpanId {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Span {
40 pub id: SpanId,
42 pub name: String,
44 pub start_ns: u64,
46 pub end_ns: Option<u64>,
48 pub parent: Option<SpanId>,
50 pub category: Option<String>,
52 #[serde(default)]
54 pub metadata: std::collections::HashMap<String, String>,
55}
56
57impl Span {
58 #[must_use]
60 pub fn new(name: impl Into<String>, start_ns: u64) -> Self {
61 Self {
62 id: SpanId::new(),
63 name: name.into(),
64 start_ns,
65 end_ns: None,
66 parent: None,
67 category: None,
68 metadata: std::collections::HashMap::new(),
69 }
70 }
71
72 #[must_use]
74 pub fn with_parent(mut self, parent: SpanId) -> Self {
75 self.parent = Some(parent);
76 self
77 }
78
79 #[must_use]
81 pub fn with_category(mut self, category: impl Into<String>) -> Self {
82 self.category = Some(category.into());
83 self
84 }
85
86 pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
88 self.metadata.insert(key.into(), value.into());
89 }
90
91 pub fn close(&mut self, end_ns: u64) {
93 self.end_ns = Some(end_ns);
94 }
95
96 #[must_use]
98 pub fn duration_ns(&self) -> Option<u64> {
99 self.end_ns.map(|end| end.saturating_sub(self.start_ns))
100 }
101
102 #[must_use]
104 pub fn duration(&self) -> Option<Duration> {
105 self.duration_ns().map(Duration::from_nanos)
106 }
107
108 #[must_use]
110 pub fn is_closed(&self) -> bool {
111 self.end_ns.is_some()
112 }
113}
114
115pub(crate) struct TracerState {
117 pub active_spans: std::collections::HashMap<SpanId, ActiveSpan>,
118 pub completed_spans: Vec<Span>,
119 pub current_span: Option<SpanId>,
120 pub trace_start: Option<Instant>,
121}
122
123impl TracerState {
124 pub fn new() -> Self {
125 Self {
126 active_spans: std::collections::HashMap::new(),
127 completed_spans: Vec::new(),
128 current_span: None,
129 trace_start: None,
130 }
131 }
132
133 pub fn elapsed_ns(&self) -> u64 {
134 self.trace_start
135 .map(|start| start.elapsed().as_nanos() as u64)
136 .unwrap_or(0)
137 }
138}
139
140pub(crate) type SharedTracerState = Rc<RefCell<TracerState>>;
142
143pub struct SpanGuard {
145 state: SharedTracerState,
146 span_id: SpanId,
147 max_spans: usize,
148}
149
150impl std::fmt::Debug for SpanGuard {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 f.debug_struct("SpanGuard")
153 .field("span_id", &self.span_id)
154 .field("max_spans", &self.max_spans)
155 .finish_non_exhaustive()
156 }
157}
158
159impl SpanGuard {
160 pub(crate) fn new(state: SharedTracerState, span_id: SpanId, max_spans: usize) -> Self {
162 Self {
163 state,
164 span_id,
165 max_spans,
166 }
167 }
168
169 #[must_use]
171 pub fn id(&self) -> SpanId {
172 self.span_id
173 }
174}
175
176impl Drop for SpanGuard {
177 fn drop(&mut self) {
178 let mut state = self.state.borrow_mut();
179 if let Some(mut active) = state.active_spans.remove(&self.span_id) {
180 let end_ns = state.elapsed_ns();
181 active.span.close(end_ns);
182
183 state.current_span = active.span.parent;
185
186 if state.completed_spans.len() < self.max_spans {
188 state.completed_spans.push(active.span);
189 }
190 }
191 }
192}
193
194#[derive(Debug)]
196pub(crate) struct ActiveSpan {
197 pub span: Span,
198 #[allow(dead_code)]
199 pub start_instant: Instant,
200}
201
202impl ActiveSpan {
203 pub fn new(name: impl Into<String>, start_ns: u64, start_instant: Instant) -> Self {
204 Self {
205 span: Span::new(name, start_ns),
206 start_instant,
207 }
208 }
209}
210
211#[cfg(test)]
212#[allow(clippy::unwrap_used, clippy::expect_used)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_span_id_unique() {
218 let id1 = SpanId::new();
219 let id2 = SpanId::new();
220 assert_ne!(id1, id2);
221 }
222
223 #[test]
224 fn test_span_id_as_u64() {
225 let id = SpanId::new();
226 assert!(id.as_u64() > 0);
227 }
228
229 #[test]
230 fn test_span_id_default() {
231 let id1 = SpanId::default();
232 let id2 = SpanId::default();
233 assert_ne!(id1, id2);
235 assert!(id1.as_u64() > 0);
236 }
237
238 #[test]
239 fn test_span_id_equality() {
240 let id = SpanId::new();
241 let id_copy = id;
242 assert_eq!(id, id_copy);
243 }
244
245 #[test]
246 fn test_span_id_hash() {
247 use std::collections::HashSet;
248 let mut set = HashSet::new();
249 let id1 = SpanId::new();
250 let id2 = SpanId::new();
251 set.insert(id1);
252 set.insert(id2);
253 assert_eq!(set.len(), 2);
254 assert!(set.contains(&id1));
255 assert!(set.contains(&id2));
256 }
257
258 #[test]
259 fn test_span_new() {
260 let span = Span::new("test", 1000);
261 assert_eq!(span.name, "test");
262 assert_eq!(span.start_ns, 1000);
263 assert!(!span.is_closed());
264 }
265
266 #[test]
267 fn test_span_with_parent() {
268 let parent_id = SpanId::new();
269 let span = Span::new("child", 2000).with_parent(parent_id);
270 assert_eq!(span.parent, Some(parent_id));
271 }
272
273 #[test]
274 fn test_span_with_category() {
275 let span = Span::new("test", 0).with_category("render");
276 assert_eq!(span.category, Some("render".to_string()));
277 }
278
279 #[test]
280 fn test_span_close() {
281 let mut span = Span::new("test", 1000);
282 span.close(2000);
283 assert!(span.is_closed());
284 assert_eq!(span.duration_ns(), Some(1000));
285 }
286
287 #[test]
288 fn test_span_duration() {
289 let mut span = Span::new("test", 0);
290 span.close(1_000_000); let duration = span.duration().unwrap();
292 assert_eq!(duration, Duration::from_nanos(1_000_000));
293 }
294
295 #[test]
296 fn test_span_metadata() {
297 let mut span = Span::new("test", 0);
298 span.add_metadata("key", "value");
299 assert_eq!(span.metadata.get("key"), Some(&"value".to_string()));
300 }
301
302 #[test]
303 fn test_span_duration_ns_unclosed() {
304 let span = Span::new("test", 1000);
305 assert_eq!(span.duration_ns(), None);
307 }
308
309 #[test]
310 fn test_span_duration_unclosed() {
311 let span = Span::new("test", 1000);
312 assert!(span.duration().is_none());
314 }
315
316 #[test]
317 fn test_span_duration_saturating_sub() {
318 let mut span = Span::new("test", 5000);
320 span.close(1000); assert_eq!(span.duration_ns(), Some(0));
323 assert_eq!(span.duration(), Some(Duration::from_nanos(0)));
324 }
325
326 #[test]
327 fn test_span_multiple_metadata() {
328 let mut span = Span::new("test", 0);
329 span.add_metadata("key1", "value1");
330 span.add_metadata("key2", "value2");
331 span.add_metadata("key1", "updated"); assert_eq!(span.metadata.get("key1"), Some(&"updated".to_string()));
333 assert_eq!(span.metadata.get("key2"), Some(&"value2".to_string()));
334 assert_eq!(span.metadata.len(), 2);
335 }
336
337 #[test]
338 fn test_span_is_closed_initially_false() {
339 let span = Span::new("test", 0);
340 assert!(!span.is_closed());
341 assert!(span.end_ns.is_none());
342 }
343
344 #[test]
346 fn test_tracer_state_new() {
347 let state = TracerState::new();
348 assert!(state.active_spans.is_empty());
349 assert!(state.completed_spans.is_empty());
350 assert!(state.current_span.is_none());
351 assert!(state.trace_start.is_none());
352 }
353
354 #[test]
355 fn test_tracer_state_elapsed_ns_no_start() {
356 let state = TracerState::new();
357 assert_eq!(state.elapsed_ns(), 0);
359 }
360
361 #[test]
362 fn test_tracer_state_elapsed_ns_with_start() {
363 let mut state = TracerState::new();
364 state.trace_start = Some(Instant::now());
365 std::thread::sleep(Duration::from_micros(100));
367 let elapsed = state.elapsed_ns();
368 assert!(elapsed > 0);
369 }
370
371 #[test]
373 fn test_active_span_new() {
374 let instant = Instant::now();
375 let active = ActiveSpan::new("test_span", 12345, instant);
376 assert_eq!(active.span.name, "test_span");
377 assert_eq!(active.span.start_ns, 12345);
378 assert!(!active.span.is_closed());
379 }
380
381 #[test]
382 fn test_active_span_debug() {
383 let instant = Instant::now();
384 let active = ActiveSpan::new("debug_test", 0, instant);
385 let debug_str = format!("{:?}", active);
386 assert!(debug_str.contains("ActiveSpan"));
387 assert!(debug_str.contains("span"));
388 }
389
390 #[test]
392 fn test_span_guard_new_and_id() {
393 let state = Rc::new(RefCell::new(TracerState::new()));
394 let span_id = SpanId::new();
395 let guard = SpanGuard::new(Rc::clone(&state), span_id, 100);
396 assert_eq!(guard.id(), span_id);
397 }
398
399 #[test]
400 fn test_span_guard_debug() {
401 let state = Rc::new(RefCell::new(TracerState::new()));
402 let span_id = SpanId::new();
403 let guard = SpanGuard::new(Rc::clone(&state), span_id, 100);
404 let debug_str = format!("{:?}", guard);
405 assert!(debug_str.contains("SpanGuard"));
406 assert!(debug_str.contains("span_id"));
407 assert!(debug_str.contains("max_spans"));
408 }
409
410 #[test]
411 fn test_span_guard_drop_closes_span() {
412 let state = Rc::new(RefCell::new(TracerState::new()));
413 {
414 let mut s = state.borrow_mut();
415 s.trace_start = Some(Instant::now());
416 }
417
418 let span_id = SpanId::new();
419 let active_span = ActiveSpan::new("test", 0, Instant::now());
420
421 {
422 let mut s = state.borrow_mut();
423 s.active_spans.insert(span_id, active_span);
424 s.current_span = Some(span_id);
425 }
426
427 {
429 let _guard = SpanGuard::new(Rc::clone(&state), span_id, 100);
430 } let s = state.borrow();
433 assert!(!s.active_spans.contains_key(&span_id));
435 assert_eq!(s.completed_spans.len(), 1);
437 assert!(s.completed_spans[0].is_closed());
438 assert!(s.current_span.is_none());
440 }
441
442 #[test]
443 fn test_span_guard_drop_respects_max_spans() {
444 let state = Rc::new(RefCell::new(TracerState::new()));
445 {
446 let mut s = state.borrow_mut();
447 s.trace_start = Some(Instant::now());
448 for i in 0..5 {
450 let mut span = Span::new(format!("existing_{}", i), i as u64 * 100);
451 span.close(i as u64 * 100 + 50);
452 s.completed_spans.push(span);
453 }
454 }
455
456 let span_id = SpanId::new();
457 let active_span = ActiveSpan::new("overflow_test", 500, Instant::now());
458
459 {
460 let mut s = state.borrow_mut();
461 s.active_spans.insert(span_id, active_span);
462 }
463
464 {
466 let _guard = SpanGuard::new(Rc::clone(&state), span_id, 5);
467 } let s = state.borrow();
470 assert!(!s.active_spans.contains_key(&span_id));
472 assert_eq!(s.completed_spans.len(), 5);
474 }
475
476 #[test]
477 fn test_span_guard_drop_updates_parent() {
478 let state = Rc::new(RefCell::new(TracerState::new()));
479 {
480 let mut s = state.borrow_mut();
481 s.trace_start = Some(Instant::now());
482 }
483
484 let parent_id = SpanId::new();
485 let child_id = SpanId::new();
486
487 let mut child_span = ActiveSpan::new("child", 100, Instant::now());
488 child_span.span.parent = Some(parent_id);
489
490 {
491 let mut s = state.borrow_mut();
492 s.active_spans.insert(child_id, child_span);
493 s.current_span = Some(child_id);
494 }
495
496 {
498 let _guard = SpanGuard::new(Rc::clone(&state), child_id, 100);
499 }
500
501 let s = state.borrow();
502 assert_eq!(s.current_span, Some(parent_id));
504 }
505
506 #[test]
507 fn test_span_guard_drop_missing_span() {
508 let state = Rc::new(RefCell::new(TracerState::new()));
510 {
511 let mut s = state.borrow_mut();
512 s.trace_start = Some(Instant::now());
513 }
514
515 let span_id = SpanId::new();
516 {
520 let _guard = SpanGuard::new(Rc::clone(&state), span_id, 100);
521 } let s = state.borrow();
524 assert!(s.completed_spans.is_empty());
526 }
527
528 #[test]
530 fn test_span_id_serde() {
531 let id = SpanId::new();
532 let serialized = serde_json::to_string(&id).unwrap();
533 let deserialized: SpanId = serde_json::from_str(&serialized).unwrap();
534 assert_eq!(id, deserialized);
535 }
536
537 #[test]
538 fn test_span_serde() {
539 let mut span = Span::new("serde_test", 1000);
540 span.close(2000);
541 span.category = Some("test_category".to_string());
542 span.add_metadata("key", "value");
543
544 let serialized = serde_json::to_string(&span).unwrap();
545 let deserialized: Span = serde_json::from_str(&serialized).unwrap();
546
547 assert_eq!(deserialized.name, "serde_test");
548 assert_eq!(deserialized.start_ns, 1000);
549 assert_eq!(deserialized.end_ns, Some(2000));
550 assert_eq!(deserialized.category, Some("test_category".to_string()));
551 assert_eq!(deserialized.metadata.get("key"), Some(&"value".to_string()));
552 }
553
554 #[test]
555 fn test_span_serde_empty_metadata() {
556 let span = Span::new("minimal", 500);
557 let serialized = serde_json::to_string(&span).unwrap();
558 let deserialized: Span = serde_json::from_str(&serialized).unwrap();
559 assert!(deserialized.metadata.is_empty());
560 }
561
562 #[test]
563 fn test_span_clone() {
564 let mut original = Span::new("original", 100);
565 original.close(200);
566 original.category = Some("cat".to_string());
567 original.add_metadata("k", "v");
568
569 let cloned = original.clone();
570 assert_eq!(cloned.name, original.name);
571 assert_eq!(cloned.start_ns, original.start_ns);
572 assert_eq!(cloned.end_ns, original.end_ns);
573 assert_eq!(cloned.category, original.category);
574 assert_eq!(cloned.metadata, original.metadata);
575 assert_eq!(cloned.id, original.id);
577 }
578
579 #[test]
580 fn test_span_id_clone_copy() {
581 let id = SpanId::new();
582 let cloned = id;
583 let copied = id;
584 assert_eq!(id, cloned);
585 assert_eq!(id, copied);
586 }
587
588 #[test]
589 fn test_span_zero_duration() {
590 let mut span = Span::new("instant", 1000);
591 span.close(1000); assert_eq!(span.duration_ns(), Some(0));
593 assert_eq!(span.duration(), Some(Duration::ZERO));
594 }
595
596 #[test]
597 fn test_span_large_timestamps() {
598 let start = u64::MAX - 1000;
599 let end = u64::MAX;
600 let mut span = Span::new("large", start);
601 span.close(end);
602 assert_eq!(span.duration_ns(), Some(1000));
603 }
604
605 #[test]
606 fn test_tracer_state_active_spans_operations() {
607 let mut state = TracerState::new();
608 let id1 = SpanId::new();
609 let id2 = SpanId::new();
610
611 let span1 = ActiveSpan::new("span1", 0, Instant::now());
612 let span2 = ActiveSpan::new("span2", 100, Instant::now());
613
614 state.active_spans.insert(id1, span1);
615 state.active_spans.insert(id2, span2);
616
617 assert_eq!(state.active_spans.len(), 2);
618 assert!(state.active_spans.contains_key(&id1));
619 assert!(state.active_spans.contains_key(&id2));
620
621 state.active_spans.remove(&id1);
622 assert_eq!(state.active_spans.len(), 1);
623 assert!(!state.active_spans.contains_key(&id1));
624 }
625}