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}