|
16 | 16 | /// The `TracerProvider` is designed to be lightweight and clonable. Cloning a `TracerProvider` |
17 | 17 | /// creates a new reference to the same provider, not a new instance. Dropping the last reference |
18 | 18 | /// to the `TracerProvider` will automatically trigger its shutdown. During shutdown, the provider |
19 | | -/// will flush all remaining spans for **batch processors**, ensuring they are exported to the configured |
20 | | -/// exporters. However, **simple processors** do not require a flush, as they export spans immediately |
21 | | -/// when they end. Users can also manually trigger shutdown using the [`shutdown`](TracerProvider::shutdown) |
22 | | -/// method, which will ensure the same behavior (flushing for batch processors, but no additional action |
23 | | -/// for simple processors). |
| 19 | +/// will flush all remaining spans, ensuring they are exported to the configured exporters. |
| 20 | +/// Users can also manually trigger shutdown using the [`shutdown`](TracerProvider::shutdown) |
| 21 | +/// method, which will ensure the same behavior. |
24 | 22 | /// |
25 | 23 | /// Once shut down, the `TracerProvider` transitions into a disabled state. In this state, further |
26 | 24 | /// operations on its associated `Tracer` instances will result in no-ops, ensuring that no spans |
|
31 | 29 | /// The `TracerProvider` manages the lifecycle of span processors, which are responsible for |
32 | 30 | /// collecting, processing, and exporting spans. To ensure all spans are processed before shutdown, |
33 | 31 | /// users can call the [`force_flush`](TracerProvider::force_flush) method at any time to trigger |
34 | | -/// an immediate flush of all pending spans for **batch processors** to the exporters. Note that |
35 | | -/// calling [`force_flush`](TracerProvider::force_flush) is optional before shutdown, as `shutdown` |
36 | | -/// will automatically trigger a flush for batch processors, but not for simple processors. |
| 32 | +/// an immediate flush of all pending spans to the exporters. |
37 | 33 | /// |
38 | 34 | /// # Examples |
39 | 35 | /// |
40 | 36 | /// ``` |
41 | 37 | /// use opentelemetry::global; |
42 | 38 | /// use opentelemetry_sdk::trace::TracerProvider; |
| 39 | +/// use opentelemetry::trace::Tracer; |
43 | 40 | /// |
44 | 41 | /// fn init_tracing() -> TracerProvider { |
45 | 42 | /// let provider = TracerProvider::default(); |
|
53 | 50 | /// fn main() { |
54 | 51 | /// let provider = init_tracing(); |
55 | 52 | /// |
56 | | -/// // create spans... |
| 53 | +/// // create tracer.. |
| 54 | +/// let tracer = global::tracer("example/client"); |
57 | 55 | /// |
58 | | -/// // Flush all spans before shutdown (optional for batch processors) |
59 | | -/// for result in provider.force_flush() { |
60 | | -/// if let Err(err) = result { |
61 | | -/// // Handle flush error... |
62 | | -/// } |
63 | | -/// } |
| 56 | +/// // create span... |
| 57 | +/// let span = tracer |
| 58 | +/// .span_builder("test_span") |
| 59 | +/// .start(&tracer); |
64 | 60 | /// |
65 | | -/// // Dropping the provider ensures remaining spans are flushed for batch processors |
66 | | -/// // and shuts down the global tracer provider. |
67 | | -/// drop(provider); |
68 | | -/// global::shutdown_tracer_provider(); |
| 61 | +/// // Explicitly shut down the provider |
| 62 | +/// provider.shutdown(); |
69 | 63 | /// } |
70 | 64 | /// ``` |
71 | 65 | use crate::runtime::RuntimeChannel; |
@@ -249,7 +243,7 @@ impl TracerProvider { |
249 | 243 | Err(TraceError::Other(format!("{errs:?}").into())) |
250 | 244 | } |
251 | 245 | } else { |
252 | | - Err(TraceError::AlreadyShutdown("TracerProvider".to_string())) |
| 246 | + Err(TraceError::AlreadyShutdown) |
253 | 247 | } |
254 | 248 | } |
255 | 249 | } |
@@ -391,6 +385,7 @@ mod tests { |
391 | 385 | use std::env; |
392 | 386 | use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; |
393 | 387 | use std::sync::Arc; |
| 388 | + use std::sync::Mutex; |
394 | 389 |
|
395 | 390 | // fields below is wrapped with Arc so we can assert it |
396 | 391 | #[derive(Default, Debug)] |
@@ -648,57 +643,124 @@ mod tests { |
648 | 643 | assert!(assert_handle.started_span_count(2)); |
649 | 644 | } |
650 | 645 |
|
| 646 | + #[derive(Debug)] |
| 647 | + struct CountingShutdownProcessor { |
| 648 | + shutdown_count: Arc<Mutex<i32>>, |
| 649 | + flush_called: Arc<Mutex<bool>>, |
| 650 | + } |
| 651 | + |
| 652 | + impl CountingShutdownProcessor { |
| 653 | + fn new(shutdown_count: Arc<Mutex<i32>>, flush_called: Arc<Mutex<bool>>) -> Self { |
| 654 | + CountingShutdownProcessor { |
| 655 | + shutdown_count, |
| 656 | + flush_called, |
| 657 | + } |
| 658 | + } |
| 659 | + } |
| 660 | + |
| 661 | + impl SpanProcessor for CountingShutdownProcessor { |
| 662 | + fn on_start(&self, _span: &mut Span, _cx: &Context) { |
| 663 | + // No operation needed for this processor |
| 664 | + } |
| 665 | + |
| 666 | + fn on_end(&self, _span: SpanData) { |
| 667 | + // No operation needed for this processor |
| 668 | + } |
| 669 | + |
| 670 | + fn force_flush(&self) -> TraceResult<()> { |
| 671 | + *self.flush_called.lock().unwrap() = true; |
| 672 | + Ok(()) |
| 673 | + } |
| 674 | + |
| 675 | + fn shutdown(&self) -> TraceResult<()> { |
| 676 | + let mut count = self.shutdown_count.lock().unwrap(); |
| 677 | + *count += 1; |
| 678 | + Ok(()) |
| 679 | + } |
| 680 | + } |
| 681 | + |
651 | 682 | #[test] |
652 | | - fn test_tracer_provider_inner_drop_shutdown() { |
653 | | - // Test 1: Already shutdown case |
| 683 | + fn drop_test_with_multiple_providers() { |
| 684 | + let shutdown_called = Arc::new(Mutex::new(0)); |
| 685 | + let flush_called = Arc::new(Mutex::new(false)); |
| 686 | + |
654 | 687 | { |
655 | | - let processor = TestSpanProcessor::new(true); |
656 | | - let assert_handle = processor.assert_info(); |
657 | | - let provider = super::TracerProvider::new(TracerProviderInner { |
658 | | - processors: vec![Box::from(processor)], |
659 | | - config: Default::default(), |
| 688 | + // Create a shared TracerProviderInner and use it across multiple providers |
| 689 | + let shared_inner = Arc::new(TracerProviderInner { |
| 690 | + processors: vec![Box::new(CountingShutdownProcessor::new( |
| 691 | + shutdown_called.clone(), |
| 692 | + flush_called.clone(), |
| 693 | + ))], |
| 694 | + config: Config::default(), |
660 | 695 | is_shutdown: AtomicBool::new(false), |
661 | 696 | }); |
662 | 697 |
|
663 | | - // Create multiple providers sharing same inner |
664 | | - let provider2 = provider.clone(); |
665 | | - let provider3 = provider.clone(); |
| 698 | + { |
| 699 | + let tracer_provider1 = super::TracerProvider { |
| 700 | + inner: shared_inner.clone(), |
| 701 | + }; |
| 702 | + let tracer_provider2 = super::TracerProvider { |
| 703 | + inner: shared_inner.clone(), |
| 704 | + }; |
666 | 705 |
|
667 | | - // Shutdown explicitly first |
668 | | - assert!(provider.shutdown().is_ok()); |
| 706 | + let tracer1 = tracer_provider1.tracer("test-tracer1"); |
| 707 | + let tracer2 = tracer_provider2.tracer("test-tracer2"); |
669 | 708 |
|
670 | | - // Drop all providers - should not trigger another shutdown in TracerProviderInner::drop |
671 | | - drop(provider); |
672 | | - drop(provider2); |
673 | | - drop(provider3); |
| 709 | + let _span1 = tracer1.start("span1"); |
| 710 | + let _span2 = tracer2.start("span2"); |
674 | 711 |
|
675 | | - // Verify shutdown was called exactly once |
676 | | - assert!(assert_handle.0.is_shutdown.load(Ordering::SeqCst)); |
| 712 | + // TracerProviderInner should not be dropped yet, since both providers and `shared_inner` |
| 713 | + // are still holding a reference. |
| 714 | + } |
| 715 | + // At this point, both `tracer_provider1` and `tracer_provider2` are dropped, |
| 716 | + // but `shared_inner` still holds a reference, so `TracerProviderInner` is NOT dropped yet. |
| 717 | + assert_eq!(*shutdown_called.lock().unwrap(), 0); |
677 | 718 | } |
| 719 | + // Verify shutdown was called during the drop of the shared TracerProviderInner |
| 720 | + assert_eq!(*shutdown_called.lock().unwrap(), 1); |
| 721 | + // Verify flush was not called during drop |
| 722 | + assert!(!*flush_called.lock().unwrap()); |
| 723 | + } |
678 | 724 |
|
679 | | - // Test 2: Not shutdown case |
| 725 | + #[test] |
| 726 | + fn drop_after_shutdown_test_with_multiple_providers() { |
| 727 | + let shutdown_called = Arc::new(Mutex::new(0)); // Count the number of times shutdown is called |
| 728 | + let flush_called = Arc::new(Mutex::new(false)); |
| 729 | + |
| 730 | + // Create a shared TracerProviderInner and use it across multiple providers |
| 731 | + let shared_inner = Arc::new(TracerProviderInner { |
| 732 | + processors: vec![Box::new(CountingShutdownProcessor::new( |
| 733 | + shutdown_called.clone(), |
| 734 | + flush_called.clone(), |
| 735 | + ))], |
| 736 | + config: Config::default(), |
| 737 | + is_shutdown: AtomicBool::new(false), |
| 738 | + }); |
| 739 | + |
| 740 | + // Create a scope to test behavior when providers are dropped |
680 | 741 | { |
681 | | - let processor = TestSpanProcessor::new(true); |
682 | | - let assert_handle = processor.assert_info(); |
683 | | - let provider = super::TracerProvider::new(TracerProviderInner { |
684 | | - processors: vec![Box::from(processor)], |
685 | | - config: Default::default(), |
686 | | - is_shutdown: AtomicBool::new(false), |
687 | | - }); |
| 742 | + let tracer_provider1 = super::TracerProvider { |
| 743 | + inner: shared_inner.clone(), |
| 744 | + }; |
| 745 | + let tracer_provider2 = super::TracerProvider { |
| 746 | + inner: shared_inner.clone(), |
| 747 | + }; |
688 | 748 |
|
689 | | - // Create multiple providers sharing same inner |
690 | | - let provider2 = provider.clone(); |
691 | | - let provider3 = provider.clone(); |
| 749 | + // Explicitly shut down the tracer provider |
| 750 | + let shutdown_result = tracer_provider1.shutdown(); |
| 751 | + assert!(shutdown_result.is_ok()); |
692 | 752 |
|
693 | | - // Drop providers without explicit shutdown |
694 | | - drop(provider); |
695 | | - drop(provider2); |
| 753 | + // Verify that shutdown was called exactly once |
| 754 | + assert_eq!(*shutdown_called.lock().unwrap(), 1); |
696 | 755 |
|
697 | | - // Last drop should trigger shutdown in TracerProviderInner::drop |
698 | | - drop(provider3); |
| 756 | + // TracerProvider2 should observe the shutdown state but not trigger another shutdown |
| 757 | + let shutdown_result2 = tracer_provider2.shutdown(); |
| 758 | + assert!(shutdown_result2.is_err()); |
699 | 759 |
|
700 | | - // Verify shutdown was called exactly once |
701 | | - assert!(assert_handle.0.is_shutdown.load(Ordering::SeqCst)); |
| 760 | + // Both tracer providers will be dropped at the end of this scope |
702 | 761 | } |
| 762 | + |
| 763 | + // Verify that shutdown was only called once, even after drop |
| 764 | + assert_eq!(*shutdown_called.lock().unwrap(), 1); |
703 | 765 | } |
704 | 766 | } |
0 commit comments