use rand::Rng;
use std::fmt;
use crate::Headers;
#[derive(Debug)]
pub struct TraceContext {
id: u64,
version: u8,
trace_id: u128,
parent_id: Option<u64>,
flags: u8,
}
impl TraceContext {
pub fn extract(headers: impl AsRef<Headers>) -> crate::Result<Self> {
let headers = headers.as_ref();
let mut rng = rand::thread_rng();
let traceparent = match headers.get("traceparent") {
Some(header) => header.as_str(),
None => return Ok(Self::new_root()),
};
let parts: Vec<&str> = traceparent.split('-').collect();
Ok(Self {
id: rng.gen(),
version: u8::from_str_radix(parts[0], 16)?,
trace_id: u128::from_str_radix(parts[1], 16)?,
parent_id: Some(u64::from_str_radix(parts[2], 16)?),
flags: u8::from_str_radix(parts[3], 16)?,
})
}
pub fn new_root() -> Self {
let mut rng = rand::thread_rng();
Self {
id: rng.gen(),
version: 0,
trace_id: rng.gen(),
parent_id: None,
flags: 1,
}
}
pub fn inject(&self, mut headers: impl AsMut<Headers>) {
let headers = headers.as_mut();
headers.insert("traceparent", format!("{}", self));
}
pub fn child(&self) -> Self {
let mut rng = rand::thread_rng();
Self {
id: rng.gen(),
version: self.version,
trace_id: self.trace_id,
parent_id: Some(self.id),
flags: self.flags,
}
}
pub fn id(&self) -> u64 {
self.id
}
pub fn version(&self) -> u8 {
self.version
}
pub fn trace_id(&self) -> u128 {
self.trace_id
}
#[inline]
pub fn parent_id(&self) -> Option<u64> {
self.parent_id
}
pub fn sampled(&self) -> bool {
(self.flags & 0b00000001) == 1
}
pub fn set_sampled(&mut self, sampled: bool) {
let x = sampled as u8;
self.flags ^= (x ^ self.flags) & (1 << 0);
}
}
impl fmt::Display for TraceContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{:02x}-{:032x}-{:016x}-{:02x}",
self.version, self.trace_id, self.id, self.flags
)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn default() -> crate::Result<()> {
let mut headers = crate::Headers::new();
headers.insert("traceparent", "00-01-deadbeef-00");
let context = TraceContext::extract(&mut headers)?;
assert_eq!(context.version(), 0);
assert_eq!(context.trace_id(), 1);
assert_eq!(context.parent_id().unwrap(), 3735928559);
assert_eq!(context.flags, 0);
assert_eq!(context.sampled(), false);
Ok(())
}
#[test]
fn no_header() -> crate::Result<()> {
let mut headers = crate::Headers::new();
let context = TraceContext::extract(&mut headers)?;
assert_eq!(context.version(), 0);
assert_eq!(context.parent_id(), None);
assert_eq!(context.flags, 1);
assert_eq!(context.sampled(), true);
Ok(())
}
#[test]
fn not_sampled() -> crate::Result<()> {
let mut headers = crate::Headers::new();
headers.insert("traceparent", "00-01-02-00");
let context = TraceContext::extract(&mut headers)?;
assert_eq!(context.sampled(), false);
Ok(())
}
#[test]
fn sampled() -> crate::Result<()> {
let mut headers = crate::Headers::new();
headers.insert("traceparent", "00-01-02-01");
let context = TraceContext::extract(&mut headers)?;
assert_eq!(context.sampled(), true);
Ok(())
}
}