use std::collections::HashMap;
use std::ops::Deref;
use opentelemetry::propagation::{Extractor, Injector, TextMapPropagator};
use opentelemetry::trace::TraceContextExt;
use opentelemetry_sdk::propagation::TraceContextPropagator;
use tracing::span::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use wasmcloud_core::TraceContext;
#[derive(Debug)]
pub struct TraceContextExtractor<'a> {
inner: &'a TraceContext,
}
impl<'a> TraceContextExtractor<'a> {
#[must_use]
pub fn new(context: &'a TraceContext) -> Self {
TraceContextExtractor { inner: context }
}
}
impl Extractor for TraceContextExtractor<'_> {
fn get(&self, key: &str) -> Option<&str> {
self.inner
.iter()
.find_map(|(k, v)| (k == key).then_some(v.as_str()))
}
fn keys(&self) -> Vec<&str> {
self.inner.iter().map(|(k, _)| k.as_str()).collect()
}
}
#[derive(Clone, Debug, Default)]
pub struct TraceContextInjector {
inner: HashMap<String, String>,
}
impl TraceContextInjector {
#[must_use]
pub fn new(headers: TraceContext) -> Self {
let mut inner = HashMap::with_capacity(headers.len());
inner.extend(headers);
TraceContextInjector { inner }
}
#[must_use]
pub fn new_with_span(headers: TraceContext) -> Self {
let mut header_map = Self::new(headers);
header_map.inject_context();
header_map
}
pub fn new_with_extractor(extractor: &dyn Extractor) -> Self {
let mut header_map = Self::default();
let ctx_propagator = TraceContextPropagator::new();
let context = ctx_propagator.extract(extractor);
if !context.span().span_context().is_valid() {
ctx_propagator.inject_context(&Span::current().context(), &mut header_map);
} else {
ctx_propagator.inject_context(&context, &mut header_map);
}
header_map
}
#[must_use]
pub fn default_with_span() -> Self {
let mut header_map = Self::default();
header_map.inject_context();
header_map
}
pub fn inject_context(&mut self) {
let ctx_propagator = TraceContextPropagator::new();
ctx_propagator.inject_context(&Span::current().context(), self);
}
pub fn inject_context_from_span(&mut self, span: &Span) {
let ctx_propagator = TraceContextPropagator::new();
ctx_propagator.inject_context(&span.context(), self);
}
}
impl Injector for TraceContextInjector {
fn set(&mut self, key: &str, value: String) {
self.inner.insert(key.to_owned(), value);
}
}
impl AsRef<HashMap<String, String>> for TraceContextInjector {
fn as_ref(&self) -> &HashMap<String, String> {
&self.inner
}
}
impl Deref for TraceContextInjector {
type Target = HashMap<String, String>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl From<TraceContext> for TraceContextInjector {
fn from(context: TraceContext) -> Self {
TraceContextInjector::new(context)
}
}
impl From<TraceContextInjector> for TraceContext {
fn from(inj: TraceContextInjector) -> Self {
inj.inner.into_iter().collect()
}
}
pub fn get_span_context(trace_context: &TraceContext) -> opentelemetry::Context {
let ctx_propagator = TraceContextPropagator::new();
let extractor = TraceContextExtractor::new(trace_context);
ctx_propagator.extract(&extractor)
}
#[allow(clippy::module_name_repetitions)]
pub fn attach_span_context(trace_context: &TraceContext) {
let parent_ctx = get_span_context(trace_context);
let _ = Span::current().set_parent(parent_ctx);
}