use std::collections::HashMap;
use std::sync::LazyLock;
use opentelemetry::propagation::TextMapPropagator;
use opentelemetry_sdk::propagation::TraceContextPropagator;
use tracing::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;
pub(crate) static PROPAGATOR: LazyLock<TraceContextPropagator> =
LazyLock::new(TraceContextPropagator::new);
struct MapExtractor<'a>(&'a HashMap<String, String>);
impl opentelemetry::propagation::Extractor for MapExtractor<'_> {
fn get(&self, key: &str) -> Option<&str> {
self.0.get(key).map(|v| v.as_str())
}
fn keys(&self) -> Vec<&str> {
self.0.keys().map(|k| k.as_str()).collect()
}
}
pub fn set_parent_from_map(headers: &HashMap<String, String>) -> opentelemetry::Context {
let cx = PROPAGATOR.extract(&MapExtractor(headers));
let _ = Span::current().set_parent(cx.clone());
cx
}
pub fn inject_trace_context(headers: &mut HashMap<String, String>) {
struct MapInjector<'a> {
headers: &'a mut HashMap<String, String>,
}
impl opentelemetry::propagation::Injector for MapInjector<'_> {
fn set(&mut self, key: &str, value: String) {
self.headers.insert(key.to_string(), value);
}
}
let cx = Span::current().context();
PROPAGATOR.inject_context(&cx, &mut MapInjector { headers });
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_absent_trace_context_is_not_invented() {
use opentelemetry::trace::TraceContextExt;
let mut headers = HashMap::new();
inject_trace_context(&mut headers);
assert!(
!headers.contains_key("traceparent"),
"no active trace must not produce a traceparent: {headers:?}"
);
let cx = set_parent_from_map(&HashMap::new());
assert!(!cx.span().span_context().is_valid());
}
#[test]
fn a_caller_supplied_traceparent_is_propagated() {
use opentelemetry::trace::TraceContextExt;
let mut headers = HashMap::new();
headers.insert(
"traceparent".to_string(),
"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01".to_string(),
);
let cx = set_parent_from_map(&headers);
let span = cx.span();
let sc = span.span_context();
assert!(sc.is_valid());
assert_eq!(
sc.trace_id().to_string(),
"4bf92f3577b34da6a3ce929d0e0e4736"
);
assert_eq!(sc.span_id().to_string(), "00f067aa0ba902b7");
}
}