1use super::IdGenerator;
2use crate::error::{OTelSdkError, OTelSdkResult};
3use crate::trace::{
68 BatchSpanProcessor, Config, RandomIdGenerator, Sampler, SdkTracer, SimpleSpanProcessor,
69 SpanLimits,
70};
71use crate::Resource;
72use crate::{trace::SpanExporter, trace::SpanProcessor};
73use opentelemetry::otel_debug;
74use opentelemetry::{otel_info, InstrumentationScope};
75use std::borrow::Cow;
76use std::sync::atomic::{AtomicBool, Ordering};
77use std::sync::{Arc, OnceLock};
78use std::time::Duration;
79
80static PROVIDER_RESOURCE: OnceLock<Resource> = OnceLock::new();
81
82static 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 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#[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 pub(crate) fn shutdown_with_timeout(&self, timeout: Duration) -> Vec<OTelSdkResult> {
116 let mut results = vec![];
117 for processor in &self.processors {
118 let result = processor.shutdown_with_timeout(timeout);
119 if let Err(err) = &result {
120 otel_debug!(name: "TracerProvider.Drop.ShutdownError",
125 error = format!("{err}"));
126 }
127 results.push(result);
128 }
129 results
130 }
131 pub(crate) fn shutdown(&self) -> Vec<OTelSdkResult> {
133 self.shutdown_with_timeout(Duration::from_secs(5))
134 }
135}
136
137impl Drop for TracerProviderInner {
138 fn drop(&mut self) {
139 if !self.is_shutdown.load(Ordering::Relaxed) {
140 let _ = self.shutdown(); } else {
142 otel_debug!(
143 name: "TracerProvider.Drop.AlreadyShutdown",
144 message = "TracerProvider was already shut down; drop will not attempt shutdown again."
145 );
146 }
147 }
148}
149
150#[derive(Clone, Debug)]
158pub struct SdkTracerProvider {
159 inner: Arc<TracerProviderInner>,
160}
161
162impl Default for SdkTracerProvider {
163 fn default() -> Self {
164 SdkTracerProvider::builder().build()
165 }
166}
167
168impl SdkTracerProvider {
169 pub(crate) fn new(inner: TracerProviderInner) -> Self {
171 SdkTracerProvider {
172 inner: Arc::new(inner),
173 }
174 }
175
176 pub fn builder() -> TracerProviderBuilder {
178 TracerProviderBuilder::default()
179 }
180
181 pub(crate) fn span_processors(&self) -> &[Box<dyn SpanProcessor>] {
183 &self.inner.processors
184 }
185
186 pub(crate) fn config(&self) -> &crate::trace::Config {
188 &self.inner.config
189 }
190
191 pub(crate) fn is_shutdown(&self) -> bool {
194 self.inner.is_shutdown.load(Ordering::Relaxed)
195 }
196
197 pub fn force_flush(&self) -> OTelSdkResult {
231 let result: Vec<_> = self
232 .span_processors()
233 .iter()
234 .map(|processor| processor.force_flush())
235 .collect();
236 if result.iter().all(|r| r.is_ok()) {
237 Ok(())
238 } else {
239 Err(OTelSdkError::InternalFailure(format!("errs: {result:?}")))
240 }
241 }
242
243 pub fn shutdown_with_timeout(&self, timeout: Duration) -> OTelSdkResult {
247 if self
248 .inner
249 .is_shutdown
250 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
251 .is_ok()
252 {
253 let results = self.inner.shutdown_with_timeout(timeout);
255
256 if results.iter().all(|res| res.is_ok()) {
257 Ok(())
258 } else {
259 Err(OTelSdkError::InternalFailure(format!(
260 "Shutdown errors: {:?}",
261 results
262 .into_iter()
263 .filter_map(Result::err)
264 .collect::<Vec<_>>() )))
266 }
267 } else {
268 Err(OTelSdkError::AlreadyShutdown)
269 }
270 }
271
272 pub fn shutdown(&self) -> OTelSdkResult {
274 self.shutdown_with_timeout(Duration::from_secs(5))
275 }
276}
277
278impl opentelemetry::trace::TracerProvider for SdkTracerProvider {
279 type Tracer = SdkTracer;
281
282 fn tracer(&self, name: impl Into<Cow<'static, str>>) -> Self::Tracer {
283 let scope = InstrumentationScope::builder(name).build();
284 self.tracer_with_scope(scope)
285 }
286
287 fn tracer_with_scope(&self, scope: InstrumentationScope) -> Self::Tracer {
288 if self.inner.is_shutdown.load(Ordering::Relaxed) {
289 return SdkTracer::new(scope, noop_tracer_provider().clone());
290 }
291 if scope.name().is_empty() {
292 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.");
293 };
294 SdkTracer::new(scope, self.clone())
295 }
296}
297
298#[derive(Debug, Default)]
300pub struct TracerProviderBuilder {
301 processors: Vec<Box<dyn SpanProcessor>>,
302 config: crate::trace::Config,
303 resource: Option<Resource>,
304}
305
306impl TracerProviderBuilder {
307 pub fn with_simple_exporter<T: SpanExporter + 'static>(self, exporter: T) -> Self {
319 let simple = SimpleSpanProcessor::new(exporter);
320 self.with_span_processor(simple)
321 }
322
323 pub fn with_batch_exporter<T: SpanExporter + 'static>(self, exporter: T) -> Self {
335 let batch = BatchSpanProcessor::builder(exporter).build();
336 self.with_span_processor(batch)
337 }
338
339 pub fn with_span_processor<T: SpanProcessor + 'static>(self, processor: T) -> Self {
351 let mut processors = self.processors;
352 processors.push(Box::new(processor));
353
354 TracerProviderBuilder { processors, ..self }
355 }
356
357 pub fn with_sampler(mut self, sampler: impl Into<Box<dyn crate::trace::ShouldSample>>) -> Self {
376 self.config.sampler = sampler.into();
377 self
378 }
379
380 pub fn with_id_generator<T: IdGenerator + 'static>(mut self, id_generator: T) -> Self {
382 self.config.id_generator = Box::new(id_generator);
383 self
384 }
385
386 pub fn with_max_events_per_span(mut self, max_events: u32) -> Self {
388 self.config.span_limits.max_events_per_span = max_events;
389 self
390 }
391
392 pub fn with_max_attributes_per_span(mut self, max_attributes: u32) -> Self {
394 self.config.span_limits.max_attributes_per_span = max_attributes;
395 self
396 }
397
398 pub fn with_max_links_per_span(mut self, max_links: u32) -> Self {
400 self.config.span_limits.max_links_per_span = max_links;
401 self
402 }
403
404 pub fn with_max_attributes_per_event(mut self, max_attributes: u32) -> Self {
406 self.config.span_limits.max_attributes_per_event = max_attributes;
407 self
408 }
409
410 pub fn with_max_attributes_per_link(mut self, max_attributes: u32) -> Self {
412 self.config.span_limits.max_attributes_per_link = max_attributes;
413 self
414 }
415
416 pub fn with_span_limits(mut self, span_limits: SpanLimits) -> Self {
418 self.config.span_limits = span_limits;
419 self
420 }
421
422 pub fn with_resource(self, resource: Resource) -> Self {
456 let resource = match self.resource {
457 Some(existing) => Some(existing.merge(&resource)),
458 None => Some(resource),
459 };
460
461 TracerProviderBuilder { resource, ..self }
462 }
463
464 pub fn build(self) -> SdkTracerProvider {
466 let mut config = self.config;
467
468 if let Some(resource) = self.resource {
470 config.resource = Cow::Owned(resource);
471 };
472
473 if matches!(config.resource, Cow::Owned(_)) {
480 config.resource =
481 match PROVIDER_RESOURCE.get_or_init(|| config.resource.clone().into_owned()) {
482 static_resource if *static_resource == *config.resource.as_ref() => {
483 Cow::Borrowed(static_resource)
484 }
485 _ => config.resource, };
487 }
488
489 let mut processors = self.processors;
491
492 for p in &mut processors {
494 p.set_resource(config.resource.as_ref());
495 }
496
497 let is_shutdown = AtomicBool::new(false);
498 SdkTracerProvider::new(TracerProviderInner {
499 processors,
500 config,
501 is_shutdown,
502 })
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use crate::error::{OTelSdkError, OTelSdkResult};
509 use crate::resource::{
510 SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION,
511 };
512 use crate::trace::provider::TracerProviderInner;
513 use crate::trace::{Config, Span, SpanProcessor};
514 use crate::trace::{SdkTracerProvider, SpanData};
515 use crate::Resource;
516 use opentelemetry::trace::{Tracer, TracerProvider};
517 use opentelemetry::{Context, Key, KeyValue, Value};
518
519 use std::env;
520 use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
521 use std::sync::Arc;
522 use std::time::Duration;
523
524 #[derive(Default, Debug)]
526 struct AssertInfo {
527 started_span: AtomicU32,
528 is_shutdown: AtomicBool,
529 }
530
531 #[derive(Default, Debug, Clone)]
532 struct SharedAssertInfo(Arc<AssertInfo>);
533
534 impl SharedAssertInfo {
535 fn started_span_count(&self, count: u32) -> bool {
536 self.0.started_span.load(Ordering::SeqCst) == count
537 }
538 }
539
540 #[derive(Debug)]
541 struct TestSpanProcessor {
542 success: bool,
543 assert_info: SharedAssertInfo,
544 }
545
546 impl TestSpanProcessor {
547 fn new(success: bool) -> TestSpanProcessor {
548 TestSpanProcessor {
549 success,
550 assert_info: SharedAssertInfo::default(),
551 }
552 }
553
554 fn assert_info(&self) -> SharedAssertInfo {
556 self.assert_info.clone()
557 }
558 }
559
560 impl SpanProcessor for TestSpanProcessor {
561 fn on_start(&self, _span: &mut Span, _cx: &Context) {
562 self.assert_info
563 .0
564 .started_span
565 .fetch_add(1, Ordering::SeqCst);
566 }
567
568 fn on_end(&self, _span: SpanData) {
569 }
571
572 fn force_flush(&self) -> OTelSdkResult {
573 if self.success {
574 Ok(())
575 } else {
576 Err(OTelSdkError::InternalFailure("cannot export".into()))
577 }
578 }
579
580 fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
581 if self.assert_info.0.is_shutdown.load(Ordering::SeqCst) {
582 Ok(())
583 } else {
584 let _ = self.assert_info.0.is_shutdown.compare_exchange(
585 false,
586 true,
587 Ordering::SeqCst,
588 Ordering::SeqCst,
589 );
590 self.force_flush()
591 }
592 }
593 }
594
595 #[test]
596 fn test_force_flush() {
597 let tracer_provider = super::SdkTracerProvider::new(TracerProviderInner {
598 processors: vec![
599 Box::from(TestSpanProcessor::new(true)),
600 Box::from(TestSpanProcessor::new(false)),
601 ],
602 config: Default::default(),
603 is_shutdown: AtomicBool::new(false),
604 });
605
606 let results = tracer_provider.force_flush();
607 assert!(results.is_err());
608 }
609
610 #[test]
611 fn test_tracer_provider_default_resource() {
612 let assert_resource = |provider: &super::SdkTracerProvider,
613 resource_key: &'static str,
614 expect: Option<&'static str>| {
615 assert_eq!(
616 provider
617 .config()
618 .resource
619 .get(&Key::from_static_str(resource_key))
620 .map(|v| v.to_string()),
621 expect.map(|s| s.to_string())
622 );
623 };
624 let assert_telemetry_resource = |provider: &super::SdkTracerProvider| {
625 assert_eq!(
626 provider
627 .config()
628 .resource
629 .get(&TELEMETRY_SDK_LANGUAGE.into()),
630 Some(Value::from("rust"))
631 );
632 assert_eq!(
633 provider.config().resource.get(&TELEMETRY_SDK_NAME.into()),
634 Some(Value::from("opentelemetry"))
635 );
636 assert_eq!(
637 provider
638 .config()
639 .resource
640 .get(&TELEMETRY_SDK_VERSION.into()),
641 Some(Value::from(env!("CARGO_PKG_VERSION")))
642 );
643 };
644
645 temp_env::with_var_unset("OTEL_RESOURCE_ATTRIBUTES", || {
647 let default_config_provider = super::SdkTracerProvider::builder().build();
648 let service_name = default_config_provider
649 .config()
650 .resource
651 .get(&Key::from_static_str(SERVICE_NAME))
652 .map(|v| v.to_string())
653 .unwrap();
654 assert!(
655 service_name.starts_with("unknown_service:opentelemetry_sdk-"),
656 "Expected service name to start with 'unknown_service:opentelemetry_sdk-', got: {}",
657 service_name
658 );
659 assert_telemetry_resource(&default_config_provider);
660 });
661
662 let custom_config_provider = super::SdkTracerProvider::builder()
664 .with_resource(
665 Resource::builder_empty()
666 .with_service_name("test_service")
667 .build(),
668 )
669 .build();
670 assert_resource(&custom_config_provider, SERVICE_NAME, Some("test_service"));
671 assert_eq!(custom_config_provider.config().resource.len(), 1);
672
673 temp_env::with_var(
675 "OTEL_RESOURCE_ATTRIBUTES",
676 Some("key1=value1, k2, k3=value2"),
677 || {
678 let env_resource_provider = super::SdkTracerProvider::builder().build();
679 let service_name = env_resource_provider
680 .config()
681 .resource
682 .get(&Key::from_static_str(SERVICE_NAME))
683 .map(|v| v.to_string())
684 .unwrap();
685 assert!(
686 service_name.starts_with("unknown_service:opentelemetry_sdk-"),
687 "Expected service name to start with 'unknown_service:opentelemetry_sdk-', got: {}",
688 service_name
689 );
690 assert_resource(&env_resource_provider, "key1", Some("value1"));
691 assert_resource(&env_resource_provider, "k3", Some("value2"));
692 assert_telemetry_resource(&env_resource_provider);
693 assert_eq!(env_resource_provider.config().resource.len(), 6);
694 },
695 );
696
697 temp_env::with_var(
699 "OTEL_RESOURCE_ATTRIBUTES",
700 Some("my-custom-key=env-val,k2=value2"),
701 || {
702 let user_provided_resource_config_provider = super::SdkTracerProvider::builder()
703 .with_resource(
704 Resource::builder()
705 .with_attributes([
706 KeyValue::new("my-custom-key", "my-custom-value"),
707 KeyValue::new("my-custom-key2", "my-custom-value2"),
708 ])
709 .build(),
710 )
711 .build();
712 let service_name = user_provided_resource_config_provider
713 .config()
714 .resource
715 .get(&Key::from_static_str(SERVICE_NAME))
716 .map(|v| v.to_string())
717 .unwrap();
718 assert!(
719 service_name.starts_with("unknown_service:opentelemetry_sdk-"),
720 "Expected service name to start with 'unknown_service:opentelemetry_sdk-', got: {}",
721 service_name
722 );
723 assert_resource(
724 &user_provided_resource_config_provider,
725 "my-custom-key",
726 Some("my-custom-value"),
727 );
728 assert_resource(
729 &user_provided_resource_config_provider,
730 "my-custom-key2",
731 Some("my-custom-value2"),
732 );
733 assert_resource(
734 &user_provided_resource_config_provider,
735 "k2",
736 Some("value2"),
737 );
738 assert_telemetry_resource(&user_provided_resource_config_provider);
739 assert_eq!(
740 user_provided_resource_config_provider
741 .config()
742 .resource
743 .len(),
744 7
745 );
746 },
747 );
748
749 let no_service_name = super::SdkTracerProvider::builder()
751 .with_resource(Resource::empty())
752 .build();
753
754 assert_eq!(no_service_name.config().resource.len(), 0)
755 }
756
757 #[test]
758 fn test_shutdown_noops() {
759 let processor = TestSpanProcessor::new(false);
760 let assert_handle = processor.assert_info();
761 let tracer_provider = super::SdkTracerProvider::new(TracerProviderInner {
762 processors: vec![Box::from(processor)],
763 config: Default::default(),
764 is_shutdown: AtomicBool::new(false),
765 });
766
767 let test_tracer_1 = tracer_provider.tracer("test1");
768 let _ = test_tracer_1.start("test");
769
770 assert!(assert_handle.started_span_count(1));
771
772 let _ = test_tracer_1.start("test");
773
774 assert!(assert_handle.started_span_count(2));
775
776 let shutdown = |tracer_provider: super::SdkTracerProvider| {
777 let _ = tracer_provider.shutdown(); };
779
780 shutdown(tracer_provider.clone());
782
783 let noop_tracer = tracer_provider.tracer("noop");
785
786 let _ = noop_tracer.start("test");
788 assert!(assert_handle.started_span_count(2));
789 assert!(noop_tracer.provider().is_shutdown());
791
792 let _ = test_tracer_1.start("test");
794 assert!(assert_handle.started_span_count(2));
795
796 assert!(test_tracer_1.provider().is_shutdown());
798 }
799
800 #[test]
801 fn with_resource_multiple_calls_ensure_additive() {
802 let resource = SdkTracerProvider::builder()
803 .with_resource(
804 Resource::builder_empty()
805 .with_attributes(vec![KeyValue::new("key1", "value1")])
806 .build(),
807 )
808 .with_resource(
809 Resource::builder_empty()
810 .with_attributes(vec![KeyValue::new("key2", "value2")])
811 .build(),
812 )
813 .with_resource(
814 Resource::builder_empty()
815 .with_schema_url(vec![], "http://example.com")
816 .build(),
817 )
818 .with_resource(
819 Resource::builder_empty()
820 .with_attributes(vec![KeyValue::new("key3", "value3")])
821 .build(),
822 )
823 .build()
824 .inner
825 .config
826 .resource
827 .clone()
828 .into_owned();
829
830 assert_eq!(
831 resource.get(&Key::from_static_str("key1")),
832 Some(Value::from("value1"))
833 );
834 assert_eq!(
835 resource.get(&Key::from_static_str("key2")),
836 Some(Value::from("value2"))
837 );
838 assert_eq!(
839 resource.get(&Key::from_static_str("key3")),
840 Some(Value::from("value3"))
841 );
842 assert_eq!(resource.schema_url(), Some("http://example.com"));
843 }
844
845 #[derive(Debug)]
846 struct CountingShutdownProcessor {
847 shutdown_count: Arc<AtomicU32>,
848 }
849
850 impl CountingShutdownProcessor {
851 fn new(shutdown_count: Arc<AtomicU32>) -> Self {
852 CountingShutdownProcessor { shutdown_count }
853 }
854 }
855
856 impl SpanProcessor for CountingShutdownProcessor {
857 fn on_start(&self, _span: &mut Span, _cx: &Context) {
858 }
860
861 fn on_end(&self, _span: SpanData) {
862 }
864
865 fn force_flush(&self) -> OTelSdkResult {
866 Ok(())
867 }
868
869 fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
870 self.shutdown_count.fetch_add(1, Ordering::SeqCst);
871 Ok(())
872 }
873 }
874
875 #[test]
876 fn drop_test_with_multiple_providers() {
877 let shutdown_count = Arc::new(AtomicU32::new(0));
878
879 {
880 let shared_inner = Arc::new(TracerProviderInner {
882 processors: vec![Box::new(CountingShutdownProcessor::new(
883 shutdown_count.clone(),
884 ))],
885 config: Config::default(),
886 is_shutdown: AtomicBool::new(false),
887 });
888
889 {
890 let tracer_provider1 = super::SdkTracerProvider {
891 inner: shared_inner.clone(),
892 };
893 let tracer_provider2 = super::SdkTracerProvider {
894 inner: shared_inner.clone(),
895 };
896
897 let tracer1 = tracer_provider1.tracer("test-tracer1");
898 let tracer2 = tracer_provider2.tracer("test-tracer2");
899
900 let _span1 = tracer1.start("span1");
901 let _span2 = tracer2.start("span2");
902
903 }
906 assert_eq!(shutdown_count.load(Ordering::SeqCst), 0);
909 }
910 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
912 }
913
914 #[test]
915 fn drop_after_shutdown_test_with_multiple_providers() {
916 let shutdown_count = Arc::new(AtomicU32::new(0));
917
918 let shared_inner = Arc::new(TracerProviderInner {
920 processors: vec![Box::new(CountingShutdownProcessor::new(
921 shutdown_count.clone(),
922 ))],
923 config: Config::default(),
924 is_shutdown: AtomicBool::new(false),
925 });
926
927 {
929 let tracer_provider1 = super::SdkTracerProvider {
930 inner: shared_inner.clone(),
931 };
932 let tracer_provider2 = super::SdkTracerProvider {
933 inner: shared_inner.clone(),
934 };
935
936 let shutdown_result = tracer_provider1.shutdown();
938 assert!(shutdown_result.is_ok());
939
940 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
942
943 let shutdown_result2 = tracer_provider2.shutdown();
945 assert!(shutdown_result2.is_err());
946 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
947
948 }
950
951 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
953 }
954}