opentelemetry_sdk/trace/
provider.rs

1use crate::error::{OTelSdkError, OTelSdkResult};
2/// # Trace Provider SDK
3///
4/// The `TracerProvider` handles the creation and management of [`Tracer`] instances and coordinates
5/// span processing. It serves as the central configuration point for tracing, ensuring consistency
6/// across all [`Tracer`] instances it creates.
7///
8/// ## Tracer Creation
9///
10/// New [`Tracer`] instances are always created through a `TracerProvider`. These `Tracer`s share
11/// a common configuration, which includes the [`Resource`], span processors, sampling strategies,
12/// and span limits. This avoids the need for each `Tracer` to maintain its own version of these
13/// configurations, ensuring uniform behavior across all instances.
14///
15/// ## Cloning and Shutdown
16///
17/// The `TracerProvider` is designed to be clonable. Cloning a `TracerProvider`  creates a
18/// new reference to the same provider, not a new instance. Dropping the last reference
19/// to the `TracerProvider` will automatically trigger its shutdown. During shutdown, the provider
20/// will flush all remaining spans, ensuring they are passed to the configured processors.
21/// Users can also manually trigger shutdown using the [`shutdown`](TracerProvider::shutdown)
22/// method, which will ensure the same behavior.
23///
24/// Once shut down, the `TracerProvider` transitions into a disabled state. In this state, further
25/// operations on its associated `Tracer` instances will result in no-ops, ensuring that no spans
26/// are processed or exported after shutdown.
27///
28/// ## Span Processing and Force Flush
29///
30/// The `TracerProvider` manages the lifecycle of span processors, which are responsible for
31/// collecting, processing, and exporting spans. The [`force_flush`](TracerProvider::force_flush) method
32/// invoked at any time will trigger an immediate flush of all pending spans (if any) to the exporters.
33/// This will block the user thread till all the spans are passed to exporters.
34///
35/// # Examples
36///
37/// ```
38/// use opentelemetry::global;
39/// use opentelemetry_sdk::trace::SdkTracerProvider;
40/// use opentelemetry::trace::Tracer;
41///
42/// fn init_tracing() -> TracerProvider {
43///     let provider = TracerProvider::default();
44///
45///     // Set the provider to be used globally
46///     let _ = global::set_tracer_provider(provider.clone());
47///
48///     provider
49/// }
50///
51/// fn main() {
52///     let provider = init_tracing();
53///
54///     // create tracer..
55///     let tracer = global::tracer("example/client");
56///
57///     // create span...
58///     let span = tracer
59///         .span_builder("test_span")
60///         .start(&tracer);
61///
62///     // Explicitly shut down the provider
63///     provider.shutdown();
64/// }
65/// ```
66use crate::trace::{
67    BatchSpanProcessor, Config, RandomIdGenerator, Sampler, SdkTracer, SimpleSpanProcessor,
68    SpanLimits,
69};
70use crate::Resource;
71use crate::{trace::SpanExporter, trace::SpanProcessor};
72use opentelemetry::otel_debug;
73use opentelemetry::{otel_info, InstrumentationScope};
74use std::borrow::Cow;
75use std::sync::atomic::{AtomicBool, Ordering};
76use std::sync::{Arc, OnceLock};
77
78use super::IdGenerator;
79
80static PROVIDER_RESOURCE: OnceLock<Resource> = OnceLock::new();
81
82// a no nop tracer provider used as placeholder when the provider is shutdown
83// TODO Replace with LazyLock once it is stable
84static NOOP_TRACER_PROVIDER: OnceLock<SdkTracerProvider> = OnceLock::new();
85#[inline]
86fn noop_tracer_provider() -> &'static SdkTracerProvider {
87    NOOP_TRACER_PROVIDER.get_or_init(|| {
88        SdkTracerProvider {
89            inner: Arc::new(TracerProviderInner {
90                processors: Vec::new(),
91                config: Config {
92                    // cannot use default here as the default resource is not empty
93                    sampler: Box::new(Sampler::ParentBased(Box::new(Sampler::AlwaysOn))),
94                    id_generator: Box::<RandomIdGenerator>::default(),
95                    span_limits: SpanLimits::default(),
96                    resource: Cow::Owned(Resource::empty()),
97                },
98                is_shutdown: AtomicBool::new(true),
99            }),
100        }
101    })
102}
103
104/// TracerProvider inner type
105#[derive(Debug)]
106pub(crate) struct TracerProviderInner {
107    processors: Vec<Box<dyn SpanProcessor>>,
108    config: crate::trace::Config,
109    is_shutdown: AtomicBool,
110}
111
112impl TracerProviderInner {
113    /// Crate-private shutdown method to be called both from explicit shutdown
114    /// and from Drop when the last reference is released.
115    pub(crate) fn shutdown(&self) -> Vec<OTelSdkResult> {
116        let mut results = vec![];
117        for processor in &self.processors {
118            let result = processor.shutdown();
119            if let Err(err) = &result {
120                // Log at debug level because:
121                //  - The error is also returned to the user for handling (if applicable)
122                //  - Or the error occurs during `TracerProviderInner::Drop` as part of telemetry shutdown,
123                //    which is non-actionable by the user
124                otel_debug!(name: "TracerProvider.Drop.ShutdownError",
125                        error = format!("{err}"));
126            }
127            results.push(result);
128        }
129        results
130    }
131}
132
133impl Drop for TracerProviderInner {
134    fn drop(&mut self) {
135        if !self.is_shutdown.load(Ordering::Relaxed) {
136            let _ = self.shutdown(); // errors are handled within shutdown
137        } else {
138            otel_debug!(
139                name: "TracerProvider.Drop.AlreadyShutdown",
140                message = "TracerProvider was already shut down; drop will not attempt shutdown again."
141            );
142        }
143    }
144}
145
146/// Creator and registry of named [`SdkTracer`] instances.
147///
148/// `TracerProvider` is a container holding pointers to `SpanProcessor` and other components.
149/// Cloning a `TracerProvider` instance and dropping it will not stop span processing. To stop span processing, users
150/// must either call the `shutdown` method explicitly or allow the last reference to the `TracerProvider`
151/// to be dropped. When the last reference is dropped, the shutdown process will be automatically triggered
152/// to ensure proper cleanup.
153#[derive(Clone, Debug)]
154pub struct SdkTracerProvider {
155    inner: Arc<TracerProviderInner>,
156}
157
158impl Default for SdkTracerProvider {
159    fn default() -> Self {
160        SdkTracerProvider::builder().build()
161    }
162}
163
164impl SdkTracerProvider {
165    /// Build a new tracer provider
166    pub(crate) fn new(inner: TracerProviderInner) -> Self {
167        SdkTracerProvider {
168            inner: Arc::new(inner),
169        }
170    }
171
172    /// Create a new [`SdkTracerProvider`] builder.
173    pub fn builder() -> TracerProviderBuilder {
174        TracerProviderBuilder::default()
175    }
176
177    /// Span processors associated with this provider
178    pub(crate) fn span_processors(&self) -> &[Box<dyn SpanProcessor>] {
179        &self.inner.processors
180    }
181
182    /// Config associated with this tracer
183    pub(crate) fn config(&self) -> &crate::trace::Config {
184        &self.inner.config
185    }
186
187    /// true if the provider has been shutdown
188    /// Don't start span or export spans when provider is shutdown
189    pub(crate) fn is_shutdown(&self) -> bool {
190        self.inner.is_shutdown.load(Ordering::Relaxed)
191    }
192
193    /// Force flush all remaining spans in span processors and return results.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// use opentelemetry::global;
199    /// use opentelemetry_sdk::trace::SdkTracerProvider;
200    ///
201    /// fn init_tracing() -> TracerProvider {
202    ///     let provider = TracerProvider::default();
203    ///
204    ///     // Set provider to be used as global tracer provider
205    ///     let _ = global::set_tracer_provider(provider.clone());
206    ///
207    ///     provider
208    /// }
209    ///
210    /// fn main() {
211    ///     let provider = init_tracing();
212    ///
213    ///     // create spans..
214    ///
215    ///     // force all spans to flush
216    ///     for result in provider.force_flush() {
217    ///         if let Err(err) = result {
218    ///             // .. handle flush error
219    ///         }
220    ///     }
221    ///
222    ///     // create more spans..
223    ///
224    ///     // dropping provider ensures all remaining spans are exported
225    ///     drop(provider);
226    /// }
227    /// ```
228    pub fn force_flush(&self) -> OTelSdkResult {
229        let result: Vec<_> = self
230            .span_processors()
231            .iter()
232            .map(|processor| processor.force_flush())
233            .collect();
234        if result.iter().all(|r| r.is_ok()) {
235            Ok(())
236        } else {
237            Err(OTelSdkError::InternalFailure(format!("errs: {:?}", result)))
238        }
239    }
240
241    /// Shuts down the current `TracerProvider`.
242    ///
243    /// Note that shut down doesn't means the TracerProvider has dropped
244    pub fn shutdown(&self) -> OTelSdkResult {
245        if self
246            .inner
247            .is_shutdown
248            .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
249            .is_ok()
250        {
251            // propagate the shutdown signal to processors
252            let results = self.inner.shutdown();
253
254            if results.iter().all(|res| res.is_ok()) {
255                Ok(())
256            } else {
257                Err(OTelSdkError::InternalFailure(format!(
258                    "Shutdown errors: {:?}",
259                    results
260                        .into_iter()
261                        .filter_map(Result::err)
262                        .collect::<Vec<_>>() // Collect only the errors
263                )))
264            }
265        } else {
266            Err(OTelSdkError::AlreadyShutdown)
267        }
268    }
269}
270
271impl opentelemetry::trace::TracerProvider for SdkTracerProvider {
272    /// This implementation of `TracerProvider` produces `Tracer` instances.
273    type Tracer = SdkTracer;
274
275    fn tracer(&self, name: impl Into<Cow<'static, str>>) -> Self::Tracer {
276        let scope = InstrumentationScope::builder(name).build();
277        self.tracer_with_scope(scope)
278    }
279
280    fn tracer_with_scope(&self, scope: InstrumentationScope) -> Self::Tracer {
281        if self.inner.is_shutdown.load(Ordering::Relaxed) {
282            return SdkTracer::new(scope, noop_tracer_provider().clone());
283        }
284        if scope.name().is_empty() {
285            otel_info!(name: "TracerNameEmpty",  message = "Tracer name is empty; consider providing a meaningful name. Tracer will function normally and the provided name will be used as-is.");
286        };
287        SdkTracer::new(scope, self.clone())
288    }
289}
290
291/// Builder for provider attributes.
292#[derive(Debug, Default)]
293pub struct TracerProviderBuilder {
294    processors: Vec<Box<dyn SpanProcessor>>,
295    config: crate::trace::Config,
296}
297
298impl TracerProviderBuilder {
299    /// Adds a [SimpleSpanProcessor] with the configured exporter to the pipeline.
300    ///
301    /// # Arguments
302    ///
303    /// * `exporter` - The exporter to be used by the SimpleSpanProcessor.
304    ///
305    /// # Returns
306    ///
307    /// A new `Builder` instance with the SimpleSpanProcessor added to the pipeline.
308    ///
309    /// Processors are invoked in the order they are added.
310    pub fn with_simple_exporter<T: SpanExporter + 'static>(self, exporter: T) -> Self {
311        let simple = SimpleSpanProcessor::new(Box::new(exporter));
312        self.with_span_processor(simple)
313    }
314
315    /// Adds a [BatchSpanProcessor] with the configured exporter to the pipeline.
316    ///
317    /// # Arguments
318    ///
319    /// * `exporter` - The exporter to be used by the BatchSpanProcessor.
320    ///
321    /// # Returns
322    ///
323    /// A new `Builder` instance with the BatchSpanProcessor added to the pipeline.
324    ///
325    /// Processors are invoked in the order they are added.
326    pub fn with_batch_exporter<T: SpanExporter + 'static>(self, exporter: T) -> Self {
327        let batch = BatchSpanProcessor::builder(exporter).build();
328        self.with_span_processor(batch)
329    }
330
331    /// Adds a custom [SpanProcessor] to the pipeline.
332    ///
333    /// # Arguments
334    ///
335    /// * `processor` - The `SpanProcessor` to be added.
336    ///
337    /// # Returns
338    ///
339    /// A new `Builder` instance with the custom `SpanProcessor` added to the pipeline.
340    ///
341    /// Processors are invoked in the order they are added.
342    pub fn with_span_processor<T: SpanProcessor + 'static>(self, processor: T) -> Self {
343        let mut processors = self.processors;
344        processors.push(Box::new(processor));
345
346        TracerProviderBuilder { processors, ..self }
347    }
348
349    /// The sdk [`crate::trace::Config`] that this provider will use.
350    #[deprecated(
351        since = "0.27.1",
352        note = "Config is becoming a private type. Use Builder::with_{config_name}(resource) instead. ex: Builder::with_resource(resource)"
353    )]
354    pub fn with_config(self, config: crate::trace::Config) -> Self {
355        TracerProviderBuilder { config, ..self }
356    }
357
358    /// Specify the sampler to be used.
359    pub fn with_sampler<T: crate::trace::ShouldSample + 'static>(mut self, sampler: T) -> Self {
360        self.config.sampler = Box::new(sampler);
361        self
362    }
363
364    /// Specify the id generator to be used.
365    pub fn with_id_generator<T: IdGenerator + 'static>(mut self, id_generator: T) -> Self {
366        self.config.id_generator = Box::new(id_generator);
367        self
368    }
369
370    /// Specify the number of events to be recorded per span.
371    pub fn with_max_events_per_span(mut self, max_events: u32) -> Self {
372        self.config.span_limits.max_events_per_span = max_events;
373        self
374    }
375
376    /// Specify the number of attributes to be recorded per span.
377    pub fn with_max_attributes_per_span(mut self, max_attributes: u32) -> Self {
378        self.config.span_limits.max_attributes_per_span = max_attributes;
379        self
380    }
381
382    /// Specify the number of events to be recorded per span.
383    pub fn with_max_links_per_span(mut self, max_links: u32) -> Self {
384        self.config.span_limits.max_links_per_span = max_links;
385        self
386    }
387
388    /// Specify the number of attributes one event can have.
389    pub fn with_max_attributes_per_event(mut self, max_attributes: u32) -> Self {
390        self.config.span_limits.max_attributes_per_event = max_attributes;
391        self
392    }
393
394    /// Specify the number of attributes one link can have.
395    pub fn with_max_attributes_per_link(mut self, max_attributes: u32) -> Self {
396        self.config.span_limits.max_attributes_per_link = max_attributes;
397        self
398    }
399
400    /// Specify all limit via the span_limits
401    pub fn with_span_limits(mut self, span_limits: SpanLimits) -> Self {
402        self.config.span_limits = span_limits;
403        self
404    }
405
406    /// Associates a [Resource] with a [SdkTracerProvider].
407    ///
408    /// This [Resource] represents the entity producing telemetry and is associated
409    /// with all [Tracer]s the [SdkTracerProvider] will create.
410    ///
411    /// By default, if this option is not used, the default [Resource] will be used.
412    ///
413    /// [Tracer]: opentelemetry::trace::Tracer
414    pub fn with_resource(self, resource: Resource) -> Self {
415        TracerProviderBuilder {
416            config: self.config.with_resource(resource),
417            ..self
418        }
419    }
420
421    /// Create a new provider from this configuration.
422    pub fn build(self) -> SdkTracerProvider {
423        let mut config = self.config;
424
425        // Standard config will contain an owned [`Resource`] (either sdk default or use supplied)
426        // we can optimize the common case with a static ref to avoid cloning the underlying
427        // resource data for each span.
428        //
429        // For the uncommon case where there are multiple tracer providers with different resource
430        // configurations, users can optionally provide their own borrowed static resource.
431        if matches!(config.resource, Cow::Owned(_)) {
432            config.resource =
433                match PROVIDER_RESOURCE.get_or_init(|| config.resource.clone().into_owned()) {
434                    static_resource if *static_resource == *config.resource.as_ref() => {
435                        Cow::Borrowed(static_resource)
436                    }
437                    _ => config.resource, // Use the new resource if different
438                };
439        }
440
441        // Create a new vector to hold the modified processors
442        let mut processors = self.processors;
443
444        // Set the resource for each processor
445        for p in &mut processors {
446            p.set_resource(config.resource.as_ref());
447        }
448
449        let is_shutdown = AtomicBool::new(false);
450        SdkTracerProvider::new(TracerProviderInner {
451            processors,
452            config,
453            is_shutdown,
454        })
455    }
456}
457
458#[cfg(test)]
459mod tests {
460    use crate::error::{OTelSdkError, OTelSdkResult};
461    use crate::resource::{
462        SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION,
463    };
464    use crate::trace::provider::TracerProviderInner;
465    use crate::trace::SpanData;
466    use crate::trace::{Config, Span, SpanProcessor};
467    use crate::Resource;
468    use opentelemetry::trace::{Tracer, TracerProvider};
469    use opentelemetry::{Context, Key, KeyValue, Value};
470
471    use std::env;
472    use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
473    use std::sync::Arc;
474
475    // fields below is wrapped with Arc so we can assert it
476    #[derive(Default, Debug)]
477    struct AssertInfo {
478        started_span: AtomicU32,
479        is_shutdown: AtomicBool,
480    }
481
482    #[derive(Default, Debug, Clone)]
483    struct SharedAssertInfo(Arc<AssertInfo>);
484
485    impl SharedAssertInfo {
486        fn started_span_count(&self, count: u32) -> bool {
487            self.0.started_span.load(Ordering::SeqCst) == count
488        }
489    }
490
491    #[derive(Debug)]
492    struct TestSpanProcessor {
493        success: bool,
494        assert_info: SharedAssertInfo,
495    }
496
497    impl TestSpanProcessor {
498        fn new(success: bool) -> TestSpanProcessor {
499            TestSpanProcessor {
500                success,
501                assert_info: SharedAssertInfo::default(),
502            }
503        }
504
505        // get handle to assert info
506        fn assert_info(&self) -> SharedAssertInfo {
507            self.assert_info.clone()
508        }
509    }
510
511    impl SpanProcessor for TestSpanProcessor {
512        fn on_start(&self, _span: &mut Span, _cx: &Context) {
513            self.assert_info
514                .0
515                .started_span
516                .fetch_add(1, Ordering::SeqCst);
517        }
518
519        fn on_end(&self, _span: SpanData) {
520            // ignore
521        }
522
523        fn force_flush(&self) -> OTelSdkResult {
524            if self.success {
525                Ok(())
526            } else {
527                Err(OTelSdkError::InternalFailure("cannot export".into()))
528            }
529        }
530
531        fn shutdown(&self) -> OTelSdkResult {
532            if self.assert_info.0.is_shutdown.load(Ordering::SeqCst) {
533                Ok(())
534            } else {
535                let _ = self.assert_info.0.is_shutdown.compare_exchange(
536                    false,
537                    true,
538                    Ordering::SeqCst,
539                    Ordering::SeqCst,
540                );
541                self.force_flush()
542            }
543        }
544    }
545
546    #[test]
547    fn test_force_flush() {
548        let tracer_provider = super::SdkTracerProvider::new(TracerProviderInner {
549            processors: vec![
550                Box::from(TestSpanProcessor::new(true)),
551                Box::from(TestSpanProcessor::new(false)),
552            ],
553            config: Default::default(),
554            is_shutdown: AtomicBool::new(false),
555        });
556
557        let results = tracer_provider.force_flush();
558        assert!(results.is_err());
559    }
560
561    #[test]
562    fn test_tracer_provider_default_resource() {
563        let assert_resource = |provider: &super::SdkTracerProvider,
564                               resource_key: &'static str,
565                               expect: Option<&'static str>| {
566            assert_eq!(
567                provider
568                    .config()
569                    .resource
570                    .get(&Key::from_static_str(resource_key))
571                    .map(|v| v.to_string()),
572                expect.map(|s| s.to_string())
573            );
574        };
575        let assert_telemetry_resource = |provider: &super::SdkTracerProvider| {
576            assert_eq!(
577                provider
578                    .config()
579                    .resource
580                    .get(&TELEMETRY_SDK_LANGUAGE.into()),
581                Some(Value::from("rust"))
582            );
583            assert_eq!(
584                provider.config().resource.get(&TELEMETRY_SDK_NAME.into()),
585                Some(Value::from("opentelemetry"))
586            );
587            assert_eq!(
588                provider
589                    .config()
590                    .resource
591                    .get(&TELEMETRY_SDK_VERSION.into()),
592                Some(Value::from(env!("CARGO_PKG_VERSION")))
593            );
594        };
595
596        // If users didn't provide a resource and there isn't a env var set. Use default one.
597        temp_env::with_var_unset("OTEL_RESOURCE_ATTRIBUTES", || {
598            let default_config_provider = super::SdkTracerProvider::builder().build();
599            assert_resource(
600                &default_config_provider,
601                SERVICE_NAME,
602                Some("unknown_service"),
603            );
604            assert_telemetry_resource(&default_config_provider);
605        });
606
607        // If user provided config, use that.
608        let custom_config_provider = super::SdkTracerProvider::builder()
609            .with_resource(
610                Resource::builder_empty()
611                    .with_service_name("test_service")
612                    .build(),
613            )
614            .build();
615        assert_resource(&custom_config_provider, SERVICE_NAME, Some("test_service"));
616        assert_eq!(custom_config_provider.config().resource.len(), 1);
617
618        // If `OTEL_RESOURCE_ATTRIBUTES` is set, read them automatically
619        temp_env::with_var(
620            "OTEL_RESOURCE_ATTRIBUTES",
621            Some("key1=value1, k2, k3=value2"),
622            || {
623                let env_resource_provider = super::SdkTracerProvider::builder().build();
624                assert_resource(
625                    &env_resource_provider,
626                    SERVICE_NAME,
627                    Some("unknown_service"),
628                );
629                assert_resource(&env_resource_provider, "key1", Some("value1"));
630                assert_resource(&env_resource_provider, "k3", Some("value2"));
631                assert_telemetry_resource(&env_resource_provider);
632                assert_eq!(env_resource_provider.config().resource.len(), 6);
633            },
634        );
635
636        // When `OTEL_RESOURCE_ATTRIBUTES` is set and also user provided config
637        temp_env::with_var(
638            "OTEL_RESOURCE_ATTRIBUTES",
639            Some("my-custom-key=env-val,k2=value2"),
640            || {
641                let user_provided_resource_config_provider = super::SdkTracerProvider::builder()
642                    .with_resource(
643                        Resource::builder()
644                            .with_attributes([
645                                KeyValue::new("my-custom-key", "my-custom-value"),
646                                KeyValue::new("my-custom-key2", "my-custom-value2"),
647                            ])
648                            .build(),
649                    )
650                    .build();
651                assert_resource(
652                    &user_provided_resource_config_provider,
653                    SERVICE_NAME,
654                    Some("unknown_service"),
655                );
656                assert_resource(
657                    &user_provided_resource_config_provider,
658                    "my-custom-key",
659                    Some("my-custom-value"),
660                );
661                assert_resource(
662                    &user_provided_resource_config_provider,
663                    "my-custom-key2",
664                    Some("my-custom-value2"),
665                );
666                assert_resource(
667                    &user_provided_resource_config_provider,
668                    "k2",
669                    Some("value2"),
670                );
671                assert_telemetry_resource(&user_provided_resource_config_provider);
672                assert_eq!(
673                    user_provided_resource_config_provider
674                        .config()
675                        .resource
676                        .len(),
677                    7
678                );
679            },
680        );
681
682        // If user provided a resource, it takes priority during collision.
683        let no_service_name = super::SdkTracerProvider::builder()
684            .with_resource(Resource::empty())
685            .build();
686
687        assert_eq!(no_service_name.config().resource.len(), 0)
688    }
689
690    #[test]
691    fn test_shutdown_noops() {
692        let processor = TestSpanProcessor::new(false);
693        let assert_handle = processor.assert_info();
694        let tracer_provider = super::SdkTracerProvider::new(TracerProviderInner {
695            processors: vec![Box::from(processor)],
696            config: Default::default(),
697            is_shutdown: AtomicBool::new(false),
698        });
699
700        let test_tracer_1 = tracer_provider.tracer("test1");
701        let _ = test_tracer_1.start("test");
702
703        assert!(assert_handle.started_span_count(1));
704
705        let _ = test_tracer_1.start("test");
706
707        assert!(assert_handle.started_span_count(2));
708
709        let shutdown = |tracer_provider: super::SdkTracerProvider| {
710            let _ = tracer_provider.shutdown(); // shutdown once
711        };
712
713        // assert tracer provider can be shutdown using on a cloned version
714        shutdown(tracer_provider.clone());
715
716        // after shutdown we should get noop tracer
717        let noop_tracer = tracer_provider.tracer("noop");
718
719        // noop tracer cannot start anything
720        let _ = noop_tracer.start("test");
721        assert!(assert_handle.started_span_count(2));
722        // noop tracer's tracer provider should be shutdown
723        assert!(noop_tracer.provider().is_shutdown());
724
725        // existing tracer becomes noop after shutdown
726        let _ = test_tracer_1.start("test");
727        assert!(assert_handle.started_span_count(2));
728
729        // also existing tracer's tracer provider are in shutdown state
730        assert!(test_tracer_1.provider().is_shutdown());
731    }
732
733    #[derive(Debug)]
734    struct CountingShutdownProcessor {
735        shutdown_count: Arc<AtomicU32>,
736    }
737
738    impl CountingShutdownProcessor {
739        fn new(shutdown_count: Arc<AtomicU32>) -> Self {
740            CountingShutdownProcessor { shutdown_count }
741        }
742    }
743
744    impl SpanProcessor for CountingShutdownProcessor {
745        fn on_start(&self, _span: &mut Span, _cx: &Context) {
746            // No operation needed for this processor
747        }
748
749        fn on_end(&self, _span: SpanData) {
750            // No operation needed for this processor
751        }
752
753        fn force_flush(&self) -> OTelSdkResult {
754            Ok(())
755        }
756
757        fn shutdown(&self) -> OTelSdkResult {
758            self.shutdown_count.fetch_add(1, Ordering::SeqCst);
759            Ok(())
760        }
761    }
762
763    #[test]
764    fn drop_test_with_multiple_providers() {
765        let shutdown_count = Arc::new(AtomicU32::new(0));
766
767        {
768            // Create a shared TracerProviderInner and use it across multiple providers
769            let shared_inner = Arc::new(TracerProviderInner {
770                processors: vec![Box::new(CountingShutdownProcessor::new(
771                    shutdown_count.clone(),
772                ))],
773                config: Config::default(),
774                is_shutdown: AtomicBool::new(false),
775            });
776
777            {
778                let tracer_provider1 = super::SdkTracerProvider {
779                    inner: shared_inner.clone(),
780                };
781                let tracer_provider2 = super::SdkTracerProvider {
782                    inner: shared_inner.clone(),
783                };
784
785                let tracer1 = tracer_provider1.tracer("test-tracer1");
786                let tracer2 = tracer_provider2.tracer("test-tracer2");
787
788                let _span1 = tracer1.start("span1");
789                let _span2 = tracer2.start("span2");
790
791                // TracerProviderInner should not be dropped yet, since both providers and `shared_inner`
792                // are still holding a reference.
793            }
794            // At this point, both `tracer_provider1` and `tracer_provider2` are dropped,
795            // but `shared_inner` still holds a reference, so `TracerProviderInner` is NOT dropped yet.
796            assert_eq!(shutdown_count.load(Ordering::SeqCst), 0);
797        }
798        // Verify shutdown was called during the drop of the shared TracerProviderInner
799        assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
800    }
801
802    #[test]
803    fn drop_after_shutdown_test_with_multiple_providers() {
804        let shutdown_count = Arc::new(AtomicU32::new(0));
805
806        // Create a shared TracerProviderInner and use it across multiple providers
807        let shared_inner = Arc::new(TracerProviderInner {
808            processors: vec![Box::new(CountingShutdownProcessor::new(
809                shutdown_count.clone(),
810            ))],
811            config: Config::default(),
812            is_shutdown: AtomicBool::new(false),
813        });
814
815        // Create a scope to test behavior when providers are dropped
816        {
817            let tracer_provider1 = super::SdkTracerProvider {
818                inner: shared_inner.clone(),
819            };
820            let tracer_provider2 = super::SdkTracerProvider {
821                inner: shared_inner.clone(),
822            };
823
824            // Explicitly shut down the tracer provider
825            let shutdown_result = tracer_provider1.shutdown();
826            assert!(shutdown_result.is_ok());
827
828            // Verify that shutdown was called exactly once
829            assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
830
831            // TracerProvider2 should observe the shutdown state but not trigger another shutdown
832            let shutdown_result2 = tracer_provider2.shutdown();
833            assert!(shutdown_result2.is_err());
834            assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
835
836            // Both tracer providers will be dropped at the end of this scope
837        }
838
839        // Verify that shutdown was only called once, even after drop
840        assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
841    }
842}