tracing_opentelemetry/
layer.rs

1use std::convert::TryFrom;
2
3use crate::{OtelData, PreSampledTracer};
4use once_cell::unsync;
5use opentelemetry::{
6    trace::{self as otel, noop, SpanBuilder, SpanKind, Status, TraceContextExt},
7    Context as OtelContext, Key, KeyValue, StringValue, Value,
8};
9use std::fmt;
10use std::marker;
11use std::thread;
12#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
13use std::time::Instant;
14use std::{any::TypeId, borrow::Cow};
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_MESSAGE_FIELD: &str = "otel.status_message";
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    max_events_per_span: Option<usize>,
43    tracked_inactivity: bool,
44    with_threads: bool,
45    sem_conv_config: SemConvConfig,
46    get_context: WithContext,
47    _registry: marker::PhantomData<S>,
48}
49
50impl<S> Default for OpenTelemetryLayer<S, noop::NoopTracer>
51where
52    S: Subscriber + for<'span> LookupSpan<'span>,
53{
54    fn default() -> Self {
55        OpenTelemetryLayer::new(noop::NoopTracer::new())
56    }
57}
58
59/// Construct a layer to track spans via [OpenTelemetry].
60///
61/// [OpenTelemetry]: https://opentelemetry.io
62///
63/// # Examples
64///
65/// ```rust,no_run
66/// use tracing_subscriber::layer::SubscriberExt;
67/// use tracing_subscriber::Registry;
68///
69/// // Use the tracing subscriber `Registry`, or any other subscriber
70/// // that impls `LookupSpan`
71/// let subscriber = Registry::default().with(tracing_opentelemetry::layer());
72/// # drop(subscriber);
73/// ```
74pub fn layer<S>() -> OpenTelemetryLayer<S, noop::NoopTracer>
75where
76    S: Subscriber + for<'span> LookupSpan<'span>,
77{
78    OpenTelemetryLayer::default()
79}
80
81// this function "remembers" the types of the subscriber so that we
82// can downcast to something aware of them without knowing those
83// types at the callsite.
84//
85// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs
86pub(crate) struct WithContext(
87    #[allow(clippy::type_complexity)]
88    fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer)),
89);
90
91impl WithContext {
92    // This function allows a function to be called in the context of the
93    // "remembered" subscriber.
94    pub(crate) fn with_context(
95        &self,
96        dispatch: &tracing::Dispatch,
97        id: &span::Id,
98        mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer),
99    ) {
100        (self.0)(dispatch, id, &mut f)
101    }
102}
103
104/// Using [`OpenTelemetryLayer::max_events_per_span`] you can limit the number
105/// of [`otel::Event`]s that we'll record for a given span. If we exceed this
106/// limit we'll keep a count of how many events we've dropped.
107#[derive(Debug)]
108struct DroppedOtelEvents {
109    // how many events we've dropped thus far
110    dropped_count: usize,
111}
112
113impl DroppedOtelEvents {
114    fn new() -> Self {
115        DroppedOtelEvents { dropped_count: 0 }
116    }
117
118    fn add_dropped(&mut self, additional_count: usize) {
119        self.dropped_count = self.dropped_count.saturating_add(additional_count);
120    }
121}
122
123fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
124    match s {
125        s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
126        s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
127        s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
128        s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
129        s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
130        _ => None,
131    }
132}
133
134fn str_to_status(s: &str) -> otel::Status {
135    match s {
136        s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
137        s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
138        _ => otel::Status::Unset,
139    }
140}
141
142#[derive(Default)]
143struct SpanBuilderUpdates {
144    name: Option<Cow<'static, str>>,
145    span_kind: Option<SpanKind>,
146    status: Option<Status>,
147    attributes: Option<Vec<KeyValue>>,
148}
149
150impl SpanBuilderUpdates {
151    fn update(self, span_builder: &mut SpanBuilder) {
152        let Self {
153            name,
154            span_kind,
155            status,
156            attributes,
157        } = self;
158
159        if let Some(name) = name {
160            span_builder.name = name;
161        }
162        if let Some(span_kind) = span_kind {
163            span_builder.span_kind = Some(span_kind);
164        }
165        if let Some(status) = status {
166            span_builder.status = status;
167        }
168        if let Some(attributes) = attributes {
169            if let Some(builder_attributes) = &mut span_builder.attributes {
170                builder_attributes.extend(attributes);
171            } else {
172                span_builder.attributes = Some(attributes);
173            }
174        }
175    }
176}
177
178struct SpanEventVisitor<'a, 'b> {
179    event_builder: &'a mut otel::Event,
180    span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
181    sem_conv_config: SemConvConfig,
182}
183
184impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
185    /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values.
186    ///
187    /// [`Span`]: opentelemetry::trace::Span
188    fn record_bool(&mut self, field: &field::Field, value: bool) {
189        match field.name() {
190            "message" => self.event_builder.name = value.to_string().into(),
191            // Skip fields that are actually log metadata that have already been handled
192            #[cfg(feature = "tracing-log")]
193            name if name.starts_with("log.") => (),
194            name => {
195                self.event_builder
196                    .attributes
197                    .push(KeyValue::new(name, value));
198            }
199        }
200    }
201
202    /// Record events on the underlying OpenTelemetry [`Span`] from `f64` values.
203    ///
204    /// [`Span`]: opentelemetry::trace::Span
205    fn record_f64(&mut self, field: &field::Field, value: f64) {
206        match field.name() {
207            "message" => self.event_builder.name = value.to_string().into(),
208            // Skip fields that are actually log metadata that have already been handled
209            #[cfg(feature = "tracing-log")]
210            name if name.starts_with("log.") => (),
211            name => {
212                self.event_builder
213                    .attributes
214                    .push(KeyValue::new(name, value));
215            }
216        }
217    }
218
219    /// Record events on the underlying OpenTelemetry [`Span`] from `i64` values.
220    ///
221    /// [`Span`]: opentelemetry::trace::Span
222    fn record_i64(&mut self, field: &field::Field, value: i64) {
223        match field.name() {
224            "message" => self.event_builder.name = value.to_string().into(),
225            // Skip fields that are actually log metadata that have already been handled
226            #[cfg(feature = "tracing-log")]
227            name if name.starts_with("log.") => (),
228            name => {
229                self.event_builder
230                    .attributes
231                    .push(KeyValue::new(name, value));
232            }
233        }
234    }
235
236    /// Record events on the underlying OpenTelemetry [`Span`] from `&str` values.
237    ///
238    /// [`Span`]: opentelemetry::trace::Span
239    fn record_str(&mut self, field: &field::Field, value: &str) {
240        match field.name() {
241            "message" => self.event_builder.name = value.to_string().into(),
242            // While tracing supports the error primitive, the instrumentation macro does not
243            // use the primitive and instead uses the debug or display primitive.
244            // In both cases, an event with an empty name and with an error attribute is created.
245            "error" if self.event_builder.name.is_empty() => {
246                if self.sem_conv_config.error_events_to_status {
247                    self.span_builder_updates
248                        .get_or_insert_with(SpanBuilderUpdates::default)
249                        .status
250                        .replace(otel::Status::error(format!("{:?}", value)));
251                }
252                if self.sem_conv_config.error_events_to_exceptions {
253                    self.event_builder.name = EVENT_EXCEPTION_NAME.into();
254                    self.event_builder.attributes.push(KeyValue::new(
255                        FIELD_EXCEPTION_MESSAGE,
256                        format!("{:?}", value),
257                    ));
258                } else {
259                    self.event_builder
260                        .attributes
261                        .push(KeyValue::new("error", format!("{:?}", value)));
262                }
263            }
264            // Skip fields that are actually log metadata that have already been handled
265            #[cfg(feature = "tracing-log")]
266            name if name.starts_with("log.") => (),
267            name => {
268                self.event_builder
269                    .attributes
270                    .push(KeyValue::new(name, value.to_string()));
271            }
272        }
273    }
274
275    /// Record events on the underlying OpenTelemetry [`Span`] from values that
276    /// implement Debug.
277    ///
278    /// [`Span`]: opentelemetry::trace::Span
279    fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
280        match field.name() {
281            "message" => self.event_builder.name = format!("{:?}", value).into(),
282            // While tracing supports the error primitive, the instrumentation macro does not
283            // use the primitive and instead uses the debug or display primitive.
284            // In both cases, an event with an empty name and with an error attribute is created.
285            "error" if self.event_builder.name.is_empty() => {
286                if self.sem_conv_config.error_events_to_status {
287                    self.span_builder_updates
288                        .get_or_insert_with(SpanBuilderUpdates::default)
289                        .status
290                        .replace(otel::Status::error(format!("{:?}", value)));
291                }
292                if self.sem_conv_config.error_events_to_exceptions {
293                    self.event_builder.name = EVENT_EXCEPTION_NAME.into();
294                    self.event_builder.attributes.push(KeyValue::new(
295                        FIELD_EXCEPTION_MESSAGE,
296                        format!("{:?}", value),
297                    ));
298                } else {
299                    self.event_builder
300                        .attributes
301                        .push(KeyValue::new("error", format!("{:?}", value)));
302                }
303            }
304            // Skip fields that are actually log metadata that have already been handled
305            #[cfg(feature = "tracing-log")]
306            name if name.starts_with("log.") => (),
307            name => {
308                self.event_builder
309                    .attributes
310                    .push(KeyValue::new(name, format!("{:?}", value)));
311            }
312        }
313    }
314
315    /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s
316    /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field
317    ///
318    /// [`Span`]: opentelemetry::trace::Span
319    fn record_error(
320        &mut self,
321        field: &tracing_core::Field,
322        value: &(dyn std::error::Error + 'static),
323    ) {
324        let mut chain: Vec<StringValue> = Vec::new();
325        let mut next_err = value.source();
326
327        while let Some(err) = next_err {
328            chain.push(err.to_string().into());
329            next_err = err.source();
330        }
331
332        let error_msg = value.to_string();
333
334        if self.sem_conv_config.error_fields_to_exceptions {
335            self.event_builder
336                .attributes
337                .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
338
339            // NOTE: This is actually not the stacktrace of the exception. This is
340            // the "source chain". It represents the heirarchy of errors from the
341            // app level to the lowest level such as IO. It does not represent all
342            // of the callsites in the code that led to the error happening.
343            // `std::error::Error::backtrace` is a nightly-only API and cannot be
344            // used here until the feature is stabilized.
345            self.event_builder
346                .attributes
347                .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
348        }
349
350        if self.sem_conv_config.error_records_to_exceptions {
351            let attributes = self
352                .span_builder_updates
353                .get_or_insert_with(SpanBuilderUpdates::default)
354                .attributes
355                .get_or_insert_with(Vec::new);
356
357            attributes.push(KeyValue::new(
358                FIELD_EXCEPTION_MESSAGE,
359                Value::String(error_msg.clone().into()),
360            ));
361
362            // NOTE: This is actually not the stacktrace of the exception. This is
363            // the "source chain". It represents the heirarchy of errors from the
364            // app level to the lowest level such as IO. It does not represent all
365            // of the callsites in the code that led to the error happening.
366            // `std::error::Error::backtrace` is a nightly-only API and cannot be
367            // used here until the feature is stabilized.
368            attributes.push(KeyValue::new(
369                FIELD_EXCEPTION_STACKTRACE,
370                Value::Array(chain.clone().into()),
371            ));
372        }
373
374        self.event_builder
375            .attributes
376            .push(Key::new(field.name()).string(error_msg));
377        self.event_builder
378            .attributes
379            .push(Key::new(format!("{}.chain", field.name())).array(chain));
380    }
381}
382
383/// Control over the mapping between tracing fields/events and OpenTelemetry conventional status/exception fields
384#[derive(Clone, Copy)]
385struct SemConvConfig {
386    /// If an error value is recorded on an event/span, should the otel fields
387    /// be added
388    ///
389    /// Note that this uses tracings `record_error` which is only implemented for `(dyn Error + 'static)`.
390    error_fields_to_exceptions: bool,
391
392    /// If an error value is recorded on an event, should the otel fields be
393    /// added to the corresponding span
394    ///
395    /// Note that this uses tracings `record_error` which is only implemented for `(dyn Error + 'static)`.
396    error_records_to_exceptions: bool,
397
398    /// If a function is instrumented and returns a `Result`, should the error
399    /// value be propagated to the span status.
400    ///
401    /// Without this enabled, the span status will be "Error" with an empty description
402    /// when at least one error event is recorded in the span.
403    ///
404    /// Note: the instrument macro will emit an error event if the function returns the `Err` variant.
405    /// This is not affected by this setting. Disabling this will only affect the span status.
406    error_events_to_status: bool,
407
408    /// If an event with an empty name and a field named `error` is recorded,
409    /// should the event be rewritten to have the name `exception` and the field `exception.message`
410    ///
411    /// Follows the semantic conventions for exceptions.
412    ///
413    /// Note: the instrument macro will emit an error event if the function returns the `Err` variant.
414    /// This is not affected by this setting. Disabling this will only affect the created fields on the OTel span.
415    error_events_to_exceptions: bool,
416}
417
418struct SpanAttributeVisitor<'a> {
419    span_builder_updates: &'a mut SpanBuilderUpdates,
420    sem_conv_config: SemConvConfig,
421}
422
423impl<'a> SpanAttributeVisitor<'a> {
424    fn record(&mut self, attribute: KeyValue) {
425        self.span_builder_updates
426            .attributes
427            .get_or_insert_with(Vec::new)
428            .push(KeyValue::new(attribute.key, attribute.value));
429    }
430}
431
432impl<'a> field::Visit for SpanAttributeVisitor<'a> {
433    /// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values.
434    ///
435    /// [`Span`]: opentelemetry::trace::Span
436    fn record_bool(&mut self, field: &field::Field, value: bool) {
437        self.record(KeyValue::new(field.name(), value));
438    }
439
440    /// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values.
441    ///
442    /// [`Span`]: opentelemetry::trace::Span
443    fn record_f64(&mut self, field: &field::Field, value: f64) {
444        self.record(KeyValue::new(field.name(), value));
445    }
446
447    /// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values.
448    ///
449    /// [`Span`]: opentelemetry::trace::Span
450    fn record_i64(&mut self, field: &field::Field, value: i64) {
451        self.record(KeyValue::new(field.name(), value));
452    }
453
454    /// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values.
455    ///
456    /// [`Span`]: opentelemetry::trace::Span
457    fn record_str(&mut self, field: &field::Field, value: &str) {
458        match field.name() {
459            SPAN_NAME_FIELD => self.span_builder_updates.name = Some(value.to_string().into()),
460            SPAN_KIND_FIELD => self.span_builder_updates.span_kind = str_to_span_kind(value),
461            SPAN_STATUS_CODE_FIELD => self.span_builder_updates.status = Some(str_to_status(value)),
462            SPAN_STATUS_MESSAGE_FIELD => {
463                self.span_builder_updates.status = Some(otel::Status::error(value.to_string()))
464            }
465            _ => self.record(KeyValue::new(field.name(), value.to_string())),
466        }
467    }
468
469    /// Set attributes on the underlying OpenTelemetry [`Span`] from values that
470    /// implement Debug.
471    ///
472    /// [`Span`]: opentelemetry::trace::Span
473    fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
474        match field.name() {
475            SPAN_NAME_FIELD => self.span_builder_updates.name = Some(format!("{:?}", value).into()),
476            SPAN_KIND_FIELD => {
477                self.span_builder_updates.span_kind = str_to_span_kind(&format!("{:?}", value))
478            }
479            SPAN_STATUS_CODE_FIELD => {
480                self.span_builder_updates.status = Some(str_to_status(&format!("{:?}", value)))
481            }
482            SPAN_STATUS_MESSAGE_FIELD => {
483                self.span_builder_updates.status = Some(otel::Status::error(format!("{:?}", value)))
484            }
485            _ => self.record(Key::new(field.name()).string(format!("{:?}", value))),
486        }
487    }
488
489    /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s
490    /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field
491    ///
492    /// [`Span`]: opentelemetry::trace::Span
493    fn record_error(
494        &mut self,
495        field: &tracing_core::Field,
496        value: &(dyn std::error::Error + 'static),
497    ) {
498        let mut chain: Vec<StringValue> = Vec::new();
499        let mut next_err = value.source();
500
501        while let Some(err) = next_err {
502            chain.push(err.to_string().into());
503            next_err = err.source();
504        }
505
506        let error_msg = value.to_string();
507
508        if self.sem_conv_config.error_fields_to_exceptions {
509            self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
510
511            // NOTE: This is actually not the stacktrace of the exception. This is
512            // the "source chain". It represents the heirarchy of errors from the
513            // app level to the lowest level such as IO. It does not represent all
514            // of the callsites in the code that led to the error happening.
515            // `std::error::Error::backtrace` is a nightly-only API and cannot be
516            // used here until the feature is stabilized.
517            self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
518        }
519
520        self.record(Key::new(field.name()).string(error_msg));
521        self.record(Key::new(format!("{}.chain", field.name())).array(chain));
522    }
523}
524
525impl<S, T> OpenTelemetryLayer<S, T>
526where
527    S: Subscriber + for<'span> LookupSpan<'span>,
528    T: otel::Tracer + PreSampledTracer + 'static,
529{
530    /// Set the [`Tracer`] that this layer will use to produce and track
531    /// OpenTelemetry [`Span`]s.
532    ///
533    /// [`Tracer`]: opentelemetry::trace::Tracer
534    /// [`Span`]: opentelemetry::trace::Span
535    ///
536    /// # Examples
537    ///
538    /// ```no_run
539    /// use tracing_opentelemetry::OpenTelemetryLayer;
540    /// use tracing_subscriber::layer::SubscriberExt;
541    /// use opentelemetry::trace::TracerProvider;
542    /// use tracing_subscriber::Registry;
543    ///
544    /// // Create an OTLP pipeline exporter for a `trace_demo` service.
545    ///
546    /// let otlp_exporter = opentelemetry_otlp::new_exporter().tonic();
547    /// let tracer = opentelemetry_otlp::new_pipeline()
548    ///     .tracing()
549    ///     .with_exporter(otlp_exporter)
550    ///     .install_simple()
551    ///     .unwrap()
552    ///     .tracer("trace_demo");
553    ///
554    /// // Create a layer with the configured tracer
555    /// let otel_layer = OpenTelemetryLayer::new(tracer);
556    ///
557    /// // Use the tracing subscriber `Registry`, or any other subscriber
558    /// // that impls `LookupSpan`
559    /// let subscriber = Registry::default().with(otel_layer);
560    /// # drop(subscriber);
561    /// ```
562    pub fn new(tracer: T) -> Self {
563        OpenTelemetryLayer {
564            tracer,
565            location: true,
566            max_events_per_span: None,
567            tracked_inactivity: true,
568            with_threads: true,
569            sem_conv_config: SemConvConfig {
570                error_fields_to_exceptions: true,
571                error_records_to_exceptions: true,
572                error_events_to_exceptions: true,
573                error_events_to_status: true,
574            },
575
576            get_context: WithContext(Self::get_context),
577            _registry: marker::PhantomData,
578        }
579    }
580
581    /// Set the [`Tracer`] that this layer will use to produce and track
582    /// OpenTelemetry [`Span`]s.
583    ///
584    /// [`Tracer`]: opentelemetry::trace::Tracer
585    /// [`Span`]: opentelemetry::trace::Span
586    ///
587    /// # Examples
588    ///
589    /// ```no_run
590    /// use tracing_subscriber::layer::SubscriberExt;
591    /// use tracing_subscriber::Registry;
592    /// use opentelemetry::trace::TracerProvider;
593    ///
594    /// // Create an OTLP pipeline exporter for a `trace_demo` service.
595    ///
596    /// let otlp_exporter = opentelemetry_otlp::new_exporter().tonic();
597    /// let tracer = opentelemetry_otlp::new_pipeline()
598    ///     .tracing()
599    ///     .with_exporter(otlp_exporter)
600    ///     .install_simple()
601    ///     .unwrap()
602    ///     .tracer("trace_demo");
603    ///
604    /// // Create a layer with the configured tracer
605    /// let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer);
606    ///
607    /// // Use the tracing subscriber `Registry`, or any other subscriber
608    /// // that impls `LookupSpan`
609    /// let subscriber = Registry::default().with(otel_layer);
610    /// # drop(subscriber);
611    /// ```
612    pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
613    where
614        Tracer: otel::Tracer + PreSampledTracer + 'static,
615    {
616        OpenTelemetryLayer {
617            tracer,
618            location: self.location,
619            max_events_per_span: self.max_events_per_span,
620            tracked_inactivity: self.tracked_inactivity,
621            with_threads: self.with_threads,
622            sem_conv_config: self.sem_conv_config,
623            get_context: WithContext(OpenTelemetryLayer::<S, Tracer>::get_context),
624            _registry: self._registry,
625        }
626    }
627
628    /// Sets whether or not span and event metadata should include OpenTelemetry
629    /// exception fields such as `exception.message` and `exception.backtrace`
630    /// when an `Error` value is recorded. If multiple error values are recorded
631    /// on the same span/event, only the most recently recorded error value will
632    /// show up under these fields.
633    ///
634    /// These attributes follow the [OpenTelemetry semantic conventions for
635    /// exceptions][conv].
636    ///
637    /// By default, these attributes are recorded.
638    /// Note that this only works for `(dyn Error + 'static)`.
639    /// See [Implementations on Foreign Types of tracing::Value][impls] or [`OpenTelemetryLayer::with_error_events_to_exceptions`]
640    ///
641    /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/
642    /// [impls]: https://docs.rs/tracing/0.1.37/tracing/trait.Value.html#foreign-impls
643    pub fn with_error_fields_to_exceptions(self, error_fields_to_exceptions: bool) -> Self {
644        Self {
645            sem_conv_config: SemConvConfig {
646                error_fields_to_exceptions,
647                ..self.sem_conv_config
648            },
649            ..self
650        }
651    }
652
653    /// Sets whether or not an event considered for exception mapping (see [`OpenTelemetryLayer::with_error_recording`])
654    /// should be propagated to the span status error description.
655    ///
656    ///
657    /// By default, these events do set the span status error description.
658    pub fn with_error_events_to_status(self, error_events_to_status: bool) -> Self {
659        Self {
660            sem_conv_config: SemConvConfig {
661                error_events_to_status,
662                ..self.sem_conv_config
663            },
664            ..self
665        }
666    }
667
668    /// Sets whether or not a subset of events following the described schema are mapped to
669    /// events following the [OpenTelemetry semantic conventions for
670    /// exceptions][conv].
671    ///
672    /// * Only events without a message field (unnamed events) and at least one field with the name error
673    /// are considered for mapping.
674    ///
675    /// By default, these events are mapped.
676    ///
677    /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/
678    pub fn with_error_events_to_exceptions(self, error_events_to_exceptions: bool) -> Self {
679        Self {
680            sem_conv_config: SemConvConfig {
681                error_events_to_exceptions,
682                ..self.sem_conv_config
683            },
684            ..self
685        }
686    }
687
688    /// Sets whether or not reporting an `Error` value on an event will
689    /// propagate the OpenTelemetry exception fields such as `exception.message`
690    /// and `exception.backtrace` to the corresponding span. You do not need to
691    /// enable `with_exception_fields` in order to enable this. If multiple
692    /// error values are recorded on the same span/event, only the most recently
693    /// recorded error value will 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 propagated to the span. Note that this only works for `(dyn Error + 'static)`.
699    /// See [Implementations on Foreign Types of tracing::Value][impls] or [`OpenTelemetryLayer::with_error_events_to_exceptions`]
700    ///
701    /// [conv]: https://github.com/open-telemetry/semantic-conventions/tree/main/docs/exceptions/
702    /// [impls]: https://docs.rs/tracing/0.1.37/tracing/trait.Value.html#foreign-impls
703    pub fn with_error_records_to_exceptions(self, error_records_to_exceptions: bool) -> Self {
704        Self {
705            sem_conv_config: SemConvConfig {
706                error_records_to_exceptions,
707                ..self.sem_conv_config
708            },
709            ..self
710        }
711    }
712
713    /// Sets whether or not span and event metadata should include OpenTelemetry
714    /// attributes with location information, such as the file, module and line number.
715    ///
716    /// These attributes follow the [OpenTelemetry semantic conventions for
717    /// source locations][conv].
718    ///
719    /// By default, locations are enabled.
720    ///
721    /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes/
722    pub fn with_location(self, location: bool) -> Self {
723        Self { location, ..self }
724    }
725
726    /// Sets whether or not spans metadata should include the _busy time_
727    /// (total time for which it was entered), and _idle time_ (total time
728    /// the span existed but was not entered).
729    pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
730        Self {
731            tracked_inactivity,
732            ..self
733        }
734    }
735
736    /// Sets whether or not spans record additional attributes for the thread
737    /// name and thread ID of the thread they were created on, following the
738    /// [OpenTelemetry semantic conventions for threads][conv].
739    ///
740    /// By default, thread attributes are enabled.
741    ///
742    /// [conv]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#general-thread-attributes/
743    pub fn with_threads(self, threads: bool) -> Self {
744        Self {
745            with_threads: threads,
746            ..self
747        }
748    }
749
750    /// Sets the maximum number of [`otel::Event`] that we will store for any given
751    /// span. If we exceed this number, we will drop the first half of our buffered
752    /// events and maintain a count of events dropped thus far.
753    ///
754    /// By default, there is no maximum
755    ///
756    /// Note: This can be very useful when tracing at a `Debug` or `Trace` level. Some
757    /// crates (e.g. `h2`) maintain long living spans which result in events
758    /// continuously getting recorded, but never flushed. So in these cases you'd keep
759    /// buffering events until you run out of memory (OOM).
760    pub fn max_events_per_span(self, num: usize) -> Self {
761        Self {
762            max_events_per_span: Some(num),
763            ..self
764        }
765    }
766
767    /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing
768    /// [`span`] through the [`Registry`]. This [`Context`] links spans to their
769    /// parent for proper hierarchical visualization.
770    ///
771    /// [`Context`]: opentelemetry::Context
772    /// [`span`]: tracing::Span
773    /// [`Registry`]: tracing_subscriber::Registry
774    fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext {
775        if let Some(parent) = attrs.parent() {
776            // A span can have an _explicit_ parent that is NOT seen by this `Layer` (for which
777            // `Context::span` returns `None`. This happens if the parent span is filtered away
778            // from the layer by a per-layer filter. In that case, we fall-through to the `else`
779            // case, and consider this span a root span.
780            //
781            // This is likely rare, as most users who use explicit parents will configure their
782            // filters so that children and parents are both seen, but it's not guaranteed. Also,
783            // if users configure their filter with a `reload` filter, it's possible that a parent
784            // and child have different filters as they are created with a filter change
785            // in-between.
786            //
787            // In these case, we prefer to emit a smaller span tree instead of panicking.
788            if let Some(span) = ctx.span(parent) {
789                let mut extensions = span.extensions_mut();
790                return extensions
791                    .get_mut::<OtelData>()
792                    .map(|builder| self.tracer.sampled_context(builder))
793                    .unwrap_or_default();
794            }
795        }
796
797        // Else if the span is inferred from context, look up any available current span.
798        if attrs.is_contextual() {
799            ctx.lookup_current()
800                .and_then(|span| {
801                    let mut extensions = span.extensions_mut();
802                    extensions
803                        .get_mut::<OtelData>()
804                        .map(|builder| self.tracer.sampled_context(builder))
805                })
806                .unwrap_or_else(OtelContext::current)
807        // Explicit root spans should have no parent context.
808        } else {
809            OtelContext::new()
810        }
811    }
812
813    fn get_context(
814        dispatch: &tracing::Dispatch,
815        id: &span::Id,
816        f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer),
817    ) {
818        let subscriber = dispatch
819            .downcast_ref::<S>()
820            .expect("subscriber should downcast to expected type; this is a bug!");
821        let span = subscriber
822            .span(id)
823            .expect("registry should have a span for the current ID");
824        let layer = dispatch
825            .downcast_ref::<OpenTelemetryLayer<S, T>>()
826            .expect("layer should downcast to expected type; this is a bug!");
827
828        let mut extensions = span.extensions_mut();
829        if let Some(builder) = extensions.get_mut::<OtelData>() {
830            f(builder, &layer.tracer);
831        }
832    }
833
834    fn extra_span_attrs(&self) -> usize {
835        let mut extra_attrs = 0;
836        if self.location {
837            extra_attrs += 3;
838        }
839        if self.with_threads {
840            extra_attrs += 2;
841        }
842        extra_attrs
843    }
844}
845
846thread_local! {
847    static THREAD_ID: unsync::Lazy<u64> = unsync::Lazy::new(|| {
848        // OpenTelemetry's semantic conventions require the thread ID to be
849        // recorded as an integer, but `std::thread::ThreadId` does not expose
850        // the integer value on stable, so we have to convert it to a `usize` by
851        // parsing it. Since this requires allocating a `String`, store it in a
852        // thread local so we only have to do this once.
853        // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized
854        // (https://github.com/rust-lang/rust/issues/67939), just use that.
855        thread_id_integer(thread::current().id())
856    });
857}
858
859impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
860where
861    S: Subscriber + for<'span> LookupSpan<'span>,
862    T: otel::Tracer + PreSampledTracer + 'static,
863{
864    /// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`].
865    ///
866    /// [OpenTelemetry `Span`]: opentelemetry::trace::Span
867    /// [tracing `Span`]: tracing::Span
868    fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
869        let span = ctx.span(id).expect("Span not found, this is a bug");
870        let mut extensions = span.extensions_mut();
871
872        if self.tracked_inactivity && extensions.get_mut::<Timings>().is_none() {
873            extensions.insert(Timings::new());
874        }
875
876        let parent_cx = self.parent_context(attrs, &ctx);
877        let mut builder = self
878            .tracer
879            .span_builder(attrs.metadata().name())
880            .with_start_time(crate::time::now())
881            // Eagerly assign span id so children have stable parent id
882            .with_span_id(self.tracer.new_span_id());
883
884        // Record new trace id if there is no active parent span
885        if !parent_cx.has_active_span() {
886            builder.trace_id = Some(self.tracer.new_trace_id());
887        }
888
889        let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
890            attrs.fields().len() + self.extra_span_attrs(),
891        ));
892
893        if self.location {
894            let meta = attrs.metadata();
895
896            if let Some(filename) = meta.file() {
897                builder_attrs.push(KeyValue::new("code.filepath", filename));
898            }
899
900            if let Some(module) = meta.module_path() {
901                builder_attrs.push(KeyValue::new("code.namespace", module));
902            }
903
904            if let Some(line) = meta.line() {
905                builder_attrs.push(KeyValue::new("code.lineno", line as i64));
906            }
907        }
908
909        if self.with_threads {
910            THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", **id as i64)));
911            if let Some(name) = std::thread::current().name() {
912                // TODO(eliza): it's a bummer that we have to allocate here, but
913                // we can't easily get the string as a `static`. it would be
914                // nice if `opentelemetry` could also take `Arc<str>`s as
915                // `String` values...
916                builder_attrs.push(KeyValue::new("thread.name", name.to_string()));
917            }
918        }
919
920        let mut updates = SpanBuilderUpdates::default();
921        attrs.record(&mut SpanAttributeVisitor {
922            span_builder_updates: &mut updates,
923            sem_conv_config: self.sem_conv_config,
924        });
925
926        updates.update(&mut builder);
927        extensions.insert(OtelData { builder, parent_cx });
928    }
929
930    fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
931        if !self.tracked_inactivity {
932            return;
933        }
934
935        let span = ctx.span(id).expect("Span not found, this is a bug");
936        let mut extensions = span.extensions_mut();
937
938        if let Some(timings) = extensions.get_mut::<Timings>() {
939            let now = Instant::now();
940            timings.idle += (now - timings.last).as_nanos() as i64;
941            timings.last = now;
942        }
943    }
944
945    fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
946        let span = ctx.span(id).expect("Span not found, this is a bug");
947        let mut extensions = span.extensions_mut();
948
949        if let Some(otel_data) = extensions.get_mut::<OtelData>() {
950            otel_data.builder.end_time = Some(crate::time::now());
951        }
952
953        if !self.tracked_inactivity {
954            return;
955        }
956
957        if let Some(timings) = extensions.get_mut::<Timings>() {
958            let now = Instant::now();
959            timings.busy += (now - timings.last).as_nanos() as i64;
960            timings.last = now;
961        }
962    }
963
964    /// Record OpenTelemetry [`attributes`] for the given values.
965    ///
966    /// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes
967    fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
968        let span = ctx.span(id).expect("Span not found, this is a bug");
969        let mut updates = SpanBuilderUpdates::default();
970        values.record(&mut SpanAttributeVisitor {
971            span_builder_updates: &mut updates,
972            sem_conv_config: self.sem_conv_config,
973        });
974        let mut extensions = span.extensions_mut();
975        if let Some(data) = extensions.get_mut::<OtelData>() {
976            updates.update(&mut data.builder);
977        }
978    }
979
980    fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context<S>) {
981        let span = ctx.span(id).expect("Span not found, this is a bug");
982        let mut extensions = span.extensions_mut();
983        let data = extensions
984            .get_mut::<OtelData>()
985            .expect("Missing otel data span extensions");
986
987        // The follows span may be filtered away (or closed), from this layer,
988        // in which case we just drop the data, as opposed to panicking. This
989        // uses the same reasoning as `parent_context` above.
990        if let Some(follows_span) = ctx.span(follows) {
991            let mut follows_extensions = follows_span.extensions_mut();
992            let follows_data = follows_extensions
993                .get_mut::<OtelData>()
994                .expect("Missing otel data span extensions");
995
996            let follows_context = self
997                .tracer
998                .sampled_context(follows_data)
999                .span()
1000                .span_context()
1001                .clone();
1002            let follows_link = otel::Link::with_context(follows_context);
1003            if let Some(ref mut links) = data.builder.links {
1004                links.push(follows_link);
1005            } else {
1006                data.builder.links = Some(vec![follows_link]);
1007            }
1008        }
1009    }
1010
1011    /// Records OpenTelemetry [`Event`] data on event.
1012    ///
1013    /// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to
1014    /// [`Error`], signaling that an error has occurred.
1015    ///
1016    /// [`Event`]: opentelemetry::trace::Event
1017    /// [`ERROR`]: tracing::Level::ERROR
1018    /// [`Error`]: opentelemetry::trace::StatusCode::Error
1019    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1020        // Ignore events that are not in the context of a span
1021        if let Some(span) = event.parent().and_then(|id| ctx.span(id)).or_else(|| {
1022            event
1023                .is_contextual()
1024                .then(|| ctx.lookup_current())
1025                .flatten()
1026        }) {
1027            // Performing read operations before getting a write lock to avoid a deadlock
1028            // See https://github.com/tokio-rs/tracing/issues/763
1029            #[cfg(feature = "tracing-log")]
1030            let normalized_meta = event.normalized_metadata();
1031            #[cfg(feature = "tracing-log")]
1032            let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1033            #[cfg(not(feature = "tracing-log"))]
1034            let meta = event.metadata();
1035
1036            let target = Key::new("target");
1037
1038            #[cfg(feature = "tracing-log")]
1039            let target = if normalized_meta.is_some() {
1040                target.string(meta.target().to_owned())
1041            } else {
1042                target.string(event.metadata().target())
1043            };
1044
1045            #[cfg(not(feature = "tracing-log"))]
1046            let target = target.string(meta.target());
1047
1048            let mut otel_event = otel::Event::new(
1049                String::new(),
1050                crate::time::now(),
1051                vec![Key::new("level").string(meta.level().as_str()), target],
1052                0,
1053            );
1054
1055            let mut builder_updates = None;
1056            event.record(&mut SpanEventVisitor {
1057                event_builder: &mut otel_event,
1058                span_builder_updates: &mut builder_updates,
1059                sem_conv_config: self.sem_conv_config,
1060            });
1061
1062            let mut extensions = span.extensions_mut();
1063            let otel_data = extensions.get_mut::<OtelData>();
1064
1065            let dropped_events = if let Some(otel_data) = otel_data {
1066                let builder = &mut otel_data.builder;
1067
1068                if builder.status == otel::Status::Unset
1069                    && *meta.level() == tracing_core::Level::ERROR
1070                {
1071                    builder.status = otel::Status::error("")
1072                }
1073
1074                if let Some(builder_updates) = builder_updates {
1075                    builder_updates.update(builder);
1076                }
1077
1078                if self.location {
1079                    #[cfg(not(feature = "tracing-log"))]
1080                    let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
1081                    let (file, module) = match &normalized_meta {
1082                        Some(meta) => (
1083                            meta.file().map(|s| Value::from(s.to_owned())),
1084                            meta.module_path().map(|s| Value::from(s.to_owned())),
1085                        ),
1086                        None => (
1087                            event.metadata().file().map(Value::from),
1088                            event.metadata().module_path().map(Value::from),
1089                        ),
1090                    };
1091
1092                    if let Some(file) = file {
1093                        otel_event
1094                            .attributes
1095                            .push(KeyValue::new("code.filepath", file));
1096                    }
1097                    if let Some(module) = module {
1098                        otel_event
1099                            .attributes
1100                            .push(KeyValue::new("code.namespace", module));
1101                    }
1102                    if let Some(line) = meta.line() {
1103                        otel_event
1104                            .attributes
1105                            .push(KeyValue::new("code.lineno", line as i64));
1106                    }
1107                }
1108
1109                if let Some(ref mut events) = builder.events {
1110                    events.push(otel_event);
1111                } else {
1112                    builder.events = Some(vec![otel_event]);
1113                }
1114
1115                // limit the maximum number of events we'll record per-span
1116                let dropped_events = match (self.max_events_per_span, &mut builder.events) {
1117                    (Some(max_num_events), Some(ref mut events))
1118                        if events.len() > max_num_events =>
1119                    {
1120                        // drain half of the existing events, using ceiling division
1121                        // to handle the case where max_num_events is 0 or 1
1122                        //
1123                        // note: this should be more efficient than just continuously
1124                        // popping the first element
1125                        let num_events_to_drain = (events.len() + 2 - 1) / 2;
1126                        events.drain(0..num_events_to_drain);
1127
1128                        Some(num_events_to_drain)
1129                    }
1130                    _ => None,
1131                };
1132
1133                dropped_events
1134            } else {
1135                None
1136            };
1137
1138            // record how many events we dropped
1139            match (dropped_events, extensions.get_mut::<DroppedOtelEvents>()) {
1140                (Some(additional_dropped), Some(current_dropped)) => {
1141                    current_dropped.add_dropped(additional_dropped);
1142                }
1143                (Some(additional_dropped), None) => {
1144                    let mut count = DroppedOtelEvents::new();
1145                    count.add_dropped(additional_dropped);
1146                    extensions.insert(count);
1147                }
1148                _ => (),
1149            }
1150        };
1151    }
1152
1153    /// Exports an OpenTelemetry [`Span`] on close.
1154    ///
1155    /// [`Span`]: opentelemetry::trace::Span
1156    fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
1157        let span = ctx.span(&id).expect("Span not found, this is a bug");
1158        let mut extensions = span.extensions_mut();
1159
1160        if let Some(OtelData {
1161            mut builder,
1162            parent_cx,
1163        }) = extensions.remove::<OtelData>()
1164        {
1165            if self.tracked_inactivity {
1166                // Append busy/idle timings when enabled.
1167                if let Some(timings) = extensions.get_mut::<Timings>() {
1168                    let busy_ns = Key::new("busy_ns");
1169                    let idle_ns = Key::new("idle_ns");
1170
1171                    let attributes = builder
1172                        .attributes
1173                        .get_or_insert_with(|| Vec::with_capacity(2));
1174                    attributes.push(KeyValue::new(busy_ns, timings.busy));
1175                    attributes.push(KeyValue::new(idle_ns, timings.idle));
1176                }
1177            }
1178
1179            // Report the number of events that were dropped, if we dropped any
1180            if let Some(DroppedOtelEvents { dropped_count }) =
1181                extensions.remove::<DroppedOtelEvents>()
1182            {
1183                let k = Key::from_static_str("dropped_event_count");
1184                let v = Value::I64(i64::try_from(dropped_count).unwrap_or(i64::MAX));
1185
1186                if let Some(ref mut attributes) = builder.attributes {
1187                    if let Some(index) = attributes.iter().position(|kv| kv.key == k) {
1188                        attributes[index] = KeyValue::new(k, v);
1189                    } else {
1190                        attributes.push(KeyValue::new(k, v));
1191                    }
1192                } else {
1193                    builder.attributes = Some(vec![KeyValue::new(k, v)]);
1194                }
1195            }
1196
1197            // Build and start span, drop span to export
1198            builder.start_with_context(&self.tracer, &parent_cx);
1199        }
1200    }
1201
1202    // SAFETY: this is safe because the `WithContext` function pointer is valid
1203    // for the lifetime of `&self`.
1204    unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
1205        match id {
1206            id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
1207            id if id == TypeId::of::<WithContext>() => {
1208                Some(&self.get_context as *const _ as *const ())
1209            }
1210            _ => None,
1211        }
1212    }
1213}
1214
1215struct Timings {
1216    idle: i64,
1217    busy: i64,
1218    last: Instant,
1219}
1220
1221impl Timings {
1222    fn new() -> Self {
1223        Self {
1224            idle: 0,
1225            busy: 0,
1226            last: Instant::now(),
1227        }
1228    }
1229}
1230
1231fn thread_id_integer(id: thread::ThreadId) -> u64 {
1232    let thread_id = format!("{:?}", id);
1233    thread_id
1234        .trim_start_matches("ThreadId(")
1235        .trim_end_matches(')')
1236        .parse::<u64>()
1237        .expect("thread ID should parse as an integer")
1238}
1239
1240#[cfg(test)]
1241mod tests {
1242    use super::*;
1243    use opentelemetry::trace::{SpanContext, TraceFlags};
1244    use std::{
1245        collections::HashMap,
1246        error::Error,
1247        fmt::Display,
1248        sync::{Arc, Mutex},
1249        time::SystemTime,
1250    };
1251    use tracing_subscriber::prelude::*;
1252    use tracing_subscriber::registry::LookupSpan;
1253
1254    #[derive(Debug, Clone)]
1255    struct TestTracer(Arc<Mutex<Option<OtelData>>>);
1256    impl otel::Tracer for TestTracer {
1257        type Span = noop::NoopSpan;
1258        fn start_with_context<T>(&self, _name: T, _context: &OtelContext) -> Self::Span
1259        where
1260            T: Into<Cow<'static, str>>,
1261        {
1262            noop::NoopSpan::DEFAULT
1263        }
1264        fn span_builder<T>(&self, name: T) -> otel::SpanBuilder
1265        where
1266            T: Into<Cow<'static, str>>,
1267        {
1268            otel::SpanBuilder::from_name(name)
1269        }
1270        fn build_with_context(
1271            &self,
1272            builder: otel::SpanBuilder,
1273            parent_cx: &OtelContext,
1274        ) -> Self::Span {
1275            *self.0.lock().unwrap() = Some(OtelData {
1276                builder,
1277                parent_cx: parent_cx.clone(),
1278            });
1279            noop::NoopSpan::DEFAULT
1280        }
1281    }
1282
1283    impl PreSampledTracer for TestTracer {
1284        fn sampled_context(&self, _builder: &mut crate::OtelData) -> OtelContext {
1285            OtelContext::new()
1286        }
1287        fn new_trace_id(&self) -> otel::TraceId {
1288            otel::TraceId::INVALID
1289        }
1290        fn new_span_id(&self) -> otel::SpanId {
1291            otel::SpanId::INVALID
1292        }
1293    }
1294
1295    impl TestTracer {
1296        fn with_data<T>(&self, f: impl FnOnce(&OtelData) -> T) -> T {
1297            let lock = self.0.lock().unwrap();
1298            let data = lock.as_ref().expect("no span data has been recorded yet");
1299            f(data)
1300        }
1301    }
1302
1303    #[derive(Debug, Clone)]
1304    struct TestSpan(otel::SpanContext);
1305    impl otel::Span for TestSpan {
1306        fn add_event_with_timestamp<T: Into<Cow<'static, str>>>(
1307            &mut self,
1308            _: T,
1309            _: SystemTime,
1310            _: Vec<KeyValue>,
1311        ) {
1312        }
1313        fn span_context(&self) -> &otel::SpanContext {
1314            &self.0
1315        }
1316        fn is_recording(&self) -> bool {
1317            false
1318        }
1319        fn set_attribute(&mut self, _attribute: KeyValue) {}
1320        fn set_status(&mut self, _status: otel::Status) {}
1321        fn update_name<T: Into<Cow<'static, str>>>(&mut self, _new_name: T) {}
1322        fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec<KeyValue>) {}
1323        fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
1324    }
1325
1326    #[derive(Debug)]
1327    struct TestDynError {
1328        msg: &'static str,
1329        source: Option<Box<TestDynError>>,
1330    }
1331    impl Display for TestDynError {
1332        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1333            write!(f, "{}", self.msg)
1334        }
1335    }
1336    impl Error for TestDynError {
1337        fn source(&self) -> Option<&(dyn Error + 'static)> {
1338            match &self.source {
1339                Some(source) => Some(source),
1340                None => None,
1341            }
1342        }
1343    }
1344    impl TestDynError {
1345        fn new(msg: &'static str) -> Self {
1346            Self { msg, source: None }
1347        }
1348        fn with_parent(self, parent_msg: &'static str) -> Self {
1349            Self {
1350                msg: parent_msg,
1351                source: Some(Box::new(self)),
1352            }
1353        }
1354    }
1355
1356    #[test]
1357    fn dynamic_span_names() {
1358        let dynamic_name = "GET http://example.com".to_string();
1359        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1360        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1361
1362        tracing::subscriber::with_default(subscriber, || {
1363            tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
1364        });
1365
1366        let recorded_name = tracer
1367            .0
1368            .lock()
1369            .unwrap()
1370            .as_ref()
1371            .map(|b| b.builder.name.clone());
1372        assert_eq!(recorded_name, Some(dynamic_name.into()))
1373    }
1374
1375    #[test]
1376    fn span_kind() {
1377        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1378        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1379
1380        tracing::subscriber::with_default(subscriber, || {
1381            tracing::debug_span!("request", otel.kind = "server");
1382        });
1383
1384        let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone());
1385        assert_eq!(recorded_kind, Some(otel::SpanKind::Server))
1386    }
1387
1388    #[test]
1389    fn span_status_code() {
1390        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1391        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1392
1393        tracing::subscriber::with_default(subscriber, || {
1394            tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
1395        });
1396
1397        let recorded_status = tracer.with_data(|data| data.builder.status.clone());
1398        assert_eq!(recorded_status, otel::Status::Ok)
1399    }
1400
1401    #[test]
1402    fn span_status_message() {
1403        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1404        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1405
1406        let message = "message";
1407
1408        tracing::subscriber::with_default(subscriber, || {
1409            tracing::debug_span!("request", otel.status_message = message);
1410        });
1411
1412        let recorded_status_message = tracer
1413            .0
1414            .lock()
1415            .unwrap()
1416            .as_ref()
1417            .unwrap()
1418            .builder
1419            .status
1420            .clone();
1421
1422        assert_eq!(recorded_status_message, otel::Status::error(message))
1423    }
1424
1425    #[test]
1426    fn trace_id_from_existing_context() {
1427        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1428        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1429        let trace_id = otel::TraceId::from(42u128);
1430        let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
1431            trace_id,
1432            otel::SpanId::from(1u64),
1433            TraceFlags::default(),
1434            false,
1435            Default::default(),
1436        )));
1437        let _g = existing_cx.attach();
1438
1439        tracing::subscriber::with_default(subscriber, || {
1440            tracing::debug_span!("request", otel.kind = "server");
1441        });
1442
1443        let recorded_trace_id =
1444            tracer.with_data(|data| data.parent_cx.span().span_context().trace_id());
1445        assert_eq!(recorded_trace_id, trace_id)
1446    }
1447
1448    #[test]
1449    fn includes_timings() {
1450        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1451        let subscriber = tracing_subscriber::registry().with(
1452            layer()
1453                .with_tracer(tracer.clone())
1454                .with_tracked_inactivity(true),
1455        );
1456
1457        tracing::subscriber::with_default(subscriber, || {
1458            tracing::debug_span!("request");
1459        });
1460
1461        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1462        let keys = attributes
1463            .iter()
1464            .map(|kv| kv.key.as_str())
1465            .collect::<Vec<&str>>();
1466        assert!(keys.contains(&"idle_ns"));
1467        assert!(keys.contains(&"busy_ns"));
1468    }
1469
1470    #[test]
1471    fn records_error_fields() {
1472        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1473        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1474
1475        let err = TestDynError::new("base error")
1476            .with_parent("intermediate error")
1477            .with_parent("user error");
1478
1479        tracing::subscriber::with_default(subscriber, || {
1480            tracing::debug_span!(
1481                "request",
1482                error = &err as &(dyn std::error::Error + 'static)
1483            );
1484        });
1485
1486        let attributes = tracer
1487            .0
1488            .lock()
1489            .unwrap()
1490            .as_ref()
1491            .unwrap()
1492            .builder
1493            .attributes
1494            .as_ref()
1495            .unwrap()
1496            .clone();
1497
1498        let key_values = attributes
1499            .into_iter()
1500            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1501            .collect::<HashMap<_, _>>();
1502
1503        assert_eq!(key_values["error"].as_str(), "user error");
1504        assert_eq!(
1505            key_values["error.chain"],
1506            Value::Array(
1507                vec![
1508                    StringValue::from("intermediate error"),
1509                    StringValue::from("base error")
1510                ]
1511                .into()
1512            )
1513        );
1514
1515        assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1516        assert_eq!(
1517            key_values[FIELD_EXCEPTION_STACKTRACE],
1518            Value::Array(
1519                vec![
1520                    StringValue::from("intermediate error"),
1521                    StringValue::from("base error")
1522                ]
1523                .into()
1524            )
1525        );
1526    }
1527
1528    #[test]
1529    fn records_no_error_fields() {
1530        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1531        let subscriber = tracing_subscriber::registry().with(
1532            layer()
1533                .with_error_records_to_exceptions(false)
1534                .with_tracer(tracer.clone()),
1535        );
1536
1537        let err = TestDynError::new("base error")
1538            .with_parent("intermediate error")
1539            .with_parent("user error");
1540
1541        tracing::subscriber::with_default(subscriber, || {
1542            tracing::debug_span!(
1543                "request",
1544                error = &err as &(dyn std::error::Error + 'static)
1545            );
1546        });
1547
1548        let attributes = tracer
1549            .0
1550            .lock()
1551            .unwrap()
1552            .as_ref()
1553            .unwrap()
1554            .builder
1555            .attributes
1556            .as_ref()
1557            .unwrap()
1558            .clone();
1559
1560        let key_values = attributes
1561            .into_iter()
1562            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1563            .collect::<HashMap<_, _>>();
1564
1565        assert_eq!(key_values["error"].as_str(), "user error");
1566        assert_eq!(
1567            key_values["error.chain"],
1568            Value::Array(
1569                vec![
1570                    StringValue::from("intermediate error"),
1571                    StringValue::from("base error")
1572                ]
1573                .into()
1574            )
1575        );
1576
1577        assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1578        assert_eq!(
1579            key_values[FIELD_EXCEPTION_STACKTRACE],
1580            Value::Array(
1581                vec![
1582                    StringValue::from("intermediate error"),
1583                    StringValue::from("base error")
1584                ]
1585                .into()
1586            )
1587        );
1588    }
1589
1590    #[test]
1591    fn includes_span_location() {
1592        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1593        let subscriber = tracing_subscriber::registry()
1594            .with(layer().with_tracer(tracer.clone()).with_location(true));
1595
1596        tracing::subscriber::with_default(subscriber, || {
1597            tracing::debug_span!("request");
1598        });
1599
1600        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1601        let keys = attributes
1602            .iter()
1603            .map(|kv| kv.key.as_str())
1604            .collect::<Vec<&str>>();
1605        assert!(keys.contains(&"code.filepath"));
1606        assert!(keys.contains(&"code.namespace"));
1607        assert!(keys.contains(&"code.lineno"));
1608    }
1609
1610    #[test]
1611    fn excludes_span_location() {
1612        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1613        let subscriber = tracing_subscriber::registry()
1614            .with(layer().with_tracer(tracer.clone()).with_location(false));
1615
1616        tracing::subscriber::with_default(subscriber, || {
1617            tracing::debug_span!("request");
1618        });
1619
1620        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1621        let keys = attributes
1622            .iter()
1623            .map(|kv| kv.key.as_str())
1624            .collect::<Vec<&str>>();
1625        assert!(!keys.contains(&"code.filepath"));
1626        assert!(!keys.contains(&"code.namespace"));
1627        assert!(!keys.contains(&"code.lineno"));
1628    }
1629
1630    #[test]
1631    fn includes_thread() {
1632        let thread = thread::current();
1633        let expected_name = thread
1634            .name()
1635            .map(|name| Value::String(name.to_owned().into()));
1636        let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
1637
1638        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1639        let subscriber = tracing_subscriber::registry()
1640            .with(layer().with_tracer(tracer.clone()).with_threads(true));
1641
1642        tracing::subscriber::with_default(subscriber, || {
1643            tracing::debug_span!("request");
1644        });
1645
1646        let attributes = tracer
1647            .with_data(|data| data.builder.attributes.as_ref().unwrap().clone())
1648            .drain(..)
1649            .map(|kv| (kv.key.as_str().to_string(), kv.value))
1650            .collect::<HashMap<_, _>>();
1651        assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
1652        assert_eq!(attributes.get("thread.id"), Some(&expected_id));
1653    }
1654
1655    #[test]
1656    fn excludes_thread() {
1657        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1658        let subscriber = tracing_subscriber::registry()
1659            .with(layer().with_tracer(tracer.clone()).with_threads(false));
1660
1661        tracing::subscriber::with_default(subscriber, || {
1662            tracing::debug_span!("request");
1663        });
1664
1665        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1666        let keys = attributes
1667            .iter()
1668            .map(|kv| kv.key.as_str())
1669            .collect::<Vec<&str>>();
1670        assert!(!keys.contains(&"thread.name"));
1671        assert!(!keys.contains(&"thread.id"));
1672    }
1673
1674    #[test]
1675    fn propagates_error_fields_from_event_to_span() {
1676        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1677        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1678
1679        let err = TestDynError::new("base error")
1680            .with_parent("intermediate error")
1681            .with_parent("user error");
1682
1683        tracing::subscriber::with_default(subscriber, || {
1684            let _guard = tracing::debug_span!("request",).entered();
1685
1686            tracing::error!(
1687                error = &err as &(dyn std::error::Error + 'static),
1688                "request error!"
1689            )
1690        });
1691
1692        let attributes = tracer
1693            .0
1694            .lock()
1695            .unwrap()
1696            .as_ref()
1697            .unwrap()
1698            .builder
1699            .attributes
1700            .as_ref()
1701            .unwrap()
1702            .clone();
1703
1704        let key_values = attributes
1705            .into_iter()
1706            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1707            .collect::<HashMap<_, _>>();
1708
1709        assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1710        assert_eq!(
1711            key_values[FIELD_EXCEPTION_STACKTRACE],
1712            Value::Array(
1713                vec![
1714                    StringValue::from("intermediate error"),
1715                    StringValue::from("base error")
1716                ]
1717                .into()
1718            )
1719        );
1720    }
1721
1722    #[test]
1723    fn propagates_no_error_fields_from_event_to_span() {
1724        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1725        let subscriber = tracing_subscriber::registry().with(
1726            layer()
1727                .with_error_fields_to_exceptions(false)
1728                .with_tracer(tracer.clone()),
1729        );
1730
1731        let err = TestDynError::new("base error")
1732            .with_parent("intermediate error")
1733            .with_parent("user error");
1734
1735        tracing::subscriber::with_default(subscriber, || {
1736            let _guard = tracing::debug_span!("request",).entered();
1737
1738            tracing::error!(
1739                error = &err as &(dyn std::error::Error + 'static),
1740                "request error!"
1741            )
1742        });
1743
1744        let attributes = tracer
1745            .0
1746            .lock()
1747            .unwrap()
1748            .as_ref()
1749            .unwrap()
1750            .builder
1751            .attributes
1752            .as_ref()
1753            .unwrap()
1754            .clone();
1755
1756        let key_values = attributes
1757            .into_iter()
1758            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1759            .collect::<HashMap<_, _>>();
1760
1761        assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1762        assert_eq!(
1763            key_values[FIELD_EXCEPTION_STACKTRACE],
1764            Value::Array(
1765                vec![
1766                    StringValue::from("intermediate error"),
1767                    StringValue::from("base error")
1768                ]
1769                .into()
1770            )
1771        );
1772    }
1773
1774    #[test]
1775    fn tracks_dropped_events() {
1776        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1777        let subscriber = tracing_subscriber::registry()
1778            .with(layer().with_tracer(tracer.clone()).max_events_per_span(4));
1779        let subscriber = Arc::new(subscriber);
1780
1781        tracing::subscriber::with_default(subscriber.clone(), || {
1782            let span = tracing::debug_span!("request");
1783            let _span_guard = span.enter();
1784
1785            // we should record all 4 events
1786            tracing::info!("event 1");
1787            tracing::info!("event 2");
1788            tracing::info!("event 3");
1789            tracing::info!("event 4");
1790
1791            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1792            let extensions = data.extensions();
1793
1794            // we shouldn't have recoreded any dropped events
1795            assert!(extensions.get::<DroppedOtelEvents>().is_none());
1796
1797            // we should have all 4 events
1798            let otel_data = extensions.get::<OtelData>().unwrap();
1799            let otel_events = otel_data.builder.events.as_ref().unwrap();
1800            assert_eq!(otel_events.len(), 4);
1801
1802            drop(extensions);
1803            drop(data);
1804
1805            // record a 5th event, which is over our max of 4
1806            tracing::info!("event 5");
1807
1808            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1809            let extensions = data.extensions();
1810
1811            // since we're over our limit we should start dropping events
1812            let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1813            assert_eq!(dropped_events.dropped_count, 3);
1814
1815            let otel_data = extensions.get::<OtelData>().unwrap();
1816            let otel_events = otel_data.builder.events.as_ref().unwrap();
1817            // dropped 3, pushed one more
1818            assert_eq!(otel_events.len(), 2);
1819
1820            drop(extensions);
1821            drop(data);
1822
1823            // record many events!
1824            for i in 6..99 {
1825                tracing::info!("event {}", i);
1826            }
1827
1828            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1829            let extensions = data.extensions();
1830
1831            // we should have dropped a ton of events, and recorded that we did
1832            let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1833            assert_eq!(dropped_events.dropped_count, 96);
1834
1835            let otel_data = extensions.get::<OtelData>().unwrap();
1836            let otel_events = otel_data.builder.events.as_ref().unwrap();
1837            assert_eq!(otel_events.len(), 2);
1838
1839            // even though we pushed a ton of events, the capacity of our buffer
1840            // should still be small
1841            assert_eq!(otel_events.capacity(), 8);
1842        });
1843
1844        // on close we should report the number of events we dropped
1845        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1846        let attributes = attributes
1847            .into_iter()
1848            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1849            .collect::<HashMap<_, _>>();
1850
1851        let dropped_count_value = attributes.get("dropped_event_count").unwrap();
1852        let dropped_count: i64 = dropped_count_value.as_str().parse().unwrap();
1853
1854        assert_eq!(dropped_count, 96);
1855    }
1856
1857    #[test]
1858    fn max_events_per_span_zero() {
1859        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1860        let subscriber = tracing_subscriber::registry()
1861            .with(layer().with_tracer(tracer.clone()).max_events_per_span(0));
1862        let subscriber = Arc::new(subscriber);
1863
1864        tracing::subscriber::with_default(subscriber.clone(), || {
1865            let span = tracing::debug_span!("request");
1866            let _span_guard = span.enter();
1867
1868            // record one event
1869            tracing::info!("event 1");
1870
1871            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1872            let extensions = data.extensions();
1873
1874            // it should immediately get dropped
1875            let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1876            assert_eq!(dropped_events.dropped_count, 1);
1877
1878            let otel_data = extensions.get::<OtelData>().unwrap();
1879            let otel_events = otel_data.builder.events.as_ref().unwrap();
1880            assert!(otel_events.is_empty());
1881        });
1882
1883        // on close we should report the number of events we dropped
1884        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1885        let attributes = attributes
1886            .into_iter()
1887            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1888            .collect::<HashMap<_, _>>();
1889
1890        let dropped_count_value = attributes.get("dropped_event_count").unwrap();
1891        let dropped_count: i64 = dropped_count_value.as_str().parse().unwrap();
1892
1893        assert_eq!(dropped_count, 1);
1894    }
1895
1896    #[test]
1897    fn exclude_max_events_per_span() {
1898        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1899        let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1900        let subscriber = Arc::new(subscriber);
1901
1902        tracing::subscriber::with_default(subscriber.clone(), || {
1903            let span = tracing::debug_span!("request");
1904            let _span_guard = span.enter();
1905
1906            // record one event
1907            tracing::info!("event 1");
1908
1909            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1910            let extensions = data.extensions();
1911
1912            // it should immediately get dropped
1913            assert!(extensions.get::<DroppedOtelEvents>().is_none());
1914        });
1915
1916        // on close we should not report the number of events we dropped, because no max was set
1917        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1918        let attributes = attributes
1919            .into_iter()
1920            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1921            .collect::<HashMap<_, _>>();
1922
1923        assert!(!attributes.contains_key("dropped_event_count"));
1924    }
1925
1926    #[test]
1927    fn max_events_per_span_one() {
1928        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1929        let subscriber = tracing_subscriber::registry()
1930            .with(layer().with_tracer(tracer.clone()).max_events_per_span(1));
1931        let subscriber = Arc::new(subscriber);
1932
1933        tracing::subscriber::with_default(subscriber.clone(), || {
1934            let span = tracing::debug_span!("request");
1935            let _span_guard = span.enter();
1936
1937            // record one event
1938            tracing::info!("event 1");
1939
1940            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1941            let extensions = data.extensions();
1942
1943            // we shouldn't have recoreded any dropped events
1944            assert!(extensions.get::<DroppedOtelEvents>().is_none());
1945
1946            // we should have the event
1947            let otel_data = extensions.get::<OtelData>().unwrap();
1948            let otel_events = otel_data.builder.events.as_ref().unwrap();
1949            assert_eq!(otel_events.len(), 1);
1950
1951            drop(extensions);
1952            drop(data);
1953
1954            // record one more, we should drop the original
1955            tracing::info!("event 2");
1956
1957            let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1958            let extensions = data.extensions();
1959
1960            // we shouldn't have recoreded any dropped events
1961            let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1962            assert_eq!(dropped_events.dropped_count, 1);
1963
1964            // we should have just one event
1965            let otel_data = extensions.get::<OtelData>().unwrap();
1966            let otel_events = otel_data.builder.events.as_ref().unwrap();
1967            assert_eq!(otel_events.len(), 1);
1968        });
1969
1970        // on close we should report the number of events we dropped
1971        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1972        let attributes = attributes
1973            .into_iter()
1974            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1975            .collect::<HashMap<_, _>>();
1976
1977        let dropped_count_value = attributes.get("dropped_event_count").unwrap();
1978        let dropped_count: i64 = dropped_count_value.as_str().parse().unwrap();
1979
1980        assert_eq!(dropped_count, 1);
1981    }
1982
1983    #[test]
1984    fn max_events_enabled_not_exceeding_max() {
1985        let tracer = TestTracer(Arc::new(Mutex::new(None)));
1986        let subscriber = tracing_subscriber::registry()
1987            .with(layer().with_tracer(tracer.clone()).max_events_per_span(32));
1988        let subscriber = Arc::new(subscriber);
1989
1990        tracing::subscriber::with_default(subscriber.clone(), || {
1991            tracing::debug_span!("request");
1992        });
1993
1994        // we didn't exceed our max, so we shouldn't report anything
1995        let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1996        let attributes = attributes
1997            .into_iter()
1998            .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1999            .collect::<HashMap<_, _>>();
2000
2001        assert!(!attributes.contains_key("dropped_event_count"));
2002    }
2003
2004    #[test]
2005    fn tracing_error_compatibility() {
2006        let tracer = TestTracer(Arc::new(Mutex::new(None)));
2007        let subscriber = tracing_subscriber::registry()
2008            .with(
2009                layer()
2010                    .with_error_fields_to_exceptions(false)
2011                    .with_tracer(tracer.clone()),
2012            )
2013            .with(tracing_error::ErrorLayer::default());
2014
2015        tracing::subscriber::with_default(subscriber, || {
2016            let span = tracing::info_span!("Blows up!", exception = tracing::field::Empty);
2017            let _entered = span.enter();
2018            let context = tracing_error::SpanTrace::capture();
2019
2020            // This can cause a deadlock if `on_record` locks extensions while attributes are visited
2021            span.record("exception", &tracing::field::debug(&context));
2022            // This can cause a deadlock if `on_event` locks extensions while the event is visited
2023            tracing::info!(exception = &tracing::field::debug(&context), "hello");
2024        });
2025
2026        // No need to assert anything, as long as this finished (and did not panic), everything is ok.
2027    }
2028}