1use crate::error::{OTelSdkError, OTelSdkResult};
2use 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
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(&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 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(); } 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#[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 pub(crate) fn new(inner: TracerProviderInner) -> Self {
167 SdkTracerProvider {
168 inner: Arc::new(inner),
169 }
170 }
171
172 pub fn builder() -> TracerProviderBuilder {
174 TracerProviderBuilder::default()
175 }
176
177 pub(crate) fn span_processors(&self) -> &[Box<dyn SpanProcessor>] {
179 &self.inner.processors
180 }
181
182 pub(crate) fn config(&self) -> &crate::trace::Config {
184 &self.inner.config
185 }
186
187 pub(crate) fn is_shutdown(&self) -> bool {
190 self.inner.is_shutdown.load(Ordering::Relaxed)
191 }
192
193 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 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 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<_>>() )))
264 }
265 } else {
266 Err(OTelSdkError::AlreadyShutdown)
267 }
268 }
269}
270
271impl opentelemetry::trace::TracerProvider for SdkTracerProvider {
272 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#[derive(Debug, Default)]
293pub struct TracerProviderBuilder {
294 processors: Vec<Box<dyn SpanProcessor>>,
295 config: crate::trace::Config,
296}
297
298impl TracerProviderBuilder {
299 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 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 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 #[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 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 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 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 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 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 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 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 pub fn with_span_limits(mut self, span_limits: SpanLimits) -> Self {
402 self.config.span_limits = span_limits;
403 self
404 }
405
406 pub fn with_resource(self, resource: Resource) -> Self {
415 TracerProviderBuilder {
416 config: self.config.with_resource(resource),
417 ..self
418 }
419 }
420
421 pub fn build(self) -> SdkTracerProvider {
423 let mut config = self.config;
424
425 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, };
439 }
440
441 let mut processors = self.processors;
443
444 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 #[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 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 }
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 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 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 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 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 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(); };
712
713 shutdown(tracer_provider.clone());
715
716 let noop_tracer = tracer_provider.tracer("noop");
718
719 let _ = noop_tracer.start("test");
721 assert!(assert_handle.started_span_count(2));
722 assert!(noop_tracer.provider().is_shutdown());
724
725 let _ = test_tracer_1.start("test");
727 assert!(assert_handle.started_span_count(2));
728
729 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 }
748
749 fn on_end(&self, _span: SpanData) {
750 }
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 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 }
794 assert_eq!(shutdown_count.load(Ordering::SeqCst), 0);
797 }
798 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 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 {
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 let shutdown_result = tracer_provider1.shutdown();
826 assert!(shutdown_result.is_ok());
827
828 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
830
831 let shutdown_result2 = tracer_provider2.shutdown();
833 assert!(shutdown_result2.is_err());
834 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
835
836 }
838
839 assert_eq!(shutdown_count.load(Ordering::SeqCst), 1);
841 }
842}