tracing_opentelemetry/
layer.rs

1use crate::stack::IdValueStack;
2use crate::{OtelData, OtelDataState};
3pub use filtered::FilteredOpenTelemetryLayer;
4use opentelemetry::ContextGuard;
5use opentelemetry::{
6    trace::{self as otel, noop, Span, SpanBuilder, SpanKind, Status, TraceContextExt},
7    Context as OtelContext, Key, KeyValue, StringValue, Value,
8};
9use std::cell::RefCell;
10use std::thread;
11#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
12use std::time::Instant;
13use std::{any::TypeId, borrow::Cow};
14use std::{fmt, vec};
15use std::{marker, mem::take};
16use tracing_core::span::{self, Attributes, Id, Record};
17use tracing_core::{field, Event, Subscriber};
18#[cfg(feature = "tracing-log")]
19use tracing_log::NormalizeEvent;
20use tracing_subscriber::layer::Context;
21use tracing_subscriber::layer::Filter;
22use tracing_subscriber::registry::{ExtensionsMut, LookupSpan};
23use tracing_subscriber::Layer;
24#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
25use web_time::Instant;
26
27mod filtered;
28
29const SPAN_NAME_FIELD: &str = "otel.name";
30const SPAN_KIND_FIELD: &str = "otel.kind";
31const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code";
32const SPAN_STATUS_DESCRIPTION_FIELD: &str = "otel.status_description";
33const SPAN_EVENT_COUNT_FIELD: &str = "otel.tracing_event_count";
34
35const EVENT_EXCEPTION_NAME: &str = "exception";
36const FIELD_EXCEPTION_MESSAGE: &str = "exception.message";
37const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace";
38
39/// An [OpenTelemetry] propagation layer for use in a project that uses
40/// [tracing].
41///
42/// [OpenTelemetry]: https://opentelemetry.io
43/// [tracing]: https://github.com/tokio-rs/tracing
44pub struct OpenTelemetryLayer<S, T> {
45    tracer: T,
46    location: bool,
47    tracked_inactivity: bool,
48    with_threads: bool,
49    with_level: bool,
50    with_target: bool,
51    context_activation: bool,
52    sem_conv_config: SemConvConfig,
53    with_context: WithContext,
54    _registry: marker::PhantomData<S>,
55}
56
57impl<S> Default for OpenTelemetryLayer<S, noop::NoopTracer>
58where
59    S: Subscriber + for<'span> LookupSpan<'span>,
60{
61    fn default() -> Self {
62        OpenTelemetryLayer::new(noop::NoopTracer::new())
63    }
64}
65
66/// Construct a layer to track spans via [OpenTelemetry].
67///
68/// It will convert the tracing spans to OpenTelemetry spans
69/// and tracing events which are in a span to events in the currently active OpenTelemetry span.
70/// Child-Parent links will be automatically translated as well.
71/// Follows-From links will also be generated, but only if the originating span has not been closed yet.
72///
73/// Various translations exist between semantic conventions of OpenTelemetry and `tracing`,
74/// such as including level information as fields, or mapping error fields to exceptions.
75/// See the various `layer().with_*` functions for configuration of these features.
76///
77/// [OpenTelemetry]: https://opentelemetry.io
78///
79/// # Examples
80///
81/// ```rust,no_run
82/// use tracing_subscriber::layer::SubscriberExt;
83/// use tracing_subscriber::Registry;
84///
85/// // Use the tracing subscriber `Registry`, or any other subscriber
86/// // that impls `LookupSpan`
87/// let subscriber = Registry::default().with(tracing_opentelemetry::layer());
88/// # drop(subscriber);
89/// ```
90pub fn layer<S>() -> OpenTelemetryLayer<S, noop::NoopTracer>
91where
92    S: Subscriber + for<'span> LookupSpan<'span>,
93{
94    OpenTelemetryLayer::default()
95}
96
97///
98/// This struct lets us call back into the layer from the [crate::OpenTelemetrySpanExt] methods,
99/// letting us access and mutate the underlying data on the layer side in the context of
100/// tokio-tracing's span operations.
101///
102/// The functions on this struct "remember" the types of the subscriber so that we
103/// can downcast to something aware of them without knowing those
104/// types at the callsite.
105///
106/// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs
107pub(crate) struct WithContext {
108    ///
109    /// Provides access to the OtelData associated with the given span ID.
110    ///
111    #[allow(clippy::type_complexity)]
112    pub(crate) with_context: fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
113
114    ///
115    /// Ensures the given SpanId has been activated - that is, created in the OTel side of things,
116    /// and had its SpanBuilder consumed - and then provides access to the OtelData associated with it.
117    ///
118    #[allow(clippy::type_complexity)]
119    pub(crate) with_activated_context:
120        fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
121
122    ///
123    /// Ensures the given SpanId has been activated - that is, created in the OTel side of things,
124    /// and had its SpanBuilder consumed - and then provides access to the OTel Context associated with it.
125    ///
126    #[allow(clippy::type_complexity)]
127    pub(crate) with_activated_otel_context:
128        fn(&tracing::Dispatch, &mut ExtensionsMut<'_>, f: &mut dyn FnMut(&OtelContext)),
129}
130
131impl WithContext {
132    ///
133    /// Return the OtelData associated with the given spanId.
134    ///
135    pub(crate) fn with_context(
136        &self,
137        dispatch: &tracing::Dispatch,
138        id: &span::Id,
139        mut f: impl FnMut(&mut OtelData),
140    ) {
141        (self.with_context)(dispatch, id, &mut f)
142    }
143
144    ///
145    /// If the span associated with the given SpanId has not yet been
146    /// built, build it, consuming the span ID.
147    ///
148    /// Optionally performs additional operations on the OtelData after building.
149    ///
150    pub(crate) fn with_activated_context(
151        &self,
152        dispatch: &tracing::Dispatch,
153        id: &span::Id,
154        mut f: impl FnMut(&mut OtelData),
155    ) {
156        (self.with_activated_context)(dispatch, id, &mut f)
157    }
158
159    ///
160    /// Ensures the given SpanId has been activated - that is, created in the OTel side of things,
161    /// and had its SpanBuilder consumed - and then provides access to the OtelData associated with it.
162    ///
163    #[allow(clippy::type_complexity)]
164    pub(crate) fn with_activated_otel_context(
165        &self,
166        dispatch: &tracing::Dispatch,
167        extensions: &mut ExtensionsMut<'_>,
168        mut f: impl FnMut(&OtelContext),
169    ) {
170        (self.with_activated_otel_context)(dispatch, extensions, &mut f)
171    }
172}
173
174fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
175    match s {
176        s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
177        s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
178        s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
179        s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
180        s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
181        _ => None,
182    }
183}
184
185fn str_to_status(s: &str) -> otel::Status {
186    match s {
187        s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
188        s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
189        _ => otel::Status::Unset,
190    }
191}
192
193#[derive(Default)]
194struct SpanBuilderUpdates {
195    name: Option<Cow<'static, str>>,
196    span_kind: Option<SpanKind>,
197    status: Option<Status>,
198    attributes: Option<Vec<KeyValue>>,
199}
200
201impl SpanBuilderUpdates {
202    fn update(self, span_builder: &mut SpanBuilder, s: &mut Status) {
203        let Self {
204            name,
205            span_kind,
206            status,
207            attributes,
208        } = self;
209
210        if let Some(name) = name {
211            span_builder.name = name;
212        }
213        if let Some(span_kind) = span_kind {
214            span_builder.span_kind = Some(span_kind);
215        }
216        if let Some(status) = status {
217            *s = status;
218        }
219        if let Some(attributes) = attributes {
220            if let Some(builder_attributes) = &mut span_builder.attributes {
221                builder_attributes.extend(attributes);
222            } else {
223                span_builder.attributes = Some(attributes);
224            }
225        }
226    }
227
228    fn update_span(self, span: &opentelemetry::trace::SpanRef<'_>) {
229        let Self {
230            status, attributes, ..
231        } = self;
232
233        if let Some(status) = status {
234            span.set_status(status);
235        }
236        if let Some(attributes) = attributes {
237            span.set_attributes(attributes);
238        }
239    }
240}
241
242struct SpanEventVisitor<'a, 'b> {
243    event_builder: &'a mut otel::Event,
244    span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
245    sem_conv_config: SemConvConfig,
246}
247
248impl field::Visit for SpanEventVisitor<'_, '_> {
249    /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values.
250    ///
251    /// [`Span`]: opentelemetry::trace::Span
252    fn record_bool(&mut self, field: &field::Field, value: bool) {
253        match field.name() {
254            "message" => self.event_builder.name = value.to_string().into(),
255            // Skip fields that are actually log metadata that have already been handled
256            #[cfg(feature = "tracing-log")]
257            name if name.starts_with("log.") => (),
258            name => {
259                self.event_builder
260                    .attributes
261                    .push(KeyValue::new(name, value));
262            }
263        }
264    }
265
266    /// Record events on the underlying OpenTelemetry [`Span`] from `f64` values.
267    ///
268    /// [`Span`]: opentelemetry::trace::Span
269    fn record_f64(&mut self, field: &field::Field, value: f64) {
270        match field.name() {
271            "message" => self.event_builder.name = value.to_string().into(),
272            // Skip fields that are actually log metadata that have already been handled
273            #[cfg(feature = "tracing-log")]
274            name if name.starts_with("log.") => (),
275            name => {
276                self.event_builder
277                    .attributes
278                    .push(KeyValue::new(name, value));
279            }
280        }
281    }
282
283    /// Record events on the underlying OpenTelemetry [`Span`] from `i64` values.
284    ///
285    /// [`Span`]: opentelemetry::trace::Span
286    fn record_i64(&mut self, field: &field::Field, value: i64) {
287        match field.name() {
288            "message" => self.event_builder.name = value.to_string().into(),
289            // Skip fields that are actually log metadata that have already been handled
290            #[cfg(feature = "tracing-log")]
291            name if name.starts_with("log.") => (),
292            name => {
293                self.event_builder
294                    .attributes
295                    .push(KeyValue::new(name, value));
296            }
297        }
298    }
299
300    /// Record events on the underlying OpenTelemetry [`Span`] from `&str` values.
301    ///
302    /// [`Span`]: opentelemetry::trace::Span
303    fn record_str(&mut self, field: &field::Field, value: &str) {
304        match field.name() {
305            "message" => self.event_builder.name = value.to_string().into(),
306            // While tracing supports the error primitive, the instrumentation macro does not
307            // use the primitive and instead uses the debug or display primitive.
308            // In both cases, an event with an empty name and with an error attribute is created.
309            "error" if self.event_builder.name.is_empty() => {
310                if self.sem_conv_config.error_events_to_status {
311                    self.span_builder_updates
312                        .get_or_insert_with(SpanBuilderUpdates::default)
313                        .status
314                        .replace(otel::Status::error(format!("{value:?}")));
315                }
316                if self.sem_conv_config.error_events_to_exceptions {
317                    self.event_builder.name = EVENT_EXCEPTION_NAME.into();
318                    self.event_builder
319                        .attributes
320                        .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
321                } else {
322                    self.event_builder
323                        .attributes
324                        .push(KeyValue::new("error", format!("{value:?}")));
325                }
326            }
327            // Skip fields that are actually log metadata that have already been handled
328            #[cfg(feature = "tracing-log")]
329            name if name.starts_with("log.") => (),
330            name => {
331                self.event_builder
332                    .attributes
333                    .push(KeyValue::new(name, value.to_string()));
334            }
335        }
336    }
337
338    /// Record events on the underlying OpenTelemetry [`Span`] from values that
339    /// implement Debug.
340    ///
341    /// [`Span`]: opentelemetry::trace::Span
342    fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
343        match field.name() {
344            "message" => self.event_builder.name = format!("{value:?}").into(),
345            // While tracing supports the error primitive, the instrumentation macro does not
346            // use the primitive and instead uses the debug or display primitive.
347            // In both cases, an event with an empty name and with an error attribute is created.
348            "error" if self.event_builder.name.is_empty() => {
349                if self.sem_conv_config.error_events_to_status {
350                    self.span_builder_updates
351                        .get_or_insert_with(SpanBuilderUpdates::default)
352                        .status
353                        .replace(otel::Status::error(format!("{value:?}")));
354                }
355                if self.sem_conv_config.error_events_to_exceptions {
356                    self.event_builder.name = EVENT_EXCEPTION_NAME.into();
357                    self.event_builder
358                        .attributes
359                        .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
360                } else {
361                    self.event_builder
362                        .attributes
363                        .push(KeyValue::new("error", format!("{value:?}")));
364                }
365            }
366            // Skip fields that are actually log metadata that have already been handled
367            #[cfg(feature = "tracing-log")]
368            name if name.starts_with("log.") => (),
369            name => {
370                self.event_builder
371                    .attributes
372                    .push(KeyValue::new(name, format!("{value:?}")));
373            }
374        }
375    }
376
377    /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s
378    /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field
379    ///
380    /// [`Span`]: opentelemetry::trace::Span
381    fn record_error(
382        &mut self,
383        field: &tracing_core::Field,
384        value: &(dyn std::error::Error + 'static),
385    ) {
386        let mut chain: Vec<StringValue> = Vec::new();
387        let mut next_err = value.source();
388
389        while let Some(err) = next_err {
390            chain.push(err.to_string().into());
391            next_err = err.source();
392        }
393
394        let error_msg = value.to_string();
395
396        if self.sem_conv_config.error_fields_to_exceptions {
397            self.event_builder.attributes.push(KeyValue::new(
398                Key::new(FIELD_EXCEPTION_MESSAGE),
399                Value::String(StringValue::from(error_msg.clone())),
400            ));
401
402            // NOTE: This is actually not the stacktrace of the exception. This is
403            // the "source chain". It represents the heirarchy of errors from the
404            // app level to the lowest level such as IO. It does not represent all
405            // of the callsites in the code that led to the error happening.
406            // `std::error::Error::backtrace` is a nightly-only API and cannot be
407            // used here until the feature is stabilized.
408            self.event_builder.attributes.push(KeyValue::new(
409                Key::new(FIELD_EXCEPTION_STACKTRACE),
410                Value::Array(chain.clone().into()),
411            ));
412        }
413
414        if self.sem_conv_config.error_records_to_exceptions {
415            let attributes = self
416                .span_builder_updates
417                .get_or_insert_with(SpanBuilderUpdates::default)
418                .attributes
419                .get_or_insert_with(Vec::new);
420
421            attributes.push(KeyValue::new(
422                FIELD_EXCEPTION_MESSAGE,
423                Value::String(error_msg.clone().into()),
424            ));
425
426            // NOTE: This is actually not the stacktrace of the exception. This is
427            // the "source chain". It represents the heirarchy of errors from the
428            // app level to the lowest level such as IO. It does not represent all
429            // of the callsites in the code that led to the error happening.
430            // `std::error::Error::backtrace` is a nightly-only API and cannot be
431            // used here until the feature is stabilized.
432            attributes.push(KeyValue::new(
433                FIELD_EXCEPTION_STACKTRACE,
434                Value::Array(chain.clone().into()),
435            ));
436        }
437
438        self.event_builder.attributes.push(KeyValue::new(
439            Key::new(field.name()),
440            Value::String(StringValue::from(error_msg)),
441        ));
442        self.event_builder.attributes.push(KeyValue::new(
443            Key::new(format!("{}.chain", field.name())),
444            Value::Array(chain.into()),
445        ));
446    }
447}
448
449/// Control over the mapping between tracing fields/events and OpenTelemetry conventional status/exception fields
450#[derive(Clone, Copy)]
451struct SemConvConfig {
452    /// If an error value is recorded on an event/span, should the otel fields
453    /// be added
454    ///
455    /// Note that this uses tracings `record_error` which is only implemented for `(dyn Error + 'static)`.
456    error_fields_to_exceptions: bool,
457
458    /// If an error value is recorded on an event, should the otel fields be
459    /// added to the corresponding span
460    ///
461    /// Note that this uses tracings `record_error` which is only implemented for `(dyn Error + 'static)`.
462    error_records_to_exceptions: bool,
463
464    /// If a function is instrumented and returns a `Result`, should the error
465    /// value be propagated to the span status.
466    ///
467    /// Without this enabled, the span status will be "Error" with an empty description
468    /// when at least one error event is recorded in the span.
469    ///
470    /// Note: the instrument macro will emit an error event if the function returns the `Err` variant.
471    /// This is not affected by this setting. Disabling this will only affect the span status.
472    error_events_to_status: bool,
473
474    /// If an event with an empty name and a field named `error` is recorded,
475    /// should the event be rewritten to have the name `exception` and the field `exception.message`
476    ///
477    /// Follows the semantic conventions for exceptions.
478    ///
479    /// Note: the instrument macro will emit an error event if the function returns the `Err` variant.
480    /// This is not affected by this setting. Disabling this will only affect the created fields on the OTel span.
481    error_events_to_exceptions: bool,
482}
483
484struct SpanAttributeVisitor<'a> {
485    span_builder_updates: &'a mut SpanBuilderUpdates,
486    sem_conv_config: SemConvConfig,
487}
488
489impl SpanAttributeVisitor<'_> {
490    fn record(&mut self, attribute: KeyValue) {
491        self.span_builder_updates
492            .attributes
493            .get_or_insert_with(Vec::new)
494            .push(KeyValue::new(attribute.key, attribute.value));
495    }
496}
497
498impl field::Visit for SpanAttributeVisitor<'_> {
499    /// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values.
500    ///
501    /// [`Span`]: opentelemetry::trace::Span
502    fn record_bool(&mut self, field: &field::Field, value: bool) {
503        self.record(KeyValue::new(field.name(), value));
504    }
505
506    /// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values.
507    ///
508    /// [`Span`]: opentelemetry::trace::Span
509    fn record_f64(&mut self, field: &field::Field, value: f64) {
510        self.record(KeyValue::new(field.name(), value));
511    }
512
513    /// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values.
514    ///
515    /// [`Span`]: opentelemetry::trace::Span
516    fn record_i64(&mut self, field: &field::Field, value: i64) {
517        self.record(KeyValue::new(field.name(), value));
518    }
519
520    /// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values.
521    ///
522    /// [`Span`]: opentelemetry::trace::Span
523    fn record_str(&mut self, field: &field::Field, value: &str) {
524        match field.name() {
525            SPAN_NAME_FIELD => self.span_builder_updates.name = Some(value.to_string().into()),
526            SPAN_KIND_FIELD => self.span_builder_updates.span_kind = str_to_span_kind(value),
527            SPAN_STATUS_CODE_FIELD => self.span_builder_updates.status = Some(str_to_status(value)),
528            SPAN_STATUS_DESCRIPTION_FIELD => {
529                self.span_builder_updates.status = Some(otel::Status::error(value.to_string()))
530            }
531            _ => self.record(KeyValue::new(field.name(), value.to_string())),
532        }
533    }
534
535    /// Set attributes on the underlying OpenTelemetry [`Span`] from values that
536    /// implement Debug.
537    ///
538    /// [`Span`]: opentelemetry::trace::Span
539    fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
540        match field.name() {
541            SPAN_NAME_FIELD => self.span_builder_updates.name = Some(format!("{value:?}").into()),
542            SPAN_KIND_FIELD => {
543                self.span_builder_updates.span_kind = str_to_span_kind(&format!("{value:?}"))
544            }
545            SPAN_STATUS_CODE_FIELD => {
546                self.span_builder_updates.status = Some(str_to_status(&format!("{value:?}")))
547            }
548            SPAN_STATUS_DESCRIPTION_FIELD => {
549                self.span_builder_updates.status = Some(otel::Status::error(format!("{value:?}")))
550            }
551            _ => self.record(KeyValue::new(
552                Key::new(field.name()),
553                Value::String(format!("{value:?}").into()),
554            )),
555        }
556    }
557
558    /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s
559    /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field
560    ///
561    /// [`Span`]: opentelemetry::trace::Span
562    fn record_error(
563        &mut self,
564        field: &tracing_core::Field,
565        value: &(dyn std::error::Error + 'static),
566    ) {
567        let mut chain: Vec<StringValue> = Vec::new();
568        let mut next_err = value.source();
569
570        while let Some(err) = next_err {
571            chain.push(err.to_string().into());
572            next_err = err.source();
573        }
574
575        let error_msg = value.to_string();
576
577        if self.sem_conv_config.error_fields_to_exceptions {
578            self.record(KeyValue::new(
579                Key::new(FIELD_EXCEPTION_MESSAGE),
580                Value::from(error_msg.clone()),
581            ));
582
583            // NOTE: This is actually not the stacktrace of the exception. This is
584            // the "source chain". It represents the heirarchy of errors from the
585            // app level to the lowest level such as IO. It does not represent all
586            // of the callsites in the code that led to the error happening.
587            // `std::error::Error::backtrace` is a nightly-only API and cannot be
588            // used here until the feature is stabilized.
589            self.record(KeyValue::new(
590                Key::new(FIELD_EXCEPTION_STACKTRACE),
591                Value::Array(chain.clone().into()),
592            ));
593        }
594
595        self.record(KeyValue::new(
596            Key::new(field.name()),
597            Value::String(error_msg.into()),
598        ));
599        self.record(KeyValue::new(
600            Key::new(format!("{}.chain", field.name())),
601            Value::Array(chain.into()),
602        ));
603    }
604}
605
606impl<S, T> OpenTelemetryLayer<S, T>
607where
608    S: Subscriber + for<'span> LookupSpan<'span>,
609    T: otel::Tracer + 'static,
610    T::Span: Send + Sync,
611{
612    /// Set the [`Tracer`] that this layer will use to produce and track
613    /// OpenTelemetry [`Span`]s.
614    ///
615    /// [`Tracer`]: opentelemetry::trace::Tracer
616    /// [`Span`]: opentelemetry::trace::Span
617    ///
618    /// # Examples
619    ///
620    /// ```no_run
621    /// use tracing_opentelemetry::OpenTelemetryLayer;
622    /// use tracing_subscriber::layer::SubscriberExt;
623    /// use opentelemetry::trace::TracerProvider as _;
624    /// use tracing_subscriber::Registry;
625    ///
626    /// // Create an OTLP pipeline exporter for a `trace_demo` service.
627    ///
628    /// let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
629    ///     .with_tonic()
630    ///     .build()
631    ///     .unwrap();
632    ///
633    /// let tracer = opentelemetry_sdk::trace::SdkTracerProvider::builder()
634    ///     .with_simple_exporter(otlp_exporter)
635    ///     .build()
636    ///     .tracer("trace_demo");
637    ///
638    /// // Create a layer with the configured tracer
639    /// let otel_layer = OpenTelemetryLayer::new(tracer);
640    ///
641    /// // Use the tracing subscriber `Registry`, or any other subscriber
642    /// // that impls `LookupSpan`
643    /// let subscriber = Registry::default().with(otel_layer);
644    /// # drop(subscriber);
645    /// ```
646    pub fn new(tracer: T) -> Self {
647        OpenTelemetryLayer {
648            tracer,
649            location: true,
650            tracked_inactivity: true,
651            with_threads: true,
652            with_level: false,
653            with_target: true,
654            context_activation: true,
655            sem_conv_config: SemConvConfig {
656                error_fields_to_exceptions: true,
657                error_records_to_exceptions: true,
658                error_events_to_exceptions: true,
659                error_events_to_status: true,
660            },
661            with_context: WithContext {
662                with_context: Self::get_context,
663                with_activated_context: Self::get_activated_context,
664                with_activated_otel_context: Self::get_activated_otel_context,
665            },
666            _registry: marker::PhantomData,
667        }
668    }
669
670    /// Set the [`Tracer`] that this layer will use to produce and track
671    /// OpenTelemetry [`Span`]s.
672    ///
673    /// [`Tracer`]: opentelemetry::trace::Tracer
674    /// [`Span`]: opentelemetry::trace::Span
675    ///
676    /// # Examples
677    ///
678    /// ```no_run
679    /// use tracing_subscriber::layer::SubscriberExt;
680    /// use tracing_subscriber::Registry;
681    /// use opentelemetry::trace::TracerProvider;
682    ///
683    /// // Create an OTLP pipeline exporter for a `trace_demo` service.
684    ///
685    /// let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
686    ///     .with_tonic()
687    ///     .build()
688    ///     .unwrap();
689    ///
690    /// let tracer = opentelemetry_sdk::trace::SdkTracerProvider::builder()
691    ///     .with_simple_exporter(otlp_exporter)
692    ///     .build()
693    ///     .tracer("trace_demo");
694    ///
695    /// // Create a layer with the configured tracer
696    /// let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer);
697    ///
698    /// // Use the tracing subscriber `Registry`, or any other subscriber
699    /// // that impls `LookupSpan`
700    /// let subscriber = Registry::default().with(otel_layer);
701    /// # drop(subscriber);
702    /// ```
703    pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
704    where
705        Tracer: otel::Tracer + 'static,
706        Tracer::Span: Send + Sync,
707    {
708        OpenTelemetryLayer {
709            tracer,
710            location: self.location,
711            tracked_inactivity: self.tracked_inactivity,
712            with_threads: self.with_threads,
713            with_level: self.with_level,
714            with_target: self.with_target,
715            context_activation: self.context_activation,
716            sem_conv_config: self.sem_conv_config,
717            with_context: WithContext {
718                with_context: OpenTelemetryLayer::<S, Tracer>::get_context,
719                with_activated_context: OpenTelemetryLayer::<S, Tracer>::get_activated_context,
720                with_activated_otel_context:
721                    OpenTelemetryLayer::<S, Tracer>::get_activated_otel_context,
722            },
723            _registry: self._registry,
724            // cannot use ``..self` here due to different generics
725        }
726    }
727
728    /// Sets whether or not span and event metadata should include OpenTelemetry
729    /// exception fields such as `exception.message` and `exception.backtrace`
730    /// when an `Error` value is recorded. If multiple error values are recorded
731    /// on the same span/event, only the most recently recorded error value will
732    /// show up under these fields.
733    ///
734    /// These attributes follow the [OpenTelemetry semantic conventions for
735    /// exceptions][conv].
736    ///
737    /// By default, these attributes are recorded.
738    /// Note that this only works for `(dyn Error + 'static)`.
739    /// See [Implementations on Foreign Types of tracing::Value][impls] or [`OpenTelemetryLayer::with_error_events_to_exceptions`]
740    ///
741    /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/
742    /// [impls]: https://docs.rs/tracing/0.1.37/tracing/trait.Value.html#foreign-impls
743    pub fn with_error_fields_to_exceptions(self, error_fields_to_exceptions: bool) -> Self {
744        Self {
745            sem_conv_config: SemConvConfig {
746                error_fields_to_exceptions,
747                ..self.sem_conv_config
748            },
749            ..self
750        }
751    }
752
753    /// Sets whether or not an event considered for exception mapping (see [`OpenTelemetryLayer::with_error_records_to_exceptions`])
754    /// should be propagated to the span status error description.
755    ///
756    /// By default, these events do set the span status error description.
757    pub fn with_error_events_to_status(self, error_events_to_status: bool) -> Self {
758        Self {
759            sem_conv_config: SemConvConfig {
760                error_events_to_status,
761                ..self.sem_conv_config
762            },
763            ..self
764        }
765    }
766
767    /// Sets whether or not a subset of events following the described schema are mapped to
768    /// events following the [OpenTelemetry semantic conventions for
769    /// exceptions][conv].
770    ///
771    /// * Only events without a message field (unnamed events) and at least one field with the name error
772    ///   are considered for mapping.
773    ///
774    /// By default, these events are mapped.
775    ///
776    /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/
777    pub fn with_error_events_to_exceptions(self, error_events_to_exceptions: bool) -> Self {
778        Self {
779            sem_conv_config: SemConvConfig {
780                error_events_to_exceptions,
781                ..self.sem_conv_config
782            },
783            ..self
784        }
785    }
786
787    /// Sets whether or not reporting an `Error` value on an event will
788    /// propagate the OpenTelemetry exception fields such as `exception.message`
789    /// and `exception.backtrace` to the corresponding span. You do not need to
790    /// enable `with_exception_fields` in order to enable this. If multiple
791    /// error values are recorded on the same span/event, only the most recently
792    /// recorded error value will show up under these fields.
793    ///
794    /// These attributes follow the [OpenTelemetry semantic conventions for
795    /// exceptions][conv].
796    ///
797    /// By default, these attributes are propagated to the span. Note that this only works for `(dyn Error + 'static)`.
798    /// See [Implementations on Foreign Types of tracing::Value][impls] or [`OpenTelemetryLayer::with_error_events_to_exceptions`]
799    ///
800    /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/
801    /// [impls]: https://docs.rs/tracing/0.1.37/tracing/trait.Value.html#foreign-impls
802    pub fn with_error_records_to_exceptions(self, error_records_to_exceptions: bool) -> Self {
803        Self {
804            sem_conv_config: SemConvConfig {
805                error_records_to_exceptions,
806                ..self.sem_conv_config
807            },
808            ..self
809        }
810    }
811
812    /// Sets whether or not span and event metadata should include OpenTelemetry
813    /// attributes with location information, such as the file, module and line number.
814    ///
815    /// These attributes follow the [OpenTelemetry semantic conventions for
816    /// source locations][conv].
817    ///
818    /// By default, locations are enabled.
819    ///
820    /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes/
821    pub fn with_location(self, location: bool) -> Self {
822        Self { location, ..self }
823    }
824
825    /// Sets whether or not spans metadata should include the _busy time_
826    /// (total time for which it was entered), and _idle time_ (total time
827    /// the span existed but was not entered).
828    ///
829    /// By default, inactivity tracking is enabled.
830    pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
831        Self {
832            tracked_inactivity,
833            ..self
834        }
835    }
836
837    /// Sets whether or not spans record additional attributes for the thread
838    /// name and thread ID of the thread they were created on, following the
839    /// [OpenTelemetry semantic conventions for threads][conv].
840    ///
841    /// By default, thread attributes are enabled.
842    ///
843    /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-thread-attributes/
844    pub fn with_threads(self, threads: bool) -> Self {
845        Self {
846            with_threads: threads,
847            ..self
848        }
849    }
850
851    /// Sets whether or not span metadata should include the `tracing` verbosity level information as a `level` field.
852    ///
853    /// The level is always added to events, and based on [`OpenTelemetryLayer::with_error_events_to_status`]
854    /// error-level events will mark the span status as an error.
855    ///
856    /// By default, level information is disabled.
857    pub fn with_level(self, level: bool) -> Self {
858        Self {
859            with_level: level,
860            ..self
861        }
862    }
863
864    /// Sets whether or not span metadata should include an attribute with `target` from `tracing` spans.
865    ///
866    /// By default, the target attribute is enabled..
867    pub fn with_target(self, target: bool) -> Self {
868        Self {
869            with_target: target,
870            ..self
871        }
872    }
873
874    /// Sets whether or not an OpenTelemetry Context should be activated on span entry.
875    ///
876    /// When enabled, entering a span will activate its OpenTelemetry context, making it
877    /// available to other OpenTelemetry instrumentation. This allows for proper context
878    /// propagation across different instrumentation libraries.
879    ///
880    /// By default, context activation is enabled.
881    pub fn with_context_activation(self, context_activation: bool) -> Self {
882        Self {
883            context_activation,
884            ..self
885        }
886    }
887
888    /// Adds a filter for events that counts all events that came to the layer. See
889    /// [`FilteredOpenTelemetryLayer`] for more details.
890    ///
891    /// If you just want to filter the events out and you don't need to count how many happened, you
892    /// can use [`Layer::with_filter`] instead.
893    pub fn with_counting_event_filter<F: Filter<S>>(
894        self,
895        filter: F,
896    ) -> FilteredOpenTelemetryLayer<S, T, F> {
897        FilteredOpenTelemetryLayer::new(self, filter)
898    }
899
900    /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing
901    /// [`span`] through the [`Registry`]. This [`Context`] links spans to their
902    /// parent for proper hierarchical visualization.
903    ///
904    /// [`Context`]: opentelemetry::Context
905    /// [`span`]: tracing::Span
906    /// [`Registry`]: tracing_subscriber::Registry
907    fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext {
908        if let Some(parent) = attrs.parent() {
909            // A span can have an _explicit_ parent that is NOT seen by this `Layer` (for which
910            // `Context::span` returns `None`. This happens if the parent span is filtered away
911            // from the layer by a per-layer filter. In that case, we fall-through to the `else`
912            // case, and consider this span a root span.
913            //
914            // This is likely rare, as most users who use explicit parents will configure their
915            // filters so that children and parents are both seen, but it's not guaranteed. Also,
916            // if users configure their filter with a `reload` filter, it's possible that a parent
917            // and child have different filters as they are created with a filter change
918            // in-between.
919            //
920            // In these case, we prefer to emit a smaller span tree instead of panicking.
921            if let Some(span) = ctx.span(parent) {
922                let mut extensions = span.extensions_mut();
923                if let Some(otel_data) = extensions.get_mut::<OtelData>() {
924                    // If the parent span has a span builder the parent span should be started
925                    // so we get a proper context with the parent span.
926                    return self.with_started_cx(otel_data, &|cx| cx.clone());
927                }
928            }
929        }
930
931        if attrs.is_contextual() {
932            if self.context_activation {
933                // If the span is contextual and we are using context activation,
934                // we should use the current OTel context
935                OtelContext::current()
936            } else {
937                // If the span is contextual and we are not using context activation,
938                // we should use the current tracing context
939                ctx.lookup_current()
940                    .and_then(|span| {
941                        let mut extensions = span.extensions_mut();
942                        extensions
943                            .get_mut::<OtelData>()
944                            .map(|data| self.with_started_cx(data, &|cx| cx.clone()))
945                    })
946                    .unwrap_or_else(OtelContext::current)
947            }
948        } else {
949            OtelContext::default()
950        }
951    }
952
953    /// Provides access to the OpenTelemetry data (`OtelData`) stored in a tracing span.
954    ///
955    /// This function retrieves the span from the subscriber's registry using the provided span ID,
956    /// and then applies the callback function `f` to the span's `OtelData` if present.
957    ///
958    /// # Parameters
959    /// * `dispatch` - A reference to the tracing dispatch, used to access the subscriber
960    /// * `id` - The ID of the span to look up
961    /// * `f` - A callback function that receives a mutable reference to the span's `OtelData`
962    ///   This callback is used to manipulate or extract information from the OpenTelemetry context
963    ///   associated with the tracing span
964    ///
965    fn get_context(dispatch: &tracing::Dispatch, id: &span::Id, f: &mut dyn FnMut(&mut OtelData)) {
966        let subscriber = dispatch
967            .downcast_ref::<S>()
968            .expect("subscriber should downcast to expected type; this is a bug!");
969        let span = subscriber
970            .span(id)
971            .expect("registry should have a span for the current ID");
972
973        let mut extensions = span.extensions_mut();
974        if let Some(otel_data) = extensions.get_mut::<OtelData>() {
975            f(otel_data);
976        }
977    }
978
979    /// Retrieves the OpenTelemetry data for a span and activates its context before calling
980    /// the provided function.
981    ///
982    /// This function retrieves the span from the subscriber's registry using the provided
983    /// span ID, activates the OTel `Context` in the span's `OtelData` if present, and then
984    /// applies the callback function `f` to the `OtelData`.
985    ///
986    /// # Parameters
987    ///
988    /// * `dispatch` - The tracing dispatch to downcast and retrieve the span from
989    /// * `id` - The span ID to look up in the registry
990    /// * `f` - The closure to invoke with mutable access to the span's `OtelData`
991    fn get_activated_context(
992        dispatch: &tracing::Dispatch,
993        id: &span::Id,
994        f: &mut dyn FnMut(&mut OtelData),
995    ) {
996        let subscriber = dispatch
997            .downcast_ref::<S>()
998            .expect("subscriber should downcast to expected type; this is a bug!");
999        let span = subscriber
1000            .span(id)
1001            .expect("registry should have a span for the current ID");
1002
1003        let mut extensions = span.extensions_mut();
1004
1005        Self::get_activated_context_extensions(dispatch, &mut extensions, f)
1006    }
1007
1008    /// Retrieves the activated OpenTelemetry context from a span's extensions and passes it
1009    /// to the provided function.
1010    ///
1011    /// This method activates the context and extracts the current OTel `Context` from the
1012    /// `OtelData` state if present, and then applies the callback function `f` to a reference
1013    /// to the OTel `Context`.
1014    ///
1015    /// # Parameters
1016    ///
1017    /// * `dispatch` - The tracing dispatch to downcast to the `OpenTelemetryLayer`
1018    /// * `extensions` - Mutable reference to the span's extensions containing `OtelData`
1019    /// * `f` - The closure to invoke with a reference to the OTel `Context`
1020    fn get_activated_otel_context(
1021        dispatch: &tracing::Dispatch,
1022        extensions: &mut ExtensionsMut<'_>,
1023        f: &mut dyn FnMut(&OtelContext),
1024    ) {
1025        Self::get_activated_context_extensions(
1026            dispatch,
1027            extensions,
1028            &mut |otel_data: &mut OtelData| {
1029                if let OtelDataState::Context { current_cx } = &otel_data.state {
1030                    f(current_cx)
1031                }
1032            },
1033        );
1034    }
1035
1036    fn get_activated_context_extensions(
1037        dispatch: &tracing::Dispatch,
1038        extensions: &mut ExtensionsMut<'_>,
1039        f: &mut dyn FnMut(&mut OtelData),
1040    ) {
1041        let layer = dispatch
1042            .downcast_ref::<OpenTelemetryLayer<S, T>>()
1043            .expect("layer should downcast to expected type; this is a bug!");
1044
1045        if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1046            // Activate the context
1047            layer.start_cx(otel_data);
1048            f(otel_data);
1049        }
1050    }
1051
1052    fn extra_span_attrs(&self) -> usize {
1053        let mut extra_attrs = 0;
1054        if self.location {
1055            extra_attrs += 3;
1056        }
1057        if self.with_threads {
1058            extra_attrs += 2;
1059        }
1060        if self.with_level {
1061            extra_attrs += 1;
1062        }
1063        if self.with_target {
1064            extra_attrs += 1;
1065        }
1066        extra_attrs
1067    }
1068
1069    ///
1070    /// Builds the OTel span associated with given OTel context, consuming the SpanBuilder within
1071    /// the context in the process.
1072    ///
1073    fn start_cx(&self, otel_data: &mut OtelData) {
1074        if let OtelDataState::Context { .. } = &otel_data.state {
1075            // If the context is already started, we do nothing.
1076        } else if let OtelDataState::Builder {
1077            builder,
1078            parent_cx,
1079            status,
1080        } = take(&mut otel_data.state)
1081        {
1082            let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1083            span.set_status(status);
1084            let current_cx = parent_cx.with_span(span);
1085            otel_data.state = OtelDataState::Context { current_cx };
1086        }
1087    }
1088
1089    fn with_started_cx<U>(&self, otel_data: &mut OtelData, f: &dyn Fn(&OtelContext) -> U) -> U {
1090        self.start_cx(otel_data);
1091        match &otel_data.state {
1092            OtelDataState::Context { current_cx, .. } => f(current_cx),
1093            _ => panic!("OtelDataState should be a Context after starting it; this is a bug!"),
1094        }
1095    }
1096}
1097
1098thread_local! {
1099    static THREAD_ID: u64 = {
1100        // OpenTelemetry's semantic conventions require the thread ID to be
1101        // recorded as an integer, but `std::thread::ThreadId` does not expose
1102        // the integer value on stable, so we have to convert it to a `usize` by
1103        // parsing it. Since this requires allocating a `String`, store it in a
1104        // thread local so we only have to do this once.
1105        // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized
1106        // (https://github.com/rust-lang/rust/issues/67939), just use that.
1107        thread_id_integer(thread::current().id())
1108    };
1109}
1110
1111thread_local! {
1112    static GUARD_STACK: RefCell<IdContextGuardStack> = RefCell::new(IdContextGuardStack::new());
1113}
1114
1115type IdContextGuardStack = IdValueStack<ContextGuard>;
1116
1117impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
1118where
1119    S: Subscriber + for<'span> LookupSpan<'span>,
1120    T: otel::Tracer + 'static,
1121    T::Span: Send + Sync,
1122{
1123    /// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`].
1124    ///
1125    /// [OpenTelemetry `Span`]: opentelemetry::trace::Span
1126    /// [tracing `Span`]: tracing::Span
1127    fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
1128        let span = ctx.span(id).expect("Span not found, this is a bug");
1129        let mut extensions = span.extensions_mut();
1130
1131        if self.tracked_inactivity && extensions.get_mut::<Timings>().is_none() {
1132            extensions.insert(Timings::new());
1133        }
1134
1135        let parent_cx = self.parent_context(attrs, &ctx);
1136        let mut builder = self
1137            .tracer
1138            .span_builder(attrs.metadata().name())
1139            .with_start_time(crate::time::now());
1140
1141        let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
1142            attrs.fields().len() + self.extra_span_attrs(),
1143        ));
1144
1145        if self.location {
1146            let meta = attrs.metadata();
1147
1148            if let Some(filename) = meta.file() {
1149                builder_attrs.push(KeyValue::new("code.file.path", filename));
1150            }
1151
1152            if let Some(module) = meta.module_path() {
1153                builder_attrs.push(KeyValue::new("code.module.name", module));
1154            }
1155
1156            if let Some(line) = meta.line() {
1157                builder_attrs.push(KeyValue::new("code.line.number", line as i64));
1158            }
1159        }
1160
1161        if self.with_threads {
1162            THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", *id as i64)));
1163            if let Some(name) = std::thread::current().name() {
1164                // TODO(eliza): it's a bummer that we have to allocate here, but
1165                // we can't easily get the string as a `static`. it would be
1166                // nice if `opentelemetry` could also take `Arc<str>`s as
1167                // `String` values...
1168                builder_attrs.push(KeyValue::new("thread.name", name.to_string()));
1169            }
1170        }
1171
1172        if self.with_level {
1173            builder_attrs.push(KeyValue::new("level", attrs.metadata().level().as_str()));
1174        }
1175        if self.with_target {
1176            builder_attrs.push(KeyValue::new("target", attrs.metadata().target()));
1177        }
1178
1179        let mut updates = SpanBuilderUpdates::default();
1180        attrs.record(&mut SpanAttributeVisitor {
1181            span_builder_updates: &mut updates,
1182            sem_conv_config: self.sem_conv_config,
1183        });
1184
1185        let mut status = Status::Unset;
1186        updates.update(&mut builder, &mut status);
1187        extensions.insert(OtelData {
1188            state: OtelDataState::Builder {
1189                builder,
1190                parent_cx,
1191                status,
1192            },
1193            end_time: None,
1194        });
1195    }
1196
1197    fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
1198        if !self.context_activation && !self.tracked_inactivity {
1199            return;
1200        }
1201
1202        let span = ctx.span(id).expect("Span not found, this is a bug");
1203        let mut extensions = span.extensions_mut();
1204
1205        if self.context_activation {
1206            if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1207                self.with_started_cx(otel_data, &|cx| {
1208                    let guard = cx.clone().attach();
1209                    GUARD_STACK.with(|stack| stack.borrow_mut().push(id.clone(), guard));
1210                });
1211            }
1212
1213            if !self.tracked_inactivity {
1214                return;
1215            }
1216        }
1217
1218        if let Some(timings) = extensions.get_mut::<Timings>() {
1219            if timings.entered_count == 0 {
1220                let now = Instant::now();
1221                timings.idle += (now - timings.last).as_nanos() as i64;
1222                timings.last = now;
1223            }
1224            timings.entered_count += 1;
1225        }
1226    }
1227
1228    fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
1229        let span = ctx.span(id).expect("Span not found, this is a bug");
1230        let mut extensions = span.extensions_mut();
1231
1232        if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1233            otel_data.end_time = Some(crate::time::now());
1234            if self.context_activation {
1235                GUARD_STACK.with(|stack| stack.borrow_mut().pop(id));
1236            }
1237        }
1238
1239        if !self.tracked_inactivity {
1240            return;
1241        }
1242
1243        if let Some(timings) = extensions.get_mut::<Timings>() {
1244            timings.entered_count -= 1;
1245            if timings.entered_count == 0 {
1246                let now = Instant::now();
1247                timings.busy += (now - timings.last).as_nanos() as i64;
1248                timings.last = now;
1249            }
1250        }
1251    }
1252
1253    /// Record OpenTelemetry [`attributes`] for the given values.
1254    ///
1255    /// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes
1256    fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
1257        let span = ctx.span(id).expect("Span not found, this is a bug");
1258        let mut updates = SpanBuilderUpdates::default();
1259        values.record(&mut SpanAttributeVisitor {
1260            span_builder_updates: &mut updates,
1261            sem_conv_config: self.sem_conv_config,
1262        });
1263        let mut extensions = span.extensions_mut();
1264        if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1265            match &mut otel_data.state {
1266                OtelDataState::Builder {
1267                    builder, status, ..
1268                } => {
1269                    // If the builder is present, then update it.
1270                    updates.update(builder, status);
1271                }
1272                OtelDataState::Context { current_cx, .. } => {
1273                    // If the Context has been created, then update the span.
1274                    updates.update_span(&current_cx.span());
1275                }
1276            }
1277        }
1278    }
1279
1280    fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context<S>) {
1281        let span = ctx.span(id).expect("Span not found, this is a bug");
1282        let mut extensions = span.extensions_mut();
1283        let Some(data) = extensions.get_mut::<OtelData>() else {
1284            return; // The span must already have been closed by us
1285        };
1286
1287        // The follows span may be filtered away (or closed), from this layer,
1288        // in which case we just drop the data, as opposed to panicking. This
1289        // uses the same reasoning as `parent_context` above.
1290        if let Some(follows_span) = ctx.span(follows) {
1291            let mut follows_extensions = follows_span.extensions_mut();
1292            let Some(follows_data) = follows_extensions.get_mut::<OtelData>() else {
1293                return; // The span must already have been closed by us
1294            };
1295            let follows_context =
1296                self.with_started_cx(follows_data, &|cx| cx.span().span_context().clone());
1297            match &mut data.state {
1298                OtelDataState::Builder { builder, .. } => {
1299                    if let Some(ref mut links) = builder.links {
1300                        links.push(otel::Link::with_context(follows_context));
1301                    } else {
1302                        builder.links = Some(vec![otel::Link::with_context(follows_context)]);
1303                    }
1304                }
1305                OtelDataState::Context { current_cx, .. } => {
1306                    current_cx.span().add_link(follows_context, vec![]);
1307                }
1308            }
1309        }
1310    }
1311
1312    /// Records OpenTelemetry [`Event`] data on event.
1313    ///
1314    /// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to
1315    /// [`Error`], signaling that an error has occurred.
1316    ///
1317    /// [`Event`]: opentelemetry::trace::Event
1318    /// [`ERROR`]: tracing::Level::ERROR
1319    /// [`Error`]: opentelemetry::trace::StatusCode::Error
1320    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1321        // Ignore events that are not in the context of a span
1322        if let Some(span) = event.parent().and_then(|id| ctx.span(id)).or_else(|| {
1323            event
1324                .is_contextual()
1325                .then(|| ctx.lookup_current())
1326                .flatten()
1327        }) {
1328            // Performing read operations before getting a write lock to avoid a deadlock
1329            // See https://github.com/tokio-rs/tracing/issues/763
1330            #[cfg(feature = "tracing-log")]
1331            let normalized_meta = event.normalized_metadata();
1332            #[cfg(feature = "tracing-log")]
1333            let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1334            #[cfg(not(feature = "tracing-log"))]
1335            let meta = event.metadata();
1336
1337            let target = Key::new("target");
1338
1339            #[cfg(feature = "tracing-log")]
1340            let target = if normalized_meta.is_some() {
1341                KeyValue::new(target, Value::String(meta.target().to_owned().into()))
1342            } else {
1343                KeyValue::new(target, Value::String(event.metadata().target().into()))
1344            };
1345
1346            #[cfg(not(feature = "tracing-log"))]
1347            let target = KeyValue::new(target, Value::String(meta.target().into()));
1348
1349            let mut otel_event = otel::Event::new(
1350                String::new(),
1351                crate::time::now(),
1352                vec![
1353                    KeyValue::new(
1354                        Key::new("level"),
1355                        Value::String(meta.level().as_str().into()),
1356                    ),
1357                    target,
1358                ],
1359                0,
1360            );
1361
1362            let mut builder_updates = None;
1363            event.record(&mut SpanEventVisitor {
1364                event_builder: &mut otel_event,
1365                span_builder_updates: &mut builder_updates,
1366                sem_conv_config: self.sem_conv_config,
1367            });
1368
1369            // If the event name is still empty, then there was no special handling of error fields.
1370            // It should be safe to set the event name to the name provided by tracing.
1371            // This is a hack but there are existing hacks that depend on the name being empty, so to avoid breaking those the event name is set here.
1372            // Ideally, the name should be set above when the event is constructed.
1373            // see: https://github.com/tokio-rs/tracing-opentelemetry/pull/28
1374            if otel_event.name.is_empty() {
1375                otel_event.name = std::borrow::Cow::Borrowed(event.metadata().name());
1376            }
1377
1378            let mut extensions = span.extensions_mut();
1379
1380            if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1381                if self.location {
1382                    #[cfg(not(feature = "tracing-log"))]
1383                    let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
1384                    let (file, module) = match &normalized_meta {
1385                        Some(meta) => (
1386                            meta.file().map(|s| Value::from(s.to_owned())),
1387                            meta.module_path().map(|s| Value::from(s.to_owned())),
1388                        ),
1389                        None => (
1390                            event.metadata().file().map(Value::from),
1391                            event.metadata().module_path().map(Value::from),
1392                        ),
1393                    };
1394
1395                    if let Some(file) = file {
1396                        otel_event
1397                            .attributes
1398                            .push(KeyValue::new("code.file.path", file));
1399                    }
1400                    if let Some(module) = module {
1401                        otel_event
1402                            .attributes
1403                            .push(KeyValue::new("code.module.name", module));
1404                    }
1405                    if let Some(line) = meta.line() {
1406                        otel_event
1407                            .attributes
1408                            .push(KeyValue::new("code.line.number", line as i64));
1409                    }
1410                }
1411
1412                match &mut otel_data.state {
1413                    OtelDataState::Builder {
1414                        builder, status, ..
1415                    } => {
1416                        if *status == otel::Status::Unset
1417                            && *meta.level() == tracing_core::Level::ERROR
1418                        {
1419                            *status = otel::Status::error("");
1420                        }
1421                        if let Some(builder_updates) = builder_updates {
1422                            builder_updates.update(builder, status);
1423                        }
1424                        if let Some(ref mut events) = builder.events {
1425                            events.push(otel_event);
1426                        } else {
1427                            builder.events = Some(vec![otel_event]);
1428                        }
1429                    }
1430                    OtelDataState::Context { current_cx, .. } => {
1431                        let span = current_cx.span();
1432                        // TODO:ban fix this with accessor in SpanRef that can check the span status
1433                        if *meta.level() == tracing_core::Level::ERROR {
1434                            span.set_status(otel::Status::error(""));
1435                        }
1436                        if let Some(builder_updates) = builder_updates {
1437                            builder_updates.update_span(&span);
1438                        }
1439                        span.add_event(otel_event.name, otel_event.attributes);
1440                    }
1441                }
1442            };
1443        }
1444    }
1445
1446    /// Exports an OpenTelemetry [`Span`] on close.
1447    ///
1448    /// [`Span`]: opentelemetry::trace::Span
1449    fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
1450        let span = ctx.span(&id).expect("Span not found, this is a bug");
1451        // Now get mutable extensions for removal
1452        let (otel_data, timings) = {
1453            let mut extensions = span.extensions_mut();
1454            let timings = if self.tracked_inactivity {
1455                extensions.remove::<Timings>()
1456            } else {
1457                None
1458            };
1459            (extensions.remove::<OtelData>(), timings)
1460        };
1461
1462        if let Some(OtelData { state, end_time }) = otel_data {
1463            // Append busy/idle timings when enabled.
1464            let timings = timings.map(|timings| {
1465                let busy_ns = Key::new("busy_ns");
1466                let idle_ns = Key::new("idle_ns");
1467
1468                vec![
1469                    KeyValue::new(busy_ns, timings.busy),
1470                    KeyValue::new(idle_ns, timings.idle),
1471                ]
1472            });
1473
1474            match state {
1475                OtelDataState::Builder {
1476                    builder,
1477                    parent_cx,
1478                    status,
1479                } => {
1480                    // Don't create the context here just to get a SpanRef since it's costly
1481                    let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1482                    if let Some(timings) = timings {
1483                        span.set_attributes(timings)
1484                    };
1485                    span.set_status(status);
1486                    if let Some(end_time) = end_time {
1487                        span.end_with_timestamp(end_time);
1488                    } else {
1489                        span.end();
1490                    }
1491                }
1492                OtelDataState::Context { current_cx } => {
1493                    let span = current_cx.span();
1494                    if let Some(timings) = timings {
1495                        span.set_attributes(timings)
1496                    };
1497                    end_time
1498                        .map_or_else(|| span.end(), |end_time| span.end_with_timestamp(end_time));
1499                }
1500            }
1501        }
1502    }
1503
1504    // SAFETY: this is safe because the `WithContext` function pointer is valid
1505    // for the lifetime of `&self`.
1506    unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
1507        match id {
1508            id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
1509            id if id == TypeId::of::<WithContext>() => {
1510                Some(&self.with_context as *const _ as *const ())
1511            }
1512            _ => None,
1513        }
1514    }
1515}
1516
1517struct Timings {
1518    idle: i64,
1519    busy: i64,
1520    last: Instant,
1521    entered_count: u64,
1522}
1523
1524impl Timings {
1525    fn new() -> Self {
1526        Self {
1527            idle: 0,
1528            busy: 0,
1529            last: Instant::now(),
1530            entered_count: 0,
1531        }
1532    }
1533}
1534
1535fn thread_id_integer(id: thread::ThreadId) -> u64 {
1536    let thread_id = format!("{id:?}");
1537    thread_id
1538        .trim_start_matches("ThreadId(")
1539        .trim_end_matches(')')
1540        .parse::<u64>()
1541        .expect("thread ID should parse as an integer")
1542}
1543
1544#[cfg(test)]
1545mod tests {
1546    use crate::OpenTelemetrySpanExt;
1547
1548    use super::*;
1549    use opentelemetry::trace::{SpanContext, TraceFlags, TracerProvider};
1550    use opentelemetry_sdk::trace::SpanExporter;
1551    use std::{collections::HashMap, error::Error, fmt::Display, time::SystemTime};
1552    use tracing::trace_span;
1553    use tracing_core::LevelFilter;
1554    use tracing_subscriber::prelude::*;
1555
1556    #[derive(Debug, Clone)]
1557    struct TestTracer {
1558        tracer: opentelemetry_sdk::trace::Tracer,
1559        exporter: opentelemetry_sdk::trace::InMemorySpanExporter,
1560    }
1561
1562    impl TestTracer {
1563        fn spans(&mut self) -> Vec<opentelemetry_sdk::trace::SpanData> {
1564            self.exporter
1565                .force_flush()
1566                .expect("problems flushing spans");
1567            self.exporter
1568                .get_finished_spans()
1569                .expect("problems recording spans")
1570        }
1571
1572        fn with_data<T>(&mut self, f: impl FnOnce(&opentelemetry_sdk::trace::SpanData) -> T) -> T {
1573            let spans = self.spans();
1574            f(spans.first().expect("no spans recorded"))
1575        }
1576
1577        fn attributes(&mut self) -> HashMap<String, Value> {
1578            self.with_data(|data| {
1579                data.attributes
1580                    .iter()
1581                    .map(|kv| (kv.key.to_string(), kv.value.clone()))
1582                    .collect()
1583            })
1584        }
1585    }
1586
1587    impl Default for TestTracer {
1588        fn default() -> Self {
1589            let exporter = opentelemetry_sdk::trace::InMemorySpanExporter::default();
1590            let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
1591                .with_simple_exporter(exporter.clone())
1592                .build();
1593            let tracer = provider.tracer("test-tracer");
1594            Self { tracer, exporter }
1595        }
1596    }
1597
1598    impl opentelemetry::trace::Tracer for TestTracer {
1599        type Span = opentelemetry_sdk::trace::Span;
1600
1601        fn build_with_context(&self, builder: SpanBuilder, parent_cx: &OtelContext) -> Self::Span {
1602            self.tracer.build_with_context(builder, parent_cx)
1603        }
1604    }
1605
1606    #[derive(Debug, Clone)]
1607    struct TestSpan(otel::SpanContext);
1608    impl otel::Span for TestSpan {
1609        fn add_event_with_timestamp<T: Into<Cow<'static, str>>>(
1610            &mut self,
1611            _: T,
1612            _: SystemTime,
1613            _: Vec<KeyValue>,
1614        ) {
1615        }
1616        fn span_context(&self) -> &otel::SpanContext {
1617            &self.0
1618        }
1619        fn is_recording(&self) -> bool {
1620            false
1621        }
1622        fn set_attribute(&mut self, _attribute: KeyValue) {}
1623        fn set_status(&mut self, _status: otel::Status) {}
1624        fn update_name<T: Into<Cow<'static, str>>>(&mut self, _new_name: T) {}
1625        fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec<KeyValue>) {}
1626        fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
1627    }
1628
1629    #[derive(Debug)]
1630    struct TestDynError {
1631        msg: &'static str,
1632        source: Option<Box<TestDynError>>,
1633    }
1634    impl Display for TestDynError {
1635        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1636            write!(f, "{}", self.msg)
1637        }
1638    }
1639    impl Error for TestDynError {
1640        fn source(&self) -> Option<&(dyn Error + 'static)> {
1641            match &self.source {
1642                Some(source) => Some(source),
1643                None => None,
1644            }
1645        }
1646    }
1647    impl TestDynError {
1648        fn new(msg: &'static str) -> Self {
1649            Self { msg, source: None }
1650        }
1651        fn with_parent(self, parent_msg: &'static str) -> Self {
1652            Self {
1653                msg: parent_msg,
1654                source: Some(Box::new(self)),
1655            }
1656        }
1657    }
1658
1659    #[test]
1660    fn dynamic_span_names() {
1661        let dynamic_name = "GET http://example.com".to_string();
1662        let mut tracer = TestTracer::default();
1663        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1664
1665        tracing::subscriber::with_default(subscriber, || {
1666            tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
1667        });
1668
1669        let recorded_name = tracer.spans().first().unwrap().name.clone();
1670        assert_eq!(recorded_name, dynamic_name.as_str())
1671    }
1672
1673    #[test]
1674    fn span_kind() {
1675        let mut tracer = TestTracer::default();
1676        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1677
1678        tracing::subscriber::with_default(subscriber, || {
1679            tracing::debug_span!("request", otel.kind = "server");
1680        });
1681
1682        let recorded_kind = tracer.with_data(|data| data.span_kind.clone());
1683        assert_eq!(recorded_kind, otel::SpanKind::Server)
1684    }
1685
1686    #[test]
1687    fn span_status_code() {
1688        let mut tracer = TestTracer::default();
1689        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1690
1691        tracing::subscriber::with_default(subscriber, || {
1692            tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
1693        });
1694
1695        let recorded_status = tracer.with_data(|data| data.status.clone());
1696        assert_eq!(recorded_status, otel::Status::Ok)
1697    }
1698
1699    #[test]
1700    fn span_status_description() {
1701        let mut tracer = TestTracer::default();
1702        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1703
1704        let message = "message";
1705
1706        tracing::subscriber::with_default(subscriber, || {
1707            tracing::debug_span!("request", otel.status_description = message);
1708        });
1709
1710        let recorded_status_message = tracer.with_data(|data| data.status.clone());
1711
1712        assert_eq!(recorded_status_message, otel::Status::error(message))
1713    }
1714
1715    #[test]
1716    fn trace_id_from_existing_context_with_context_activation() {
1717        trace_id_from_existing_context_impl(true);
1718    }
1719
1720    #[test]
1721    fn trace_id_from_existing_context_no_context_activation() {
1722        trace_id_from_existing_context_impl(false);
1723    }
1724
1725    fn trace_id_from_existing_context_impl(context_activation: bool) {
1726        let mut tracer = TestTracer::default();
1727        let subscriber = tracing_subscriber::registry().with(
1728            layer()
1729                .with_tracer(tracer.clone())
1730                .with_context_activation(context_activation),
1731        );
1732        let trace_id = otel::TraceId::from(42u128);
1733        let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
1734            trace_id,
1735            otel::SpanId::from(1u64),
1736            TraceFlags::SAMPLED,
1737            false,
1738            Default::default(),
1739        )));
1740        let _g = existing_cx.attach();
1741
1742        tracing::subscriber::with_default(subscriber, || {
1743            tracing::debug_span!("request", otel.kind = "server");
1744        });
1745
1746        let recorded_trace_id = tracer.with_data(|data| data.span_context.trace_id());
1747        assert_eq!(recorded_trace_id, trace_id)
1748    }
1749
1750    #[test]
1751    fn includes_timings_with_context_activation() {
1752        includes_timings_impl(true);
1753    }
1754
1755    #[test]
1756    fn includes_timings_no_context_activation() {
1757        includes_timings_impl(false);
1758    }
1759
1760    fn includes_timings_impl(context_activation: bool) {
1761        let mut tracer = TestTracer::default();
1762        let subscriber = tracing_subscriber::registry().with(
1763            layer()
1764                .with_tracer(tracer.clone())
1765                .with_tracked_inactivity(true)
1766                .with_context_activation(context_activation),
1767        );
1768
1769        tracing::subscriber::with_default(subscriber, || {
1770            tracing::debug_span!("request");
1771        });
1772
1773        let attributes = tracer.attributes();
1774
1775        assert!(attributes.contains_key("idle_ns"));
1776        assert!(attributes.contains_key("busy_ns"));
1777    }
1778
1779    #[test]
1780    fn records_error_fields_with_context_activation() {
1781        records_error_fields_impl(true);
1782    }
1783
1784    #[test]
1785    fn records_error_fields_no_context_activation() {
1786        records_error_fields_impl(false);
1787    }
1788
1789    fn records_error_fields_impl(context_activation: bool) {
1790        let mut tracer = TestTracer::default();
1791        let subscriber = tracing_subscriber::registry().with(
1792            layer()
1793                .with_tracer(tracer.clone())
1794                .with_context_activation(context_activation),
1795        );
1796
1797        let err = TestDynError::new("base error")
1798            .with_parent("intermediate error")
1799            .with_parent("user error");
1800
1801        tracing::subscriber::with_default(subscriber, || {
1802            tracing::debug_span!(
1803                "request",
1804                error = &err as &(dyn std::error::Error + 'static)
1805            );
1806        });
1807
1808        let attributes = tracer.attributes();
1809
1810        assert_eq!(attributes["error"].as_str(), "user error");
1811        assert_eq!(
1812            attributes["error.chain"],
1813            Value::Array(
1814                vec![
1815                    StringValue::from("intermediate error"),
1816                    StringValue::from("base error")
1817                ]
1818                .into()
1819            )
1820        );
1821
1822        assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1823        assert_eq!(
1824            attributes[FIELD_EXCEPTION_STACKTRACE],
1825            Value::Array(
1826                vec![
1827                    StringValue::from("intermediate error"),
1828                    StringValue::from("base error")
1829                ]
1830                .into()
1831            )
1832        );
1833    }
1834
1835    #[test]
1836    fn records_event_name_with_context_activation() {
1837        records_event_name_impl(true);
1838    }
1839
1840    #[test]
1841    fn records_event_name_no_context_activation() {
1842        records_event_name_impl(false);
1843    }
1844
1845    fn records_event_name_impl(context_activation: bool) {
1846        let mut tracer = TestTracer::default();
1847        let subscriber = tracing_subscriber::registry().with(
1848            layer()
1849                .with_tracer(tracer.clone())
1850                .with_context_activation(context_activation),
1851        );
1852
1853        tracing::subscriber::with_default(subscriber, || {
1854            tracing::debug_span!("test span").in_scope(|| {
1855                tracing::event!(tracing::Level::INFO, "event name 1"); // this is equivalent to 'message = "event name 1"'
1856                tracing::event!(name: "event name 2", tracing::Level::INFO, field1 = "field1");
1857                tracing::event!(name: "event name 3", tracing::Level::INFO, error = "field2");
1858                tracing::event!(name: "event name 4", tracing::Level::INFO, message = "field3");
1859                tracing::event!(name: "event name 5", tracing::Level::INFO, name = "field4");
1860            });
1861        });
1862
1863        let events = tracer.with_data(|data| data.events.clone());
1864
1865        let mut iter = events.iter();
1866
1867        assert_eq!(iter.next().unwrap().name, "event name 1");
1868        assert_eq!(iter.next().unwrap().name, "event name 2");
1869        assert_eq!(iter.next().unwrap().name, "exception"); // error attribute is handled specially
1870        assert_eq!(iter.next().unwrap().name, "field3"); // message attribute is handled specially
1871        assert_eq!(iter.next().unwrap().name, "event name 5"); // name attribute should not conflict with event name.
1872    }
1873
1874    #[test]
1875    fn event_filter_count() {
1876        let mut tracer = TestTracer::default();
1877        let subscriber = tracing_subscriber::registry().with(
1878            layer()
1879                .with_tracer(tracer.clone())
1880                .with_counting_event_filter(LevelFilter::INFO)
1881                .with_filter(LevelFilter::DEBUG),
1882        );
1883
1884        tracing::subscriber::with_default(subscriber, || {
1885            tracing::debug_span!("test span").in_scope(|| {
1886                tracing::event!(tracing::Level::TRACE, "1");
1887                tracing::event!(tracing::Level::DEBUG, "2");
1888                tracing::event!(tracing::Level::INFO, "3");
1889                tracing::event!(tracing::Level::WARN, "4");
1890                tracing::event!(tracing::Level::ERROR, "5");
1891            });
1892        });
1893
1894        let events = tracer.with_data(|data| data.events.clone());
1895
1896        let mut iter = events.iter();
1897
1898        assert_eq!(iter.next().unwrap().name, "3");
1899        assert_eq!(iter.next().unwrap().name, "4");
1900        assert_eq!(iter.next().unwrap().name, "5");
1901        assert!(iter.next().is_none());
1902
1903        let spans = tracer.spans();
1904        assert_eq!(spans.len(), 1);
1905
1906        let Value::I64(event_count) = spans
1907            .first()
1908            .unwrap()
1909            .attributes
1910            .iter()
1911            .find(|key_value| key_value.key.as_str() == SPAN_EVENT_COUNT_FIELD)
1912            .unwrap()
1913            .value
1914        else {
1915            panic!("Unexpected type of `{SPAN_EVENT_COUNT_FIELD}`.");
1916        };
1917        // We've sent 5 events out of which 1 was filtered out by an actual filter and another by
1918        // our event filter.
1919        assert_eq!(event_count, 4);
1920    }
1921
1922    #[test]
1923    fn records_no_error_fields_with_context_activation() {
1924        records_no_error_fields_impl(true);
1925    }
1926
1927    #[test]
1928    fn records_no_error_fields_no_context_activation() {
1929        records_no_error_fields_impl(false);
1930    }
1931
1932    fn records_no_error_fields_impl(context_activation: bool) {
1933        let mut tracer = TestTracer::default();
1934        let subscriber = tracing_subscriber::registry().with(
1935            layer()
1936                .with_error_records_to_exceptions(false)
1937                .with_tracer(tracer.clone())
1938                .with_context_activation(context_activation),
1939        );
1940
1941        let err = TestDynError::new("base error")
1942            .with_parent("intermediate error")
1943            .with_parent("user error");
1944
1945        tracing::subscriber::with_default(subscriber, || {
1946            tracing::debug_span!(
1947                "request",
1948                error = &err as &(dyn std::error::Error + 'static)
1949            );
1950        });
1951
1952        let attributes = tracer.attributes();
1953
1954        assert_eq!(attributes["error"].as_str(), "user error");
1955        assert_eq!(
1956            attributes["error.chain"],
1957            Value::Array(
1958                vec![
1959                    StringValue::from("intermediate error"),
1960                    StringValue::from("base error")
1961                ]
1962                .into()
1963            )
1964        );
1965
1966        assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1967        assert_eq!(
1968            attributes[FIELD_EXCEPTION_STACKTRACE],
1969            Value::Array(
1970                vec![
1971                    StringValue::from("intermediate error"),
1972                    StringValue::from("base error")
1973                ]
1974                .into()
1975            )
1976        );
1977    }
1978
1979    #[test]
1980    fn includes_span_location_with_context_activation() {
1981        includes_span_location_impl(true);
1982    }
1983
1984    #[test]
1985    fn includes_span_location_no_context_activation() {
1986        includes_span_location_impl(false);
1987    }
1988
1989    fn includes_span_location_impl(context_activation: bool) {
1990        let mut tracer = TestTracer::default();
1991        let subscriber = tracing_subscriber::registry().with(
1992            layer()
1993                .with_tracer(tracer.clone())
1994                .with_location(true)
1995                .with_context_activation(context_activation),
1996        );
1997
1998        tracing::subscriber::with_default(subscriber, || {
1999            tracing::debug_span!("request");
2000        });
2001
2002        let attributes = tracer.attributes();
2003
2004        assert!(attributes.contains_key("code.file.path"));
2005        assert!(attributes.contains_key("code.module.name"));
2006        assert!(attributes.contains_key("code.line.number"));
2007    }
2008
2009    #[test]
2010    fn excludes_span_location_with_context_activation() {
2011        excludes_span_location_impl(true);
2012    }
2013
2014    #[test]
2015    fn excludes_span_location_no_context_activation() {
2016        excludes_span_location_impl(false);
2017    }
2018
2019    fn excludes_span_location_impl(context_activation: bool) {
2020        let mut tracer = TestTracer::default();
2021        let subscriber = tracing_subscriber::registry().with(
2022            layer()
2023                .with_tracer(tracer.clone())
2024                .with_location(false)
2025                .with_context_activation(context_activation),
2026        );
2027
2028        tracing::subscriber::with_default(subscriber, || {
2029            tracing::debug_span!("request");
2030        });
2031
2032        let attributes = tracer.attributes();
2033
2034        assert!(!attributes.contains_key("code.file.path"));
2035        assert!(!attributes.contains_key("code.module.name"));
2036        assert!(!attributes.contains_key("code.line.number"));
2037    }
2038
2039    #[test]
2040    fn includes_thread_with_context_activation() {
2041        includes_thread_impl(true);
2042    }
2043
2044    #[test]
2045    fn includes_thread_no_context_activation() {
2046        includes_thread_impl(false);
2047    }
2048
2049    fn includes_thread_impl(context_activation: bool) {
2050        let thread = thread::current();
2051        let expected_name = thread
2052            .name()
2053            .map(|name| Value::String(name.to_owned().into()));
2054        let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
2055
2056        let mut tracer = TestTracer::default();
2057        let subscriber = tracing_subscriber::registry().with(
2058            layer()
2059                .with_tracer(tracer.clone())
2060                .with_threads(true)
2061                .with_context_activation(context_activation),
2062        );
2063
2064        tracing::subscriber::with_default(subscriber, || {
2065            tracing::debug_span!("request");
2066        });
2067
2068        let attributes = tracer.attributes();
2069
2070        assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
2071        assert_eq!(attributes.get("thread.id"), Some(&expected_id));
2072    }
2073
2074    #[test]
2075    fn excludes_thread_with_context_activation() {
2076        excludes_thread_impl(true);
2077    }
2078
2079    #[test]
2080    fn excludes_thread_no_context_activation() {
2081        excludes_thread_impl(false);
2082    }
2083
2084    fn excludes_thread_impl(context_activation: bool) {
2085        let mut tracer = TestTracer::default();
2086        let subscriber = tracing_subscriber::registry().with(
2087            layer()
2088                .with_tracer(tracer.clone())
2089                .with_threads(false)
2090                .with_context_activation(context_activation),
2091        );
2092
2093        tracing::subscriber::with_default(subscriber, || {
2094            tracing::debug_span!("request");
2095        });
2096
2097        let attributes = tracer.attributes();
2098
2099        assert!(!attributes.contains_key("thread.name"));
2100        assert!(!attributes.contains_key("thread.id"));
2101    }
2102
2103    #[test]
2104    fn includes_level_with_context_activation() {
2105        includes_level_impl(true);
2106    }
2107
2108    #[test]
2109    fn includes_level_no_context_activation() {
2110        includes_level_impl(false);
2111    }
2112
2113    fn includes_level_impl(context_activation: bool) {
2114        let mut tracer = TestTracer::default();
2115        let subscriber = tracing_subscriber::registry().with(
2116            layer()
2117                .with_tracer(tracer.clone())
2118                .with_level(true)
2119                .with_context_activation(context_activation),
2120        );
2121
2122        tracing::subscriber::with_default(subscriber, || {
2123            tracing::debug_span!("request");
2124        });
2125
2126        let attributes = tracer.attributes();
2127
2128        assert!(attributes.contains_key("level"));
2129    }
2130
2131    #[test]
2132    fn excludes_level_with_context_activation() {
2133        excludes_level_impl(true);
2134    }
2135
2136    #[test]
2137    fn excludes_level_no_context_activation() {
2138        excludes_level_impl(false);
2139    }
2140
2141    fn excludes_level_impl(context_activation: bool) {
2142        let mut tracer = TestTracer::default();
2143        let subscriber = tracing_subscriber::registry().with(
2144            layer()
2145                .with_tracer(tracer.clone())
2146                .with_level(false)
2147                .with_context_activation(context_activation),
2148        );
2149
2150        tracing::subscriber::with_default(subscriber, || {
2151            tracing::debug_span!("request");
2152        });
2153
2154        let attributes = tracer.attributes();
2155
2156        assert!(!attributes.contains_key("level"));
2157    }
2158
2159    #[test]
2160    fn includes_target() {
2161        let mut tracer = TestTracer::default();
2162        let subscriber = tracing_subscriber::registry()
2163            .with(layer().with_tracer(tracer.clone()).with_target(true));
2164
2165        tracing::subscriber::with_default(subscriber, || {
2166            tracing::debug_span!("request");
2167        });
2168
2169        let attributes = tracer.attributes();
2170        let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2171        assert!(keys.contains(&"target"));
2172    }
2173
2174    #[test]
2175    fn excludes_target() {
2176        let mut tracer = TestTracer::default();
2177        let subscriber = tracing_subscriber::registry()
2178            .with(layer().with_tracer(tracer.clone()).with_target(false));
2179
2180        tracing::subscriber::with_default(subscriber, || {
2181            tracing::debug_span!("request");
2182        });
2183
2184        let attributes = tracer.attributes();
2185        let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2186        assert!(!keys.contains(&"target"));
2187    }
2188
2189    #[test]
2190    fn propagates_error_fields_from_event_to_span_with_context_activation() {
2191        propagates_error_fields_from_event_to_span_impl(true);
2192    }
2193
2194    #[test]
2195    fn propagates_error_fields_from_event_to_span_no_context_activation() {
2196        propagates_error_fields_from_event_to_span_impl(false);
2197    }
2198
2199    fn propagates_error_fields_from_event_to_span_impl(context_activation: bool) {
2200        let mut tracer = TestTracer::default();
2201        let subscriber = tracing_subscriber::registry().with(
2202            layer()
2203                .with_tracer(tracer.clone())
2204                .with_context_activation(context_activation),
2205        );
2206
2207        let err = TestDynError::new("base error")
2208            .with_parent("intermediate error")
2209            .with_parent("user error");
2210
2211        tracing::subscriber::with_default(subscriber, || {
2212            let _guard = tracing::debug_span!("request",).entered();
2213
2214            tracing::error!(
2215                error = &err as &(dyn std::error::Error + 'static),
2216                "request error!"
2217            )
2218        });
2219
2220        let attributes = tracer.attributes();
2221
2222        assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2223        assert_eq!(
2224            attributes[FIELD_EXCEPTION_STACKTRACE],
2225            Value::Array(
2226                vec![
2227                    StringValue::from("intermediate error"),
2228                    StringValue::from("base error")
2229                ]
2230                .into()
2231            )
2232        );
2233    }
2234
2235    #[test]
2236    fn propagates_no_error_fields_from_event_to_span_with_context_activation() {
2237        propagates_no_error_fields_from_event_to_span_impl(true);
2238    }
2239
2240    #[test]
2241    fn propagates_no_error_fields_from_event_to_span_no_context_activation() {
2242        propagates_no_error_fields_from_event_to_span_impl(false);
2243    }
2244
2245    fn propagates_no_error_fields_from_event_to_span_impl(context_activation: bool) {
2246        let mut tracer = TestTracer::default();
2247        let subscriber = tracing_subscriber::registry().with(
2248            layer()
2249                .with_error_fields_to_exceptions(false)
2250                .with_tracer(tracer.clone())
2251                .with_context_activation(context_activation),
2252        );
2253
2254        let err = TestDynError::new("base error")
2255            .with_parent("intermediate error")
2256            .with_parent("user error");
2257
2258        tracing::subscriber::with_default(subscriber, || {
2259            let _guard = tracing::debug_span!("request",).entered();
2260
2261            tracing::error!(
2262                error = &err as &(dyn std::error::Error + 'static),
2263                "request error!"
2264            )
2265        });
2266
2267        let attributes = tracer.attributes();
2268
2269        assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2270        assert_eq!(
2271            attributes[FIELD_EXCEPTION_STACKTRACE],
2272            Value::Array(
2273                vec![
2274                    StringValue::from("intermediate error"),
2275                    StringValue::from("base error")
2276                ]
2277                .into()
2278            )
2279        );
2280    }
2281
2282    #[test]
2283    fn tracing_error_compatibility_with_context_activation() {
2284        tracing_error_compatibility_impl(true);
2285    }
2286
2287    #[test]
2288    fn tracing_error_compatibility_no_context_activation() {
2289        tracing_error_compatibility_impl(false);
2290    }
2291
2292    fn tracing_error_compatibility_impl(context_activation: bool) {
2293        let tracer = TestTracer::default();
2294        let subscriber = tracing_subscriber::registry()
2295            .with(
2296                layer()
2297                    .with_error_fields_to_exceptions(false)
2298                    .with_tracer(tracer.clone())
2299                    .with_context_activation(context_activation),
2300            )
2301            .with(tracing_error::ErrorLayer::default());
2302
2303        tracing::subscriber::with_default(subscriber, || {
2304            let span = tracing::info_span!("Blows up!", exception = tracing::field::Empty);
2305            let _entered = span.enter();
2306            let context = tracing_error::SpanTrace::capture();
2307
2308            // This can cause a deadlock if `on_record` locks extensions while attributes are visited
2309            span.record("exception", tracing::field::debug(&context));
2310            // This can cause a deadlock if `on_event` locks extensions while the event is visited
2311            tracing::info!(exception = &tracing::field::debug(&context), "hello");
2312        });
2313
2314        // No need to assert anything, as long as this finished (and did not panic), everything is ok.
2315    }
2316
2317    #[derive(Debug, PartialEq)]
2318    struct ValueA(&'static str);
2319    #[derive(Debug, PartialEq)]
2320    struct ValueB(&'static str);
2321
2322    #[test]
2323    fn otel_context_propagation() {
2324        use opentelemetry::trace::Tracer;
2325        use tracing::span;
2326
2327        let mut tracer = TestTracer::default();
2328        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2329
2330        tracing::subscriber::with_default(subscriber, || {
2331            // Add a value to the current OpenTelemetry context for the bridge to propagate
2332            let _outer_guard =
2333                OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2334            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2335            let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2336            // Drop the guard to ensure the context is cleared
2337            drop(_outer_guard);
2338            assert!(OtelContext::current().get::<ValueA>().is_none());
2339            // Enter the root span, the context should be propagated
2340            let _enter_root = root.enter();
2341            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2342            // Add another value to the current OpenTelemetry context for the bridge to propagate
2343            let _inner_guard =
2344                OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2345            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2346            assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2347            let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2348            // Drop the guard to ensure the context is reverted
2349            drop(_inner_guard);
2350            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2351            assert!(OtelContext::current().get::<ValueB>().is_none());
2352            // Enter the child span, the context should be propagated
2353            let _enter_child = child.enter();
2354            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2355            assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2356            // Create an OpenTelemetry span using the OpentTelemetry notion of current
2357            // span to see check that it is a child of the tokio child span
2358            let span = tracer
2359                .tracer
2360                .span_builder("otel-tracing-span")
2361                .start(&tracer);
2362            let _otel_guard = OtelContext::attach(OtelContext::current_with_span(span));
2363            let child2 = span!(tracing::Level::TRACE, "tokio-tracing-span-child2");
2364            drop(_otel_guard);
2365            // Drop the child span, the context should be reverted
2366            drop(_enter_child);
2367            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2368            assert!(OtelContext::current().get::<ValueB>().is_none());
2369            // Drop the root span, the context should be reverted
2370            drop(_enter_root);
2371            assert!(OtelContext::current().get::<ValueA>().is_none());
2372            assert!(OtelContext::current().get::<ValueB>().is_none());
2373            let _ = child2.enter();
2374        });
2375
2376        // Let's check the spans
2377        let spans = tracer.spans();
2378        let parent = spans
2379            .iter()
2380            .find(|span| span.name == "tokio-tracing-span-parent")
2381            .unwrap();
2382        let child = spans
2383            .iter()
2384            .find(|span| span.name == "tokio-tracing-span-child")
2385            .unwrap();
2386        let child2 = spans
2387            .iter()
2388            .find(|span| span.name == "tokio-tracing-span-child2")
2389            .unwrap();
2390        let otel = spans
2391            .iter()
2392            .find(|span| span.name == "otel-tracing-span")
2393            .unwrap();
2394        // The tokio parent span should be a root span
2395        assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2396        // The first tokio child span should have the tokio parent span as parent
2397        assert_eq!(child.parent_span_id, parent.span_context.span_id());
2398        // The otel span should have the first tokio child span as parent
2399        assert_eq!(otel.parent_span_id, child.span_context.span_id());
2400        // The second tokio child span should have the otel span as parent
2401        assert_eq!(child2.parent_span_id, otel.span_context.span_id());
2402    }
2403
2404    #[test]
2405    fn parent_context() {
2406        let mut tracer = TestTracer::default();
2407        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2408
2409        tracing::subscriber::with_default(subscriber, || {
2410            let root = trace_span!("root");
2411
2412            let child1 = trace_span!("child-1");
2413            let root_context = root.context(); // SpanData (None)
2414            let _ = child1.set_parent(root_context); // Clone context, but SpanData(None)
2415
2416            let _enter_root = root.enter();
2417            drop(_enter_root);
2418
2419            let child2 = trace_span!("child-2");
2420            let _ = child2.set_parent(root.context());
2421        });
2422
2423        // Let's check the spans
2424        let spans = tracer.spans();
2425        let parent = spans.iter().find(|span| span.name == "root").unwrap();
2426        let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2427        let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2428        assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2429        assert_eq!(child1.parent_span_id, parent.span_context.span_id());
2430        assert_eq!(child2.parent_span_id, parent.span_context.span_id());
2431    }
2432
2433    #[test]
2434    fn record_after_with_context_activation() {
2435        record_after_impl(true);
2436    }
2437
2438    #[test]
2439    fn record_after_no_context_activation() {
2440        record_after_impl(false);
2441    }
2442
2443    fn record_after_impl(context_activation: bool) {
2444        let mut tracer = TestTracer::default();
2445        let subscriber = tracing_subscriber::registry().with(
2446            layer()
2447                .with_tracer(tracer.clone())
2448                .with_context_activation(context_activation),
2449        );
2450
2451        tracing::subscriber::with_default(subscriber, || {
2452            let root = trace_span!("root", before = "before", after = "before");
2453
2454            // Record a value before the span is entered
2455            root.record("before", "after");
2456
2457            // Enter and exit the span
2458            let _enter_root = root.enter();
2459            drop(_enter_root);
2460
2461            // Record a value after the span is exited
2462            root.record("after", "after");
2463        });
2464
2465        // Let's check the spans. Both values should've been
2466        // updated to 'after'.
2467        let spans = tracer.spans();
2468        let parent = spans.iter().find(|span| span.name == "root").unwrap();
2469        assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2470        assert!(parent
2471            .attributes
2472            .iter()
2473            .filter(|kv| kv.key.as_str() == "before")
2474            .any(|kv| kv.value.as_str() == "after"));
2475
2476        assert!(parent
2477            .attributes
2478            .iter()
2479            .filter(|kv| kv.key.as_str() == "after")
2480            .any(|kv| kv.value.as_str() == "after"));
2481    }
2482
2483    #[test]
2484    fn parent_context_2() {
2485        let mut tracer = TestTracer::default();
2486        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2487
2488        tracing::subscriber::with_default(subscriber, || {
2489            let root = trace_span!("root");
2490            _ = root.enter();
2491
2492            let child1 = trace_span!("child-1");
2493            let _ = child1.set_parent(root.context());
2494
2495            trace_span!(parent: &child1, "child-2");
2496            let _ = child1.set_parent(root.context()); // <-- this is what causes the issue
2497
2498            trace_span!(parent: &child1, "child-3");
2499        });
2500
2501        // Let's check the spans
2502        let spans = tracer.spans();
2503        let root = spans.iter().find(|span| span.name == "root").unwrap();
2504        let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2505        let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2506        let child3 = spans.iter().find(|span| span.name == "child-3").unwrap();
2507        assert_eq!(root.parent_span_id, otel::SpanId::INVALID);
2508        assert_eq!(child1.parent_span_id, root.span_context.span_id());
2509        assert_eq!(child2.parent_span_id, child1.span_context.span_id());
2510
2511        // The parent should be `child1`
2512        assert_eq!(child3.parent_span_id, child1.span_context.span_id());
2513    }
2514
2515    #[test]
2516    fn follows_from_adds_link_with_context_activation() {
2517        follows_from_adds_link_impl(true);
2518    }
2519
2520    #[test]
2521    fn follows_from_adds_link_no_context_activation() {
2522        follows_from_adds_link_impl(false);
2523    }
2524
2525    fn follows_from_adds_link_impl(context_activation: bool) {
2526        use crate::OpenTelemetrySpanExt;
2527        let mut tracer = TestTracer::default();
2528        let subscriber = tracing_subscriber::registry().with(
2529            layer()
2530                .with_tracer(tracer.clone())
2531                .with_context_activation(context_activation),
2532        );
2533
2534        let span1_id = tracing::subscriber::with_default(subscriber, || {
2535            let span2 = tracing::debug_span!("span2");
2536            let span1 = tracing::debug_span!("span1");
2537
2538            // Ensure that span2 is started
2539            let _ = span2.context();
2540
2541            // Establish follows_from relationship
2542            span2.follows_from(&span1);
2543
2544            // Enter span2 to ensure it's exported
2545            let _guard = span2.enter();
2546
2547            // Get span ID for later verification
2548            span1.context().span().span_context().span_id()
2549        });
2550
2551        let spans = tracer.spans();
2552        // Check that both spans are exported
2553        assert_eq!(spans.len(), 2, "Expected two spans to be exported");
2554        assert!(spans.iter().any(|span| span.name == "span1"));
2555        let span2 = spans
2556            .iter()
2557            .find(|span| span.name == "span2")
2558            .expect("Expected span2 to be exported");
2559
2560        // Collect span2 links
2561        let links = span2
2562            .links
2563            .iter()
2564            .map(|link| link.span_context.span_id())
2565            .collect::<Vec<_>>();
2566
2567        // Verify that span2 has a link to span1
2568        assert_eq!(
2569            links.len(),
2570            1,
2571            "Expected span to have one link from follows_from relationship"
2572        );
2573
2574        assert!(
2575            links.contains(&span1_id),
2576            "Link should point to the correct source span"
2577        );
2578    }
2579
2580    #[test]
2581    fn follows_from_multiple_links_with_context_activation() {
2582        follows_from_multiple_links_impl(true);
2583    }
2584
2585    #[test]
2586    fn follows_from_multiple_links_no_context_activation() {
2587        follows_from_multiple_links_impl(false);
2588    }
2589
2590    fn follows_from_multiple_links_impl(context_activation: bool) {
2591        use crate::OpenTelemetrySpanExt;
2592        let mut tracer = TestTracer::default();
2593        let subscriber = tracing_subscriber::registry().with(
2594            layer()
2595                .with_tracer(tracer.clone())
2596                .with_context_activation(context_activation),
2597        );
2598
2599        let (span1_id, span2_id) = tracing::subscriber::with_default(subscriber, || {
2600            let span3 = tracing::debug_span!("span3");
2601            let span2 = tracing::debug_span!("span2");
2602            let span1 = tracing::debug_span!("span1");
2603
2604            // Establish multiple follows_from relationships
2605            span3.follows_from(&span1);
2606            span3.follows_from(&span2);
2607
2608            // Enter span3 to ensure it's exported
2609            let _guard = span3.enter();
2610
2611            // Get span IDs for later verification
2612            (
2613                span1.context().span().span_context().span_id(),
2614                span2.context().span().span_context().span_id(),
2615            )
2616        });
2617
2618        let spans = tracer.spans();
2619        // Check that all three spans are exported
2620        assert_eq!(spans.len(), 3, "Expected three spans to be exported");
2621        assert!(spans.iter().any(|span| span.name == "span1"));
2622        assert!(spans.iter().any(|span| span.name == "span2"));
2623        let span3 = spans
2624            .iter()
2625            .find(|span| span.name == "span3")
2626            .expect("Expected span3 to be exported");
2627
2628        // Collect span3 links
2629        let links = span3
2630            .links
2631            .iter()
2632            .map(|link| link.span_context.span_id())
2633            .collect::<Vec<_>>();
2634
2635        // Verify that span3 has multiple links and they point to the correct spans
2636        assert_eq!(
2637            links.len(),
2638            2,
2639            "Expected span to have two links from follows_from relationships"
2640        );
2641
2642        // Verify that the links point to the correct spans in the correct order
2643        assert!(
2644            links[0] == span1_id && links[1] == span2_id,
2645            "Links should point to the correct source spans"
2646        );
2647    }
2648
2649    #[test]
2650    fn context_activation_disabled() {
2651        use tracing::span;
2652
2653        let mut tracer = TestTracer::default();
2654        let subscriber = tracing_subscriber::registry().with(
2655            layer()
2656                .with_tracer(tracer.clone())
2657                .with_context_activation(false),
2658        );
2659
2660        tracing::subscriber::with_default(subscriber, || {
2661            // Add a value to the current OpenTelemetry context
2662            let _outer_guard =
2663                OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2664            assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2665
2666            let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2667
2668            // Drop the guard to ensure the context is cleared
2669            drop(_outer_guard);
2670            assert!(OtelContext::current().get::<ValueA>().is_none());
2671
2672            // Enter the root span - with context activation disabled,
2673            // the context should NOT be propagated
2674            let _enter_root = root.enter();
2675            assert!(OtelContext::current().get::<ValueA>().is_none());
2676
2677            // Add another value to the current OpenTelemetry context
2678            let _inner_guard =
2679                OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2680            assert!(OtelContext::current().get::<ValueA>().is_none());
2681            assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2682
2683            let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2684
2685            // Drop the guard to ensure the context is reverted
2686            drop(_inner_guard);
2687            assert!(OtelContext::current().get::<ValueA>().is_none());
2688            assert!(OtelContext::current().get::<ValueB>().is_none());
2689
2690            // Enter the child span - with context activation disabled,
2691            // the context should NOT be propagated
2692            let _enter_child = child.enter();
2693            assert!(OtelContext::current().get::<ValueA>().is_none());
2694            assert!(OtelContext::current().get::<ValueB>().is_none());
2695        });
2696
2697        // Verify spans were still created and exported
2698        let spans = tracer.spans();
2699        assert_eq!(spans.len(), 2);
2700        assert!(spans
2701            .iter()
2702            .any(|span| span.name == "tokio-tracing-span-parent"));
2703        assert!(spans
2704            .iter()
2705            .any(|span| span.name == "tokio-tracing-span-child"));
2706    }
2707}