1#![warn(unreachable_pub)]
4use std::fmt;
5
6use opentelemetry::trace::TraceContextExt;
7use serde::Deserialize;
8use serde::Serialize;
9use tracing::Span;
10use tracing_subscriber::Registry;
11use tracing_subscriber::registry::LookupSpan;
12
13use crate::plugins::telemetry::otel::OpenTelemetrySpanExt;
14use crate::plugins::telemetry::reload::otel::IsSampled;
15
16#[cfg_attr(test, derive(Default))]
18#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
19pub struct TraceId([u8; 16]);
20
21impl TraceId {
22 pub fn maybe_new() -> Option<Self> {
24 let span = Span::current();
25 let context = span.context();
26 let span_ref = context.span();
27 let span_context = span_ref.span_context();
28 if span_context.is_sampled() {
29 Some(Self(span_context.trace_id().to_bytes()))
30 } else {
31 None
32 }
33 }
34
35 pub(crate) fn current() -> Option<Self> {
37 Span::current()
38 .with_subscriber(move |(id, dispatch)| {
39 if let Some(reg) = dispatch.downcast_ref::<Registry>() {
40 match reg.span(id) {
41 None => {
42 eprintln!("no spanref, this is a bug");
43 None
44 }
45 Some(s) => s.get_trace_id(),
46 }
47 } else {
48 ::tracing::error!("no Registry, this is a bug");
49 None
50 }
51 })
52 .flatten()
53 }
54
55 pub fn as_bytes(&self) -> &[u8; 16] {
57 &self.0
58 }
59
60 pub const fn to_u128(&self) -> u128 {
62 u128::from_be_bytes(self.0)
63 }
64}
65
66impl fmt::Display for TraceId {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{:032x}", self.to_u128())
69 }
70}
71
72impl From<[u8; 16]> for TraceId {
73 fn from(value: [u8; 16]) -> Self {
74 Self(value)
75 }
76}
77
78#[cfg(test)]
84mod test {
85 use once_cell::sync::Lazy;
86 use opentelemetry::trace::TracerProvider;
87 use parking_lot::Mutex;
88 use tracing_subscriber::Registry;
89 use tracing_subscriber::layer::SubscriberExt;
90
91 use super::TraceId;
92 use crate::plugins::telemetry::otel;
93
94 static TRACING_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
100
101 #[test]
102 fn it_returns_invalid_trace_id() {
103 let my_id = TraceId::maybe_new();
104 assert!(my_id.is_none());
105 }
106
107 #[test]
108 fn it_correctly_compares_invalid_and_invalid_trace_id() {
109 let my_id = TraceId::maybe_new();
110 let other_id = TraceId::maybe_new();
111 assert!(my_id.is_none());
112 assert!(other_id.is_none());
113 assert!(other_id == my_id);
114 }
115
116 #[tokio::test]
117 async fn it_returns_valid_trace_id() {
118 use opentelemetry::InstrumentationScope;
119
120 let _guard = TRACING_LOCK.lock();
121 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
124 .with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
125 .build();
126 let tracer = provider.tracer_with_scope(InstrumentationScope::builder("noop").build());
127
128 let telemetry = otel::layer().force_sampling().with_tracer(tracer);
129 let subscriber = Registry::default().with(telemetry);
132 tracing::subscriber::with_default(subscriber, || {
134 let _span = tracing::trace_span!("trace test").entered();
136 assert!(TraceId::maybe_new().is_some());
137 });
138 }
139
140 #[test]
141 fn it_correctly_compares_valid_and_invalid_trace_id() {
142 let _guard = TRACING_LOCK.lock();
143 let my_id = TraceId::maybe_new();
144 assert!(my_id.is_none());
145 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
147 .with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
148 .build();
149 let tracer = provider
150 .tracer_with_scope(opentelemetry::InstrumentationScope::builder("noop").build());
151 let telemetry = otel::layer().force_sampling().with_tracer(tracer);
152 let subscriber = Registry::default().with(telemetry);
155 tracing::subscriber::with_default(subscriber, || {
157 let _span = tracing::trace_span!("trace test").entered();
159
160 let other_id = TraceId::maybe_new();
161 assert!(other_id.is_some());
162 assert_ne!(other_id, my_id);
163 });
164 }
165
166 #[test]
167 fn it_correctly_compares_valid_and_valid_trace_id() {
168 let _guard = TRACING_LOCK.lock();
169 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
171 .with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
172 .build();
173 let tracer = provider
174 .tracer_with_scope(opentelemetry::InstrumentationScope::builder("noop").build());
175 let telemetry = otel::layer().force_sampling().with_tracer(tracer);
176 let subscriber = Registry::default().with(telemetry);
179 tracing::subscriber::with_default(subscriber, || {
181 let _span = tracing::trace_span!("trace test").entered();
183
184 let my_id = TraceId::maybe_new();
185 assert!(my_id.is_some());
186 let other_id = TraceId::maybe_new();
187 assert_eq!(other_id, my_id);
188 });
189 }
190}