use tracing_subscriber::registry::LookupSpan;
#[derive(Debug, Clone, Copy, Default)]
pub struct OtelContext {
pub trace_id: [u8; 32],
pub span_id: [u8; 16],
pub is_valid: bool,
}
impl OtelContext {
pub const fn empty() -> Self {
Self {
trace_id: [0u8; 32],
span_id: [0u8; 16],
is_valid: false,
}
}
}
pub fn extract_otel_context<'a, R>(span: &tracing_subscriber::registry::SpanRef<'a, R>) -> OtelContext
where
R: LookupSpan<'a>,
{
let extensions = span.extensions();
if let Some(otel_data) = extensions.get::<tracing_opentelemetry::OtelData>() {
let trace_id = otel_data.trace_id();
let span_id = otel_data.span_id();
if let (Some(tid), Some(sid)) = (trace_id, span_id) {
return format_otel_context(tid, sid);
}
}
OtelContext::empty()
}
fn format_otel_context(
tid: opentelemetry::trace::TraceId,
sid: opentelemetry::trace::SpanId,
) -> OtelContext {
let mut ctx = OtelContext {
trace_id: [0u8; 32],
span_id: [0u8; 16],
is_valid: true,
};
let tid_bytes = tid.to_bytes();
for (i, byte) in tid_bytes.iter().enumerate() {
let hex = format!("{:02x}", byte);
ctx.trace_id[i * 2] = hex.as_bytes()[0];
ctx.trace_id[i * 2 + 1] = hex.as_bytes()[1];
}
let sid_bytes = sid.to_bytes();
for (i, byte) in sid_bytes.iter().enumerate() {
let hex = format!("{:02x}", byte);
ctx.span_id[i * 2] = hex.as_bytes()[0];
ctx.span_id[i * 2 + 1] = hex.as_bytes()[1];
}
ctx
}
pub fn extract_parent_otel_context<'a, R>(
parent: Option<tracing_subscriber::registry::SpanRef<'a, R>>,
) -> Option<OtelContext>
where
R: LookupSpan<'a>,
{
parent.map(|p| {
let ctx = extract_otel_context(&p);
if ctx.is_valid {
Some(ctx)
} else {
None
}
}).flatten()
}