CommonLibrary/Telemetry/
Traceparent.rs1#![allow(non_snake_case)]
2
3use std::{
17 collections::hash_map::DefaultHasher,
18 hash::{Hash, Hasher},
19 time::{SystemTime, UNIX_EPOCH},
20};
21
22use crate::Telemetry::EmitOTLPSpan;
23
24const VERSION:&str = "00";
26const SAMPLED_FLAG:&str = "01";
27
28fn FreshSpanId() -> String {
29 let mut H = DefaultHasher::new();
30 std::thread::current().id().hash(&mut H);
31 if let Ok(D) = SystemTime::now().duration_since(UNIX_EPOCH) {
32 D.as_nanos().hash(&mut H);
33 }
34 format!("{:016x}", H.finish())
35}
36
37pub fn Build() -> String {
42 let TraceId = TraceIdValue();
43 let SpanId = FreshSpanId();
44 format!("{}-{}-{}-{}", VERSION, TraceId, SpanId, SAMPLED_FLAG)
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct Decoded {
51 pub TraceId:String,
52 pub ParentSpanId:String,
53 pub Sampled:bool,
54}
55
56pub fn Parse(Header:&str) -> Option<Decoded> {
59 let Parts:Vec<&str> = Header.split('-').collect();
60 if Parts.len() != 4 {
61 return None;
62 }
63 if Parts[0] != VERSION {
64 return None;
65 }
66 if Parts[1].len() != 32 || !Parts[1].chars().all(|C| C.is_ascii_hexdigit()) {
67 return None;
68 }
69 if Parts[2].len() != 16 || !Parts[2].chars().all(|C| C.is_ascii_hexdigit()) {
70 return None;
71 }
72 let Sampled = Parts[3] == SAMPLED_FLAG || Parts[3] == "01";
73 Some(Decoded { TraceId:Parts[1].to_string(), ParentSpanId:Parts[2].to_string(), Sampled })
74}
75
76pub fn TraceIdValue() -> String {
80 let mut H = DefaultHasher::new();
84 std::process::id().hash(&mut H);
85 EmitOTLPSpan::NowNanoPub().hash(&mut H);
86 format!("{:032x}", H.finish() as u128)
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn RoundTrip() {
99 let Header = Build();
100 let Decoded = Parse(&Header).expect("parse");
101 assert_eq!(Decoded.TraceId.len(), 32);
102 assert_eq!(Decoded.ParentSpanId.len(), 16);
103 assert!(Decoded.Sampled);
104 }
105
106 #[test]
107 fn RejectsMalformed() {
108 assert!(Parse("").is_none());
109 assert!(Parse("not-a-valid-header").is_none());
110 assert!(Parse("00-tooshort-00f067aa0ba902b7-01").is_none());
111 assert!(Parse("01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01").is_none());
112 }
113}