use opentelemetry::Context;
use opentelemetry::propagation::{Extractor, Injector};
#[must_use]
pub fn extract<E: Extractor>(carrier: &E) -> Context {
opentelemetry::global::get_text_map_propagator(|propagator| propagator.extract(carrier))
}
pub fn inject<I: Injector>(cx: &Context, carrier: &mut I) {
opentelemetry::global::get_text_map_propagator(|propagator| {
propagator.inject_context(cx, carrier);
});
}
pub fn inject_current(headers: &mut http::HeaderMap) {
use tracing_opentelemetry::OpenTelemetrySpanExt;
let cx = tracing::Span::current().context();
inject(&cx, &mut HeaderInjector(headers));
}
pub struct HeaderInjector<'a>(pub &'a mut http::HeaderMap);
impl Injector for HeaderInjector<'_> {
fn set(&mut self, key: &str, value: String) {
if let Ok(name) = http::header::HeaderName::from_bytes(key.as_bytes())
&& let Ok(val) = http::header::HeaderValue::from_str(&value)
{
self.0.insert(name, val);
}
}
}
pub struct HeaderExtractor<'a>(pub &'a http::HeaderMap);
impl Extractor for HeaderExtractor<'_> {
fn get(&self, key: &str) -> Option<&str> {
self.0.get(key).and_then(|value| value.to_str().ok())
}
fn keys(&self) -> Vec<&str> {
self.0.keys().map(http::header::HeaderName::as_str).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::trace::{
SpanContext, SpanId, TraceContextExt, TraceFlags, TraceId, TraceState,
};
#[test]
fn round_trips_trace_context_through_headers() {
opentelemetry::global::set_text_map_propagator(
opentelemetry_sdk::propagation::TraceContextPropagator::new(),
);
let trace_id = TraceId::from_hex("0af7651916cd43dd8448eb211c80319c").unwrap();
let span_id = SpanId::from_hex("b7ad6b7169203331").unwrap();
let span_context =
SpanContext::new(trace_id, span_id, TraceFlags::SAMPLED, true, TraceState::default());
let cx = Context::new().with_remote_span_context(span_context);
let mut headers = http::HeaderMap::new();
inject(&cx, &mut HeaderInjector(&mut headers));
let traceparent = headers.get("traceparent").unwrap().to_str().unwrap();
assert!(traceparent.contains("0af7651916cd43dd8448eb211c80319c"), "{traceparent}");
let extracted = extract(&HeaderExtractor(&headers));
assert_eq!(extracted.span().span_context().trace_id(), trace_id);
}
}