opentelemetry_spanprocessor_any/global/
trace.rs

1use crate::global::handle_error;
2use crate::trace::{noop::NoopTracerProvider, SpanContext, StatusCode, TraceResult};
3use crate::{trace, trace::TracerProvider, Context, KeyValue};
4use std::borrow::Cow;
5use std::fmt;
6use std::mem;
7use std::sync::{Arc, RwLock};
8use std::time::SystemTime;
9
10pub trait ObjectSafeSpan {
11    /// An API to record events at a specific time in the context of a given `Span`.
12    ///
13    /// Events SHOULD preserve the order in which they're set. This will typically match
14    /// the ordering of the events' timestamps.
15    ///
16    /// Note that the OpenTelemetry project documents certain ["standard event names and
17    /// keys"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
18    /// which have prescribed semantic meanings.
19    fn add_event_with_timestamp(
20        &mut self,
21        name: Cow<'static, str>,
22        timestamp: SystemTime,
23        attributes: Vec<KeyValue>,
24    );
25
26    /// Returns the `SpanContext` for the given `Span`. The returned value may be used even after
27    /// the `Span is finished. The returned value MUST be the same for the entire `Span` lifetime.
28    fn span_context(&self) -> &SpanContext;
29
30    /// Returns true if this `Span` is recording information like events with the `add_event`
31    /// operation, attributes using `set_attributes`, status with `set_status`, etc.
32    ///
33    /// This flag SHOULD be used to avoid expensive computations of a `Span` attributes or events in
34    /// case when a `Span` is definitely not recorded. Note that any child span's recording is
35    /// determined independently from the value of this flag (typically based on the sampled flag of
36    /// a `TraceFlag` on `SpanContext`).
37    ///
38    /// This flag may be true despite the entire trace being sampled out. This allows to record and
39    /// process information about the individual Span without sending it to the backend. An example
40    /// of this scenario may be recording and processing of all incoming requests for the processing
41    /// and building of SLA/SLO latency charts while sending only a subset - sampled spans - to the
42    /// backend. See also the sampling section of SDK design.
43    ///
44    /// Users of the API should only access the `is_recording` property when instrumenting code and
45    /// never access `SampledFlag` unless used in context propagators.
46    fn is_recording(&self) -> bool;
47
48    /// An API to set a single `Attribute` where the attribute properties are passed
49    /// as arguments. To avoid extra allocations some implementations may offer a separate API for
50    /// each of the possible value types.
51    ///
52    /// An `Attribute` is defined as a `KeyValue` pair.
53    ///
54    /// Attributes SHOULD preserve the order in which they're set. Setting an attribute
55    /// with the same key as an existing attribute SHOULD overwrite the existing
56    /// attribute's value.
57    ///
58    /// Note that the OpenTelemetry project documents certain ["standard
59    /// attributes"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
60    /// that have prescribed semantic meanings.
61    fn set_attribute(&mut self, attribute: KeyValue);
62
63    /// Sets the status of the `Span`. `message` MUST be ignored when the status is `OK` or
64    /// `Unset`.
65    ///
66    /// The order of status is `Ok` > `Error` > `Unset`. That's means set the status
67    /// to `Unset` will always be ignore, set the status to `Error` only works when current
68    /// status is `Unset`, set the status to `Ok` will be consider final and any further call
69    /// to this function will be ignore.
70    fn set_status(&mut self, code: StatusCode, message: String);
71
72    /// Updates the `Span`'s name. After this update, any sampling behavior based on the
73    /// name will depend on the implementation.
74    ///
75    /// It is highly discouraged to update the name of a `Span` after its creation.
76    /// `Span` name is often used to group, filter and identify the logical groups of
77    /// spans. Often, filtering logic will be implemented before the `Span` creation
78    /// for performance reasons, and the name update may interfere with this logic.
79    ///
80    /// The method name is called `update_name` to differentiate this method from the
81    /// regular property. It emphasizes that this operation signifies a
82    /// major change for a `Span` and may lead to re-calculation of sampling or
83    /// filtering decisions made previously depending on the implementation.
84    fn update_name(&mut self, new_name: Cow<'static, str>);
85
86    /// Finishes the `Span`.
87    ///
88    /// Implementations MUST ignore all subsequent calls to `end` (there might be
89    /// exceptions when the tracer is streaming events and has no mutable state
90    /// associated with the Span).
91    ///
92    /// Calls to `end` a Span MUST not have any effects on child `Span`s as they may
93    /// still be running and can be ended later.
94    ///
95    /// This API MUST be non-blocking.
96    fn end(&mut self) {
97        self.end_with_timestamp(crate::time::now());
98    }
99
100    /// Finishes the `Span` with given timestamp
101    ///
102    /// For more details, refer to [`Span::end`]
103    ///
104    /// [`Span::end`]: Span::end()
105    fn end_with_timestamp(&mut self, timestamp: SystemTime);
106}
107
108impl<T: trace::Span> ObjectSafeSpan for T {
109    fn add_event_with_timestamp(
110        &mut self,
111        name: Cow<'static, str>,
112        timestamp: SystemTime,
113        attributes: Vec<KeyValue>,
114    ) {
115        self.add_event_with_timestamp(name, timestamp, attributes)
116    }
117
118    fn span_context(&self) -> &SpanContext {
119        self.span_context()
120    }
121
122    fn is_recording(&self) -> bool {
123        self.is_recording()
124    }
125
126    fn set_attribute(&mut self, attribute: KeyValue) {
127        self.set_attribute(attribute)
128    }
129
130    fn set_status(&mut self, code: StatusCode, message: String) {
131        self.set_status(code, message)
132    }
133
134    fn update_name(&mut self, new_name: Cow<'static, str>) {
135        self.update_name(new_name)
136    }
137
138    fn end_with_timestamp(&mut self, timestamp: SystemTime) {
139        self.end_with_timestamp(timestamp)
140    }
141}
142
143/// Wraps the [`BoxedTracer`]'s [`Span`] so it can be used generically by
144/// applications without knowing the underlying type.
145///
146/// [`Span`]: crate::trace::Span
147pub struct BoxedSpan(Box<dyn ObjectSafeSpan + Send + Sync>);
148
149impl BoxedSpan {
150    pub(crate) fn new<T>(span: T) -> Self
151    where
152        T: ObjectSafeSpan + Send + Sync + 'static,
153    {
154        BoxedSpan(Box::new(span))
155    }
156}
157
158impl fmt::Debug for BoxedSpan {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        f.write_str("BoxedSpan")
161    }
162}
163
164impl trace::Span for BoxedSpan {
165    /// Records events at a specific time in the context of a given `Span`.
166    ///
167    /// Note that the OpenTelemetry project documents certain ["standard event names and
168    /// keys"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
169    /// which have prescribed semantic meanings.
170    fn add_event_with_timestamp<T>(
171        &mut self,
172        name: T,
173        timestamp: SystemTime,
174        attributes: Vec<KeyValue>,
175    ) where
176        T: Into<Cow<'static, str>>,
177    {
178        self.0
179            .add_event_with_timestamp(name.into(), timestamp, attributes)
180    }
181
182    /// Returns the `SpanContext` for the given `Span`.
183    fn span_context(&self) -> &trace::SpanContext {
184        self.0.span_context()
185    }
186
187    /// Returns true if this `Span` is recording information like events with the `add_event`
188    /// operation, attributes using `set_attributes`, status with `set_status`, etc.
189    fn is_recording(&self) -> bool {
190        self.0.is_recording()
191    }
192
193    /// Sets a single `Attribute` where the attribute properties are passed as arguments.
194    ///
195    /// Note that the OpenTelemetry project documents certain ["standard
196    /// attributes"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
197    /// that have prescribed semantic meanings.
198    fn set_attribute(&mut self, attribute: KeyValue) {
199        self.0.set_attribute(attribute)
200    }
201
202    /// Sets the status of the `Span`. If used, this will override the default `Span`
203    /// status, which is `Unset`.
204    fn set_status(&mut self, code: trace::StatusCode, message: String) {
205        self.0.set_status(code, message)
206    }
207
208    /// Updates the `Span`'s name.
209    fn update_name<T>(&mut self, new_name: T)
210    where
211        T: Into<Cow<'static, str>>,
212    {
213        self.0.update_name(new_name.into())
214    }
215
216    /// Finishes the span with given timestamp.
217    fn end_with_timestamp(&mut self, timestamp: SystemTime) {
218        self.0.end_with_timestamp(timestamp);
219    }
220}
221
222/// Wraps the [`GlobalTracerProvider`]'s [`Tracer`] so it can be used generically by
223/// applications without knowing the underlying type.
224///
225/// [`Tracer`]: crate::trace::Tracer
226/// [`GlobalTracerProvider`]: crate::global::GlobalTracerProvider
227pub struct BoxedTracer(Box<dyn ObjectSafeTracer + Send + Sync>);
228
229impl fmt::Debug for BoxedTracer {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        f.write_str("BoxedTracer")
232    }
233}
234
235impl trace::Tracer for BoxedTracer {
236    /// Global tracer uses `BoxedSpan`s so that it can be a global singleton,
237    /// which is not possible if it takes generic type parameters.
238    type Span = BoxedSpan;
239
240    /// Starts a new `Span`.
241    ///
242    /// Each span has zero or one parent spans and zero or more child spans, which
243    /// represent causally related operations. A tree of related spans comprises a
244    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
245    /// trace includes a single root span, which is the shared ancestor of all other
246    /// spans in the trace.
247    fn start_with_context<T>(&self, name: T, parent_cx: &Context) -> Self::Span
248    where
249        T: Into<Cow<'static, str>>,
250    {
251        BoxedSpan(self.0.start_with_context_boxed(name.into(), parent_cx))
252    }
253
254    /// Creates a span builder
255    ///
256    /// An ergonomic way for attributes to be configured before the `Span` is started.
257    fn span_builder<T>(&self, name: T) -> trace::SpanBuilder
258    where
259        T: Into<Cow<'static, str>>,
260    {
261        trace::SpanBuilder::from_name(name)
262    }
263
264    /// Create a span from a `SpanBuilder`
265    fn build_with_context(&self, builder: trace::SpanBuilder, parent_cx: &Context) -> Self::Span {
266        BoxedSpan(self.0.build_with_context_boxed(builder, parent_cx))
267    }
268}
269
270/// Allows a specific [`Tracer`] to be used generically by [`BoxedTracer`]
271/// instances by mirroring the interface and boxing the return types.
272///
273/// [`Tracer`]: crate::trace::Tracer
274pub trait ObjectSafeTracer {
275    /// Returns a trait object so the underlying implementation can be swapped
276    /// out at runtime.
277    fn start_with_context_boxed(
278        &self,
279        name: Cow<'static, str>,
280        parent_cx: &Context,
281    ) -> Box<dyn ObjectSafeSpan + Send + Sync>;
282
283    /// Returns a trait object so the underlying implementation can be swapped
284    /// out at runtime.
285    fn build_with_context_boxed(
286        &self,
287        builder: trace::SpanBuilder,
288        parent_cx: &Context,
289    ) -> Box<dyn ObjectSafeSpan + Send + Sync>;
290}
291
292impl<S, T> ObjectSafeTracer for T
293where
294    S: trace::Span + Send + Sync + 'static,
295    T: trace::Tracer<Span = S>,
296{
297    /// Returns a trait object so the underlying implementation can be swapped
298    /// out at runtime.
299    fn start_with_context_boxed(
300        &self,
301        name: Cow<'static, str>,
302        parent_cx: &Context,
303    ) -> Box<dyn ObjectSafeSpan + Send + Sync> {
304        Box::new(self.start_with_context(name, parent_cx))
305    }
306
307    /// Returns a trait object so the underlying implementation can be swapped
308    /// out at runtime.
309    fn build_with_context_boxed(
310        &self,
311        builder: trace::SpanBuilder,
312        parent_cx: &Context,
313    ) -> Box<dyn ObjectSafeSpan + Send + Sync> {
314        Box::new(self.build_with_context(builder, parent_cx))
315    }
316}
317
318/// Allows a specific [`TracerProvider`] to be used generically by the
319/// [`GlobalTracerProvider`] by mirroring the interface and boxing the return types.
320///
321/// [`TracerProvider`]: crate::trace::TracerProvider
322/// [`GlobalTracerProvider`]: crate::global::GlobalTracerProvider
323pub trait ObjectSafeTracerProvider {
324    /// Creates a versioned named tracer instance that is a trait object through the underlying
325    /// `TracerProvider`.
326    fn versioned_tracer_boxed(
327        &self,
328        name: Cow<'static, str>,
329        version: Option<&'static str>,
330        schema_url: Option<&'static str>,
331    ) -> Box<dyn ObjectSafeTracer + Send + Sync>;
332
333    /// Force flush all remaining spans in span processors and return results.
334    fn force_flush(&self) -> Vec<TraceResult<()>>;
335}
336
337impl<S, T, P> ObjectSafeTracerProvider for P
338where
339    S: trace::Span + Send + Sync + 'static,
340    T: trace::Tracer<Span = S> + Send + Sync + 'static,
341    P: trace::TracerProvider<Tracer = T>,
342{
343    /// Return a versioned boxed tracer
344    fn versioned_tracer_boxed(
345        &self,
346        name: Cow<'static, str>,
347        version: Option<&'static str>,
348        schema_url: Option<&'static str>,
349    ) -> Box<dyn ObjectSafeTracer + Send + Sync> {
350        Box::new(self.versioned_tracer(name, version, schema_url))
351    }
352
353    fn force_flush(&self) -> Vec<TraceResult<()>> {
354        self.force_flush()
355    }
356}
357
358/// Represents the globally configured [`TracerProvider`] instance for this
359/// application. This allows generic tracing through the returned
360/// [`BoxedTracer`] instances.
361///
362/// [`TracerProvider`]: crate::trace::TracerProvider
363#[derive(Clone)]
364pub struct GlobalTracerProvider {
365    provider: Arc<dyn ObjectSafeTracerProvider + Send + Sync>,
366}
367
368impl fmt::Debug for GlobalTracerProvider {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        f.write_str("GlobalTracerProvider")
371    }
372}
373
374impl GlobalTracerProvider {
375    /// Create a new GlobalTracerProvider instance from a struct that implements `TracerProvider`.
376    fn new<P, T, S>(provider: P) -> Self
377    where
378        S: trace::Span + Send + Sync + 'static,
379        T: trace::Tracer<Span = S> + Send + Sync + 'static,
380        P: trace::TracerProvider<Tracer = T> + Send + Sync + 'static,
381    {
382        GlobalTracerProvider {
383            provider: Arc::new(provider),
384        }
385    }
386}
387
388impl trace::TracerProvider for GlobalTracerProvider {
389    type Tracer = BoxedTracer;
390
391    /// Create a versioned tracer using the global provider.
392    fn versioned_tracer(
393        &self,
394        name: impl Into<Cow<'static, str>>,
395        version: Option<&'static str>,
396        schema_url: Option<&'static str>,
397    ) -> Self::Tracer {
398        BoxedTracer(
399            self.provider
400                .versioned_tracer_boxed(name.into(), version, schema_url),
401        )
402    }
403
404    /// Force flush all remaining spans in span processors and return results.
405    fn force_flush(&self) -> Vec<TraceResult<()>> {
406        self.provider.force_flush()
407    }
408}
409
410lazy_static::lazy_static! {
411    /// The global `Tracer` provider singleton.
412    static ref GLOBAL_TRACER_PROVIDER: RwLock<GlobalTracerProvider> = RwLock::new(GlobalTracerProvider::new(trace::noop::NoopTracerProvider::new()));
413}
414
415/// Returns an instance of the currently configured global [`TracerProvider`] through
416/// [`GlobalTracerProvider`].
417///
418/// [`TracerProvider`]: crate::trace::TracerProvider
419/// [`GlobalTracerProvider`]: crate::global::GlobalTracerProvider
420pub fn tracer_provider() -> GlobalTracerProvider {
421    GLOBAL_TRACER_PROVIDER
422        .read()
423        .expect("GLOBAL_TRACER_PROVIDER RwLock poisoned")
424        .clone()
425}
426
427/// Creates a named instance of [`Tracer`] via the configured [`GlobalTracerProvider`].
428///
429/// If the name is an empty string, the provider will use a default name.
430///
431/// This is a more convenient way of expressing `global::tracer_provider().tracer(name)`.
432///
433/// [`Tracer`]: crate::trace::Tracer
434pub fn tracer(name: impl Into<Cow<'static, str>>) -> BoxedTracer {
435    tracer_provider().tracer(name.into())
436}
437
438/// Sets the given [`TracerProvider`] instance as the current global provider.
439///
440/// It returns the [`TracerProvider`] instance that was previously mounted as global provider
441/// (e.g. [`NoopTracerProvider`] if a provider had not been set before).
442///
443/// [`TracerProvider`]: crate::trace::TracerProvider
444pub fn set_tracer_provider<P, T, S>(new_provider: P) -> GlobalTracerProvider
445where
446    S: trace::Span + Send + Sync + 'static,
447    T: trace::Tracer<Span = S> + Send + Sync + 'static,
448    P: trace::TracerProvider<Tracer = T> + Send + Sync + 'static,
449{
450    let mut tracer_provider = GLOBAL_TRACER_PROVIDER
451        .write()
452        .expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
453    mem::replace(
454        &mut *tracer_provider,
455        GlobalTracerProvider::new(new_provider),
456    )
457}
458
459/// Shut down the current tracer provider. This will invoke the shutdown method on all span processors.
460/// span processors should export remaining spans before return
461pub fn shutdown_tracer_provider() {
462    let mut tracer_provider = GLOBAL_TRACER_PROVIDER
463        .write()
464        .expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
465
466    let _ = mem::replace(
467        &mut *tracer_provider,
468        GlobalTracerProvider::new(NoopTracerProvider::new()),
469    );
470}
471
472/// Force flush all remaining spans in span processors.
473///
474/// Use the [`global::handle_error`] to handle errors happened during force flush.
475///
476/// [`global::handle_error`]: crate::global::handle_error
477pub fn force_flush_tracer_provider() {
478    let tracer_provider = GLOBAL_TRACER_PROVIDER
479        .write()
480        .expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
481
482    let results = trace::TracerProvider::force_flush(&*tracer_provider);
483    for result in results {
484        if let Err(err) = result {
485            handle_error(err)
486        }
487    }
488}
489
490#[cfg(test)]
491// Note that all tests here should be marked as ignore so that it won't be picked up by default We
492// need to run those tests one by one as the GlobalTracerProvider is a shared object between
493// threads Use cargo test -- --ignored --test-threads=1 to run those tests.
494mod tests {
495    use super::*;
496    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
497    use crate::runtime;
498    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
499    use crate::sdk::trace::TraceRuntime;
500    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
501    use crate::trace::Tracer;
502    use std::{fmt::Debug, io::Write, sync::Mutex};
503
504    #[derive(Debug)]
505    struct AssertWriter {
506        buf: Arc<Mutex<Vec<u8>>>,
507    }
508
509    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
510    impl AssertWriter {
511        fn new() -> AssertWriter {
512            AssertWriter {
513                buf: Arc::new(Mutex::new(Vec::new())),
514            }
515        }
516
517        fn len(&self) -> usize {
518            self.buf
519                .lock()
520                .expect("cannot acquire the lock of assert writer")
521                .len()
522        }
523    }
524
525    impl Write for AssertWriter {
526        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
527            let mut buffer = self
528                .buf
529                .lock()
530                .expect("cannot acquire the lock of assert writer");
531            buffer.write(buf)
532        }
533
534        fn flush(&mut self) -> std::io::Result<()> {
535            let mut buffer = self
536                .buf
537                .lock()
538                .expect("cannot acquire the lock of assert writer");
539            buffer.flush()
540        }
541    }
542
543    impl Clone for AssertWriter {
544        fn clone(&self) -> Self {
545            AssertWriter {
546                buf: self.buf.clone(),
547            }
548        }
549    }
550
551    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
552    fn build_batch_tracer_provider<R: TraceRuntime>(
553        assert_writer: AssertWriter,
554        runtime: R,
555    ) -> crate::sdk::trace::TracerProvider {
556        use crate::sdk::trace::TracerProvider;
557        let exporter = crate::sdk::export::trace::stdout::Exporter::new(assert_writer, true);
558        TracerProvider::builder()
559            .with_batch_exporter(exporter, runtime)
560            .build()
561    }
562
563    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
564    fn build_simple_tracer_provider(
565        assert_writer: AssertWriter,
566    ) -> crate::sdk::trace::TracerProvider {
567        use crate::sdk::trace::TracerProvider;
568        let exporter = crate::sdk::export::trace::stdout::Exporter::new(assert_writer, true);
569        TracerProvider::builder()
570            .with_simple_exporter(exporter)
571            .build()
572    }
573
574    #[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
575    async fn test_set_provider_in_tokio<R: TraceRuntime>(runtime: R) -> AssertWriter {
576        let buffer = AssertWriter::new();
577        let _ = set_tracer_provider(build_batch_tracer_provider(buffer.clone(), runtime));
578        let tracer = tracer("opentelemetery");
579
580        tracer.in_span("test", |_cx| {});
581
582        buffer
583    }
584
585    // When using `tokio::spawn` to spawn the worker task in batch processor
586    //
587    // multiple -> no shut down -> not export
588    // multiple -> shut down -> export
589    // single -> no shutdown -> not export
590    // single -> shutdown -> hang forever
591
592    // When using |fut| tokio::task::spawn_blocking(|| futures::executor::block_on(fut))
593    // to spawn the worker task in batch processor
594    //
595    // multiple -> no shutdown -> hang forever
596    // multiple -> shut down -> export
597    // single -> shut down -> export
598    // single -> no shutdown -> hang forever
599
600    // Test if the multiple thread tokio runtime could exit successfully when not force flushing spans
601    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
602    #[ignore = "requires --test-threads=1"]
603    #[cfg(feature = "rt-tokio")]
604    async fn test_set_provider_multiple_thread_tokio() {
605        let assert_writer = test_set_provider_in_tokio(runtime::Tokio).await;
606        assert_eq!(assert_writer.len(), 0);
607    }
608
609    // Test if the multiple thread tokio runtime could exit successfully when force flushing spans
610    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
611    #[ignore = "requires --test-threads=1"]
612    #[cfg(feature = "rt-tokio")]
613    async fn test_set_provider_multiple_thread_tokio_shutdown() {
614        let assert_writer = test_set_provider_in_tokio(runtime::Tokio).await;
615        shutdown_tracer_provider();
616        assert!(assert_writer.len() > 0);
617    }
618
619    // Test use simple processor in single thread tokio runtime.
620    // Expected to see the spans being exported to buffer
621    #[tokio::test]
622    #[ignore = "requires --test-threads=1"]
623    #[cfg(feature = "rt-tokio")]
624    async fn test_set_provider_single_thread_tokio_with_simple_processor() {
625        let assert_writer = AssertWriter::new();
626        let _ = set_tracer_provider(build_simple_tracer_provider(assert_writer.clone()));
627        let tracer = tracer("opentelemetry");
628
629        tracer.in_span("test", |_cx| {});
630
631        shutdown_tracer_provider();
632
633        assert!(assert_writer.len() > 0);
634    }
635
636    // Test if the single thread tokio runtime could exit successfully when not force flushing spans
637    #[tokio::test]
638    #[ignore = "requires --test-threads=1"]
639    #[cfg(feature = "rt-tokio-current-thread")]
640    async fn test_set_provider_single_thread_tokio() {
641        let assert_writer = test_set_provider_in_tokio(runtime::TokioCurrentThread).await;
642        assert_eq!(assert_writer.len(), 0)
643    }
644
645    // Test if the single thread tokio runtime could exit successfully when force flushing spans.
646    #[tokio::test]
647    #[ignore = "requires --test-threads=1"]
648    #[cfg(feature = "rt-tokio-current-thread")]
649    async fn test_set_provider_single_thread_tokio_shutdown() {
650        let assert_writer = test_set_provider_in_tokio(runtime::TokioCurrentThread).await;
651        shutdown_tracer_provider();
652        assert!(assert_writer.len() > 0);
653    }
654}