arcly_http/observability/
lean_telemetry.rs1use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
16use std::sync::OnceLock;
17use std::time::Instant;
18
19#[derive(Clone, Copy)]
20pub struct TraceCtx {
21 pub trace_id: [u8; 16],
22 pub span_id: [u8; 8],
23}
24
25static REQS: AtomicU64 = AtomicU64::new(0);
26static INFLT: AtomicU64 = AtomicU64::new(0);
27
28#[inline]
33pub fn parse_traceparent(h: &[u8]) -> Option<TraceCtx> {
34 if h.len() < 55 || h[2] != b'-' || h[35] != b'-' || h[52] != b'-' {
35 return None;
36 }
37 let mut tid = [0u8; 16];
38 let mut sid = [0u8; 8];
39 hex_into(&h[3..35], &mut tid)?;
40 hex_into(&h[36..52], &mut sid)?;
41 Some(TraceCtx {
42 trace_id: tid,
43 span_id: sid,
44 })
45}
46
47#[inline]
48fn hex_into(src: &[u8], dst: &mut [u8]) -> Option<()> {
49 for (i, byte) in dst.iter_mut().enumerate() {
50 *byte = (nyb(src[2 * i])? << 4) | nyb(src[2 * i + 1])?;
51 }
52 Some(())
53}
54
55#[inline]
56fn nyb(c: u8) -> Option<u8> {
57 match c {
58 b'0'..=b'9' => Some(c - b'0'),
59 b'a'..=b'f' => Some(c - b'a' + 10),
60 b'A'..=b'F' => Some(c - b'A' + 10),
61 _ => None,
62 }
63}
64
65fn monotonic_nanos() -> u64 {
69 static EPOCH: OnceLock<Instant> = OnceLock::new();
70 EPOCH.get_or_init(Instant::now).elapsed().as_nanos() as u64
71}
72
73pub fn new_span_id() -> [u8; 8] {
78 static COUNTER: AtomicU64 = AtomicU64::new(1);
79 let count = COUNTER.fetch_add(1, Relaxed);
80 let mut v = monotonic_nanos() ^ count.wrapping_mul(0x9E3779B97F4A7C15);
81 v ^= v << 13;
82 v ^= v >> 7;
83 v ^= v << 17;
84 v.to_ne_bytes()
85}
86
87pub fn new_trace_id() -> [u8; 16] {
89 let lo = new_span_id();
90 let hi = new_span_id();
91 let mut out = [0u8; 16];
92 out[..8].copy_from_slice(&hi);
93 out[8..].copy_from_slice(&lo);
94 out
95}
96
97pub fn hex_encode(bytes: &[u8]) -> String {
101 bytes.iter().map(|b| format!("{b:02x}")).collect()
102}
103
104pub struct RequestGuard;
108
109#[inline]
110pub fn on_request_start() -> RequestGuard {
111 REQS.fetch_add(1, Relaxed);
112 INFLT.fetch_add(1, Relaxed);
113 RequestGuard
114}
115
116impl Drop for RequestGuard {
117 #[inline]
118 fn drop(&mut self) {
119 INFLT.fetch_sub(1, Relaxed);
120 }
121}
122
123#[inline]
125pub fn snapshot() -> (u64, u64) {
126 (REQS.load(Relaxed), INFLT.load(Relaxed))
127}