#![warn(unreachable_pub)]
use std::fmt;
use opentelemetry::trace::TraceContextExt;
use serde::Deserialize;
use serde::Serialize;
use tracing::Span;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Registry;
use crate::plugins::telemetry::otel::OpenTelemetrySpanExt;
use crate::plugins::telemetry::reload::IsSampled;
#[cfg_attr(test, derive(Default))]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct TraceId([u8; 16]);
impl TraceId {
pub fn maybe_new() -> Option<Self> {
let span = Span::current();
let context = span.context();
let span_ref = context.span();
let span_context = span_ref.span_context();
if span_context.is_sampled() {
Some(Self(span_context.trace_id().to_bytes()))
} else {
None
}
}
pub(crate) fn current() -> Option<Self> {
let trace_id = Span::current()
.with_subscriber(move |(id, dispatch)| {
if let Some(reg) = dispatch.downcast_ref::<Registry>() {
match reg.span(id) {
None => {
eprintln!("no spanref, this is a bug");
None
}
Some(s) => s.get_trace_id(),
}
} else {
::tracing::error!("no Registry, this is a bug");
None
}
})
.flatten();
trace_id
}
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
pub const fn to_u128(&self) -> u128 {
u128::from_be_bytes(self.0)
}
}
impl fmt::Display for TraceId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:032x}", self.to_u128())
}
}
impl From<[u8; 16]> for TraceId {
fn from(value: [u8; 16]) -> Self {
Self(value)
}
}
#[cfg(test)]
mod test {
use std::sync::Mutex;
use once_cell::sync::Lazy;
use opentelemetry::trace::TracerProvider;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;
use super::TraceId;
use crate::plugins::telemetry::otel;
static TRACING_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
#[test]
fn it_returns_invalid_trace_id() {
let my_id = TraceId::maybe_new();
assert!(my_id.is_none());
}
#[test]
fn it_correctly_compares_invalid_and_invalid_trace_id() {
let my_id = TraceId::maybe_new();
let other_id = TraceId::maybe_new();
assert!(my_id.is_none());
assert!(other_id.is_none());
assert!(other_id == my_id);
}
#[tokio::test]
async fn it_returns_valid_trace_id() {
let _guard = TRACING_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let provider = opentelemetry::sdk::trace::TracerProvider::builder()
.with_simple_exporter(
opentelemetry_stdout::SpanExporter::builder()
.with_writer(std::io::stdout())
.build(),
)
.build();
let tracer = provider.versioned_tracer("noop", None::<String>, None::<String>, None);
let telemetry = otel::layer().force_sampling().with_tracer(tracer);
let subscriber = Registry::default().with(telemetry);
tracing::subscriber::with_default(subscriber, || {
let _span = tracing::trace_span!("trace test").entered();
assert!(TraceId::maybe_new().is_some());
});
}
#[test]
fn it_correctly_compares_valid_and_invalid_trace_id() {
let _guard = TRACING_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let my_id = TraceId::maybe_new();
assert!(my_id.is_none());
let provider = opentelemetry::sdk::trace::TracerProvider::builder()
.with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
.build();
let tracer = provider.versioned_tracer("noop", None::<String>, None::<String>, None);
let telemetry = otel::layer().force_sampling().with_tracer(tracer);
let subscriber = Registry::default().with(telemetry);
tracing::subscriber::with_default(subscriber, || {
let _span = tracing::trace_span!("trace test").entered();
let other_id = TraceId::maybe_new();
assert!(other_id.is_some());
assert_ne!(other_id, my_id);
});
}
#[test]
fn it_correctly_compares_valid_and_valid_trace_id() {
let _guard = TRACING_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let provider = opentelemetry::sdk::trace::TracerProvider::builder()
.with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
.build();
let tracer = provider.versioned_tracer("noop", None::<String>, None::<String>, None);
let telemetry = otel::layer().force_sampling().with_tracer(tracer);
let subscriber = Registry::default().with(telemetry);
tracing::subscriber::with_default(subscriber, || {
let _span = tracing::trace_span!("trace test").entered();
let my_id = TraceId::maybe_new();
assert!(my_id.is_some());
let other_id = TraceId::maybe_new();
assert_eq!(other_id, my_id);
});
}
}