use std::sync::atomic::{AtomicBool, Ordering};
static TRACES_ADOPTED: AtomicBool = AtomicBool::new(false);
static METRICS_ADOPTED: AtomicBool = AtomicBool::new(false);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct AdoptedProviders {
pub traces: bool,
pub metrics: bool,
}
impl AdoptedProviders {
#[must_use]
pub fn all() -> Self {
Self {
traces: true,
metrics: true,
}
}
}
pub fn adopt_global_providers(adopted: AdoptedProviders) {
TRACES_ADOPTED.store(adopted.traces, Ordering::Release);
METRICS_ADOPTED.store(adopted.metrics, Ordering::Release);
}
#[must_use]
pub fn adopted_global_providers() -> AdoptedProviders {
AdoptedProviders {
traces: TRACES_ADOPTED.load(Ordering::Acquire),
metrics: METRICS_ADOPTED.load(Ordering::Acquire),
}
}
pub(crate) fn release_adopted_providers() {
adopt_global_providers(AdoptedProviders::default());
}
#[cfg(feature = "otel")]
pub(crate) fn traces_adopted() -> bool {
TRACES_ADOPTED.load(Ordering::Acquire)
}
#[cfg(feature = "otel")]
pub(crate) fn metrics_adopted() -> bool {
METRICS_ADOPTED.load(Ordering::Acquire)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::acquire_test_state_lock;
#[test]
fn nothing_is_adopted_by_default() {
let _guard = acquire_test_state_lock();
release_adopted_providers();
assert_eq!(adopted_global_providers(), AdoptedProviders::default());
assert!(!adopted_global_providers().traces);
assert!(!adopted_global_providers().metrics);
}
#[test]
fn adoption_is_per_signal() {
let _guard = acquire_test_state_lock();
adopt_global_providers(AdoptedProviders {
traces: true,
metrics: false,
});
assert!(adopted_global_providers().traces);
assert!(!adopted_global_providers().metrics);
release_adopted_providers();
}
#[test]
fn all_asserts_both_signals() {
let _guard = acquire_test_state_lock();
adopt_global_providers(AdoptedProviders::all());
assert!(adopted_global_providers().traces);
assert!(adopted_global_providers().metrics);
release_adopted_providers();
}
#[cfg(feature = "otel")]
#[test]
fn adoption_makes_the_traces_provider_effective() {
let _guard = acquire_test_state_lock();
crate::testing::reset_telemetry_state();
release_adopted_providers();
assert!(!crate::otel::traces_provider_effective());
adopt_global_providers(AdoptedProviders::all());
assert!(crate::otel::traces_provider_effective());
assert!(crate::otel::metrics_provider_effective());
release_adopted_providers();
}
#[cfg(feature = "otel")]
#[test]
fn an_adopted_provider_actually_receives_the_span() {
use opentelemetry_sdk::error::OTelSdkResult;
use opentelemetry_sdk::trace::{SdkTracerProvider, SpanData, SpanExporter};
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
struct RecordingExporter {
names: Arc<Mutex<Vec<String>>>,
}
impl SpanExporter for RecordingExporter {
async fn export(&self, batch: Vec<SpanData>) -> OTelSdkResult {
let mut names = self.names.lock().expect("exporter mutex");
names.extend(batch.iter().map(|span| span.name.to_string()));
Ok(())
}
}
let _guard = acquire_test_state_lock();
crate::testing::reset_telemetry_state();
let names = Arc::new(Mutex::new(Vec::new()));
let host_provider = SdkTracerProvider::builder()
.with_simple_exporter(RecordingExporter {
names: Arc::clone(&names),
})
.build();
opentelemetry::global::set_tracer_provider(host_provider.clone());
adopt_global_providers(AdoptedProviders::all());
crate::trace("adopted.export.span", || {});
host_provider.force_flush().expect("flush host provider");
let recorded = names.lock().expect("exporter mutex").clone();
assert!(
recorded.iter().any(|name| name == "adopted.export.span"),
"facade reported the host provider as in play but no span reached its exporter; got {recorded:?}"
);
release_adopted_providers();
crate::testing::reset_telemetry_state();
}
#[test]
fn shutdown_releases_the_assertion_without_owning_the_providers() {
let _guard = acquire_test_state_lock();
adopt_global_providers(AdoptedProviders::all());
crate::shutdown_telemetry(None).expect("shutdown should succeed");
assert_eq!(adopted_global_providers(), AdoptedProviders::default());
}
#[cfg(feature = "otel")]
#[test]
fn adopted_traces_bypass_facade_sampling() {
use crate::sampling::{set_sampling_policy, SamplingPolicy, Signal};
let _guard = acquire_test_state_lock();
crate::shutdown_telemetry(None).expect("pre-test shutdown should succeed");
crate::health::_reset_health_for_tests();
set_sampling_policy(
Signal::Traces,
SamplingPolicy {
default_rate: 0.0,
overrides: Default::default(),
},
)
.expect("policy should apply");
crate::trace("adopt.unsampled", || {});
assert_eq!(crate::health::get_health_snapshot().emitted_traces, 0);
adopt_global_providers(AdoptedProviders::all());
crate::trace("adopt.sampled", || {});
assert_eq!(crate::health::get_health_snapshot().emitted_traces, 1);
release_adopted_providers();
crate::sampling::_reset_sampling_for_tests();
}
}