enact_core/context/
trace.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TraceContext {
12 pub trace_id: String,
14 pub span_id: String,
16 pub trace_flags: Option<u8>,
18 pub trace_state: Option<String>,
20}
21
22impl TraceContext {
23 pub fn new() -> Self {
25 Self {
26 trace_id: Self::generate_trace_id(),
27 span_id: Self::generate_span_id(),
28 trace_flags: Some(1), trace_state: None,
30 }
31 }
32
33 pub fn child_span(&self) -> Self {
35 Self {
36 trace_id: self.trace_id.clone(),
37 span_id: Self::generate_span_id(),
38 trace_flags: self.trace_flags,
39 trace_state: self.trace_state.clone(),
40 }
41 }
42
43 pub fn from_traceparent(header: &str) -> Option<Self> {
45 let parts: Vec<&str> = header.split('-').collect();
46 if parts.len() >= 3 {
47 Some(Self {
48 trace_id: parts[1].to_string(),
49 span_id: parts[2].to_string(),
50 trace_flags: parts.get(3).and_then(|f| u8::from_str_radix(f, 16).ok()),
51 trace_state: None,
52 })
53 } else {
54 None
55 }
56 }
57
58 pub fn to_traceparent(&self) -> String {
60 format!(
61 "00-{}-{}-{:02x}",
62 self.trace_id,
63 self.span_id,
64 self.trace_flags.unwrap_or(0)
65 )
66 }
67
68 fn generate_trace_id() -> String {
69 use std::time::{SystemTime, UNIX_EPOCH};
70 let now = SystemTime::now()
71 .duration_since(UNIX_EPOCH)
72 .unwrap()
73 .as_nanos();
74 format!("{:032x}", now)
75 }
76
77 fn generate_span_id() -> String {
78 use std::sync::atomic::{AtomicU64, Ordering};
79 use std::time::{SystemTime, UNIX_EPOCH};
80 static COUNTER: AtomicU64 = AtomicU64::new(0);
81
82 let now = SystemTime::now()
83 .duration_since(UNIX_EPOCH)
84 .unwrap()
85 .as_nanos() as u64;
86 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
87 format!("{:016x}", now ^ (counter << 48))
89 }
90}
91
92impl Default for TraceContext {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_trace_context_new() {
104 let ctx = TraceContext::new();
105 assert_eq!(ctx.trace_id.len(), 32);
106 assert_eq!(ctx.span_id.len(), 16);
107 }
108
109 #[test]
110 fn test_child_span() {
111 let parent = TraceContext::new();
112 let child = parent.child_span();
113
114 assert_eq!(child.trace_id, parent.trace_id);
115 assert_ne!(child.span_id, parent.span_id);
116 }
117
118 #[test]
119 fn test_traceparent_roundtrip() {
120 let ctx = TraceContext::new();
121 let header = ctx.to_traceparent();
122 let parsed = TraceContext::from_traceparent(&header).unwrap();
123
124 assert_eq!(parsed.trace_id, ctx.trace_id);
125 assert_eq!(parsed.span_id, ctx.span_id);
126 }
127}