tracing_opentelemetry/
layer.rs

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