use core::{fmt, marker};
use opentelemetry::trace::{Status, TraceContextExt};
use opentelemetry::propagation::{Extractor, Injector, TextMapPropagator};
use opentelemetry_sdk::propagation::TraceContextPropagator;
pub trait ParentDestination {
fn set(&mut self, key: &str, value: String);
}
impl<T: ParentDestination> ParentDestination for &'_ mut T {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
T::set(self, key, value)
}
}
impl<T: ParentDestination> ParentDestination for Box<T> {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
T::set(self, key, value)
}
}
#[cfg(feature = "grpc")]
impl ParentDestination for tonic::metadata::MetadataMap {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
let key = tonic::metadata::MetadataKey::from_bytes(key.as_bytes()).expect("value header key");
self.insert(key, value.try_into().expect("value header value"));
}
}
#[cfg(feature = "http")]
impl ParentDestination for http::HeaderMap {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
let key = http::header::HeaderName::from_bytes(key.as_bytes()).expect("value header key");
self.insert(key, value.try_into().expect("value header value"));
}
}
impl<K: for<'a> From<&'a str>, V: From<String>> ParentDestination for Vec<(K, V)> {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
self.push((key.into(), value.into()));
}
}
impl<K: for<'a> From<&'a str> + core::hash::Hash + Eq, V: From<String>, S: core::hash::BuildHasher> ParentDestination for std::collections::HashMap<K, V, S> {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
self.insert(key.into(), value.into());
}
}
impl<K: for<'a> From<&'a str> + Ord, V: From<String>> ParentDestination for std::collections::BTreeMap<K, V> {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
self.insert(key.into(), value.into());
}
}
#[repr(transparent)]
struct ParentDestinationImpl<T: ParentDestination>(T);
impl<T: ParentDestination> Injector for ParentDestinationImpl<T> {
#[inline(always)]
fn set(&mut self, key: &str, value: String) {
ParentDestination::set(&mut self.0, key, value)
}
}
pub trait ParentSource {
fn get(&self, key: &str) -> Option<&str>;
fn keys(&self) -> impl Iterator<Item = &str>;
}
impl<T: ParentSource> ParentSource for &'_ T {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
T::get(self, key)
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
T::keys(self)
}
}
impl<T: ParentSource> ParentSource for &'_ mut T {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
T::get(self, key)
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
T::keys(self)
}
}
impl<T: ParentSource> ParentSource for Box<T> {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
T::get(self, key)
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
T::keys(self)
}
}
impl<T: ParentSource> ParentSource for std::sync::Arc<T> {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
T::get(self, key)
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
T::keys(self)
}
}
impl<T: ParentSource> ParentSource for std::rc::Rc<T> {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
T::get(self, key)
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
T::keys(self)
}
}
#[cfg(feature = "grpc")]
impl ParentSource for tonic::metadata::MetadataMap {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
self.get(key).and_then(|value| value.to_str().ok())
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
self.iter().map(|kv| match kv {
tonic::metadata::KeyAndValueRef::Ascii(key, _) => key.as_str(),
tonic::metadata::KeyAndValueRef::Binary(key, _) => key.as_str(),
})
}
}
#[cfg(feature = "http")]
impl ParentSource for http::HeaderMap {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
self.get(key).and_then(|value| value.to_str().ok())
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
self.iter().map(|(key, _)| key.as_str())
}
}
#[repr(transparent)]
struct ParentSourceImpl<T: ParentSource>(T);
impl<T: ParentSource> Extractor for ParentSourceImpl<T> {
#[inline(always)]
fn get(&self, key: &str) -> Option<&str> {
self.0.get(key)
}
#[inline(always)]
fn keys(&self) -> Vec<&str> {
self.0.keys().collect()
}
}
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct ParentSourceIter<'a, K: AsRef<str> + 'a, V: AsRef<str> + 'a, T: IntoIterator<Item = (&'a K, &'a V)> + Copy + 'a> {
inner: T,
_fields: marker::PhantomData<(&'a K, &'a V)>,
}
impl<'a, K: AsRef<str> + 'a, V: AsRef<str> + 'a, T: IntoIterator<Item = (&'a K, &'a V)> + Copy + 'a> ParentSourceIter<'a, K, V, T> {
#[inline(always)]
pub const fn new(inner: T) -> Self {
Self {
inner,
_fields: marker::PhantomData
}
}
}
impl<'a, K: AsRef<str> + 'a, V: AsRef<str> + 'a, T: IntoIterator<Item = (&'a K, &'a V)> + Copy + 'a> ParentSource for ParentSourceIter<'a, K, V, T> {
#[inline(always)]
fn get(&self, expected_key: &str) -> Option<&str> {
for (key, value) in self.inner.into_iter() {
if key.as_ref() == expected_key {
return Some(value.as_ref())
}
}
None
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
self.inner.into_iter().map(|(key, _)| key.as_ref())
}
}
impl<K: core::borrow::Borrow<str> + core::hash::Hash + Eq, V: AsRef<str>, S: core::hash::BuildHasher> ParentSource for std::collections::HashMap<K, V, S> {
#[inline(always)]
fn get(&self, expected_key: &str) -> Option<&str> {
std::collections::HashMap::get(self, expected_key).map(|value| value.as_ref())
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
self.keys().map(|key| key.borrow())
}
}
impl<K: core::borrow::Borrow<str> + Ord, V: AsRef<str>> ParentSource for std::collections::BTreeMap<K, V> {
#[inline(always)]
fn get(&self, expected_key: &str) -> Option<&str> {
std::collections::BTreeMap::get(self, expected_key).map(|value| value.as_ref())
}
#[inline(always)]
fn keys(&self) -> impl Iterator<Item = &str> {
self.keys().map(|key| key.borrow())
}
}
pub struct Context {
context: opentelemetry::context::Context,
}
impl Context {
pub fn new_from_parent(span: tracing::Span, source: impl ParentSource) -> (tracing::Span, Self) {
use tracing_opentelemetry::OpenTelemetrySpanExt;
let parent = TraceContextPropagator::new().extract(&ParentSourceImpl(source));
let _ = span.set_parent(parent);
let this = Self {
context: span.context()
};
(span, this)
}
#[inline(always)]
pub fn current() -> Self {
use tracing_opentelemetry::OpenTelemetrySpanExt;
Self {
context: tracing::Span::current().context(),
}
}
#[inline(always)]
pub fn set_status(&self, status: Result<(), std::borrow::Cow<'static, str>>) {
let span = self.context.span();
span.set_status(match status {
Ok(()) => Status::Ok,
Err(description) => Status::Error {
description,
}
});
}
#[inline(always)]
pub fn set_error<E: core::error::Error>(&self, error: &E) {
let span = self.context.span();
span.record_error(error);
span.set_status(Status::Error {
description: error.to_string().into()
});
}
#[inline(always)]
pub fn inject_into(&self, dest: &mut impl ParentDestination) {
TraceContextPropagator::new().inject_context(&self.context, &mut ParentDestinationImpl(dest));
}
}
impl fmt::Debug for Context {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.context, fmt)
}
}