use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use multimap::MultiMap;
use tokio::task::block_in_place;
use tower::BoxError;
use tower::ServiceExt;
use crate::Endpoint;
use crate::ListenAddr;
use crate::metrics::aggregation::MeterProviderType;
use crate::plugins::telemetry::apollo;
use crate::plugins::telemetry::apollo_exporter::Sender;
use crate::plugins::telemetry::config::Conf;
use crate::plugins::telemetry::config::MetricView;
use crate::plugins::telemetry::config_new::cache::CACHE_METRIC;
use crate::plugins::telemetry::fmt_layer::create_fmt_layer;
use crate::plugins::telemetry::metrics;
use crate::plugins::telemetry::metrics::prometheus::PrometheusService;
use crate::plugins::telemetry::otlp;
use crate::plugins::telemetry::reload::activation::Activation;
use crate::plugins::telemetry::reload::metrics::MetricsBuilder;
use crate::plugins::telemetry::reload::metrics::MetricsConfigurator;
use crate::plugins::telemetry::reload::tracing::TracingBuilder;
use crate::plugins::telemetry::reload::tracing::TracingConfigurator;
use crate::plugins::telemetry::reload::tracing::create_propagator;
use crate::plugins::telemetry::tracing::datadog;
use crate::plugins::telemetry::tracing::zipkin;
static PROMETHEUS_CALL_COUNT: AtomicUsize = AtomicUsize::new(1);
pub(super) struct Builder<'a> {
previous_config: &'a Option<Conf>,
config: &'a Conf,
activation: Activation,
endpoints: MultiMap<ListenAddr, Endpoint>,
apollo_sender: Sender,
}
impl<'a> Builder<'a> {
pub(super) fn new(previous_config: &'a Option<Conf>, config: &'a Conf) -> Self {
Self {
previous_config,
config,
activation: Activation::new(),
endpoints: Default::default(),
apollo_sender: Sender::Noop,
}
}
pub(super) fn build(
mut self,
) -> Result<(Activation, MultiMap<ListenAddr, Endpoint>, Sender), BoxError> {
block_in_place(|| {
self.setup_logging();
self.setup_public_tracing()?;
self.setup_public_metrics()?;
self.setup_apollo_metrics()?;
self.setup_propagation();
Ok((self.activation, self.endpoints, self.apollo_sender))
})
}
fn setup_public_metrics(&mut self) -> Result<(), BoxError> {
if self.is_metrics_config_changed::<metrics::prometheus::Config>()
|| self.is_metrics_config_changed::<otlp::Config>()
|| self.prometheus_force_change()
{
::tracing::debug!("configuring metrics");
let mut builder = MetricsBuilder::new(self.config);
builder.configure(&self.config.exporters.metrics.prometheus)?;
builder.configure(&self.config.exporters.metrics.otlp)?;
crate::plugins::telemetry::metrics::allocation::register_memory_allocation_views(
&mut builder,
);
builder.configure_views(MeterProviderType::Public);
let (prometheus_registry, meter_providers, _) = builder.build();
self.activation
.with_prometheus_registry(prometheus_registry);
self.activation.add_meter_providers(meter_providers);
}
if let Some(prometheus_registry) = self.activation.prometheus_registry() {
let listen = self.config.exporters.metrics.prometheus.listen.clone();
let path = self.config.exporters.metrics.prometheus.path.clone();
tracing::info!("Prometheus endpoint exposed at {}{}", &listen, &path);
self.endpoints.insert(
listen,
Endpoint::from_router_service(
path,
PrometheusService {
registry: prometheus_registry.clone(),
}
.boxed(),
),
);
}
Ok(())
}
fn setup_public_tracing(&mut self) -> Result<(), BoxError> {
if self.is_tracing_config_changed::<otlp::Config>()
|| self.is_tracing_config_changed::<datadog::Config>()
|| self.is_tracing_config_changed::<zipkin::Config>()
|| self.is_tracing_config_changed::<apollo::Config>()
{
::tracing::debug!("configuring tracing");
let mut builder = TracingBuilder::new(self.config);
builder.configure(&self.config.exporters.tracing.otlp)?;
builder.configure(&self.config.exporters.tracing.zipkin)?;
builder.configure(&self.config.exporters.tracing.datadog)?;
builder.configure(&self.config.apollo)?;
self.activation.with_tracer_provider(builder.build())
}
Ok(())
}
fn setup_apollo_metrics(&mut self) -> Result<(), BoxError> {
::tracing::debug!("configuring Apollo metrics");
let mut builder = MetricsBuilder::new(self.config);
builder.configure(&self.config.apollo)?;
if !builder.meter_provider_builders.is_empty() {
builder.with_view(
MeterProviderType::Apollo,
MetricView {
name: String::from(CACHE_METRIC),
rename: None,
description: None,
unit: None,
aggregation: Some(crate::plugins::telemetry::config::MetricAggregation::Drop),
allowed_attribute_keys: None,
}
.into_view_fn(),
);
}
let (_, meter_providers, sender) = builder.build();
self.activation.add_meter_providers(meter_providers);
self.apollo_sender = sender;
Ok(())
}
fn is_metrics_config_changed<T: MetricsConfigurator + PartialEq>(&self) -> bool {
let Some(previous_config) = self.previous_config else {
return true;
};
T::config(previous_config) != T::config(self.config)
|| previous_config.exporters.metrics.common != self.config.exporters.metrics.common
}
fn is_tracing_config_changed<T: TracingConfigurator + PartialEq>(&self) -> bool {
let Some(previous_config) = self.previous_config else {
return true;
};
T::config(previous_config) != T::config(self.config)
|| previous_config.exporters.tracing.common != self.config.exporters.tracing.common
}
fn setup_propagation(&mut self) {
let propagators = create_propagator(
&self.config.exporters.tracing.propagation,
&self.config.exporters.tracing,
);
self.activation.with_tracer_propagator(propagators);
}
fn setup_logging(&mut self) {
::tracing::debug!("configuring logging");
self.activation.with_logging(create_fmt_layer(self.config));
}
fn prometheus_force_change(&self) -> bool {
if !metrics::prometheus::Config::config(self.config).enabled {
return false;
}
let count = PROMETHEUS_CALL_COUNT.fetch_add(1, Ordering::Relaxed);
count.is_multiple_of(20)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::plugins::telemetry::apollo;
use crate::plugins::telemetry::config::Exporters;
use crate::plugins::telemetry::config::Instrumentation;
use crate::plugins::telemetry::config::Metrics;
use crate::plugins::telemetry::config::Propagation;
use crate::plugins::telemetry::config::Tracing;
fn create_default_config() -> Conf {
Conf {
apollo: apollo::Config::default(),
exporters: Exporters {
metrics: Metrics {
common: Default::default(),
otlp: Default::default(),
prometheus: Default::default(),
},
tracing: Tracing::default(),
logging: Default::default(),
},
instrumentation: Instrumentation::default(),
}
}
fn create_config_with_prometheus_enabled() -> Conf {
let mut config = create_default_config();
config.exporters.metrics.prometheus.enabled = true;
config
}
fn create_config_with_otlp_metrics_enabled() -> Conf {
let mut config = create_default_config();
config.exporters.metrics.otlp.enabled = true;
config
}
fn create_config_with_otlp_tracing_enabled() -> Conf {
let mut config = create_default_config();
config.exporters.tracing.otlp.enabled = true;
config
}
fn create_config_with_apollo_enabled() -> Conf {
let mut config = create_default_config();
config.apollo.apollo_key = Some("test-key".to_string());
config.apollo.apollo_graph_ref = Some("test@current".to_string());
config
}
#[tokio::test(flavor = "multi_thread")]
async fn test_no_reload_when_configs_identical() {
let config = create_default_config();
let previous_config = Some(config.clone());
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
!instr.tracer_provider_set,
"Tracer provider should not reload when configs identical"
);
assert!(
!instr.prometheus_registry_set,
"Prometheus registry should not reload when configs identical"
);
assert!(
instr.meter_providers_added.is_empty(),
"No meter providers should be added when configs identical and apollo not configured"
);
assert!(instr.logging_layer_set, "Logging should always be set");
assert!(
instr.tracer_propagator_set,
"Propagator should always be set"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_metrics_reload_on_prometheus_change() {
let previous_config = Some(create_default_config());
let config = create_config_with_prometheus_enabled();
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.prometheus_registry_set,
"Prometheus registry should be set when config changes"
);
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider should be added"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_metrics_reload_on_otlp_change() {
let previous_config = Some(create_default_config());
let config = create_config_with_otlp_metrics_enabled();
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider should be added when OTLP metrics changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tracing_reload_on_otlp_change() {
let previous_config = Some(create_default_config());
let config = create_config_with_otlp_tracing_enabled();
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"Tracer provider should be set when OTLP tracing changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_apollo_metrics_always_rebuild_when_enabled() {
let config = create_config_with_apollo_enabled();
let previous_config = Some(config.clone());
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Apollo)
|| instr
.meter_providers_added
.contains(&MeterProviderType::ApolloRealtime),
"Apollo metrics should always rebuild when apollo is configured"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_first_run_builds_everything() {
let config = create_default_config();
let previous_config = None;
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"First run should build tracer provider"
);
assert!(
instr.tracer_propagator_set,
"First run should set tracer propagator"
);
assert!(
instr.logging_layer_set,
"First run should set logging layer"
);
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider (noop) should be set on first run"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_first_run_with_apollo_enabled() {
let config = create_config_with_apollo_enabled();
let previous_config = None;
let builder = Builder::new(&previous_config, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"First run should build tracer provider"
);
assert!(
instr.tracer_propagator_set,
"First run should set tracer propagator"
);
assert!(
instr.logging_layer_set,
"First run should set logging layer"
);
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Apollo)
|| instr
.meter_providers_added
.contains(&MeterProviderType::ApolloRealtime),
"First run should add apollo meter providers when apollo enabled"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_metrics_common_change_triggers_reload() {
let previous_config = create_config_with_prometheus_enabled();
let mut config = create_config_with_prometheus_enabled();
config.exporters.metrics.common.service_name = Some("new-service".to_string());
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider should reload when common config changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tracing_common_change_triggers_reload() {
let previous_config = create_config_with_otlp_tracing_enabled();
let mut config = create_config_with_otlp_tracing_enabled();
config.exporters.tracing.common.service_name = Some("new-service".to_string());
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"Tracer provider should reload when common config changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_metrics_common_service_namespace_change() {
let previous_config = create_config_with_prometheus_enabled();
let mut config = create_config_with_prometheus_enabled();
config.exporters.metrics.common.service_namespace = Some("new-namespace".to_string());
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider should reload when service_namespace changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_metrics_common_resource_change() {
let previous_config = create_config_with_prometheus_enabled();
let mut config = create_config_with_prometheus_enabled();
config.exporters.metrics.common.resource.insert(
"deployment.environment".to_string(),
crate::plugins::telemetry::config::AttributeValue::String("staging".to_string()),
);
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider should reload when resource attributes change"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_metrics_common_buckets_change() {
let previous_config = create_config_with_prometheus_enabled();
let mut config = create_config_with_prometheus_enabled();
config.exporters.metrics.common.buckets = vec![0.1, 0.5, 1.0, 2.5, 5.0, 10.0];
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr
.meter_providers_added
.contains(&MeterProviderType::Public),
"Public meter provider should reload when histogram buckets change"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tracing_common_service_namespace_change() {
let previous_config = create_config_with_otlp_tracing_enabled();
let mut config = create_config_with_otlp_tracing_enabled();
config.exporters.tracing.common.service_namespace = Some("new-namespace".to_string());
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"Tracer provider should reload when service_namespace changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tracing_common_sampler_change() {
let previous_config = create_config_with_otlp_tracing_enabled();
let mut config = create_config_with_otlp_tracing_enabled();
config.exporters.tracing.common.sampler =
crate::plugins::telemetry::config::SamplerOption::TraceIdRatioBased(0.5);
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"Tracer provider should reload when sampler changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tracing_common_parent_based_sampler_change() {
let previous_config = create_config_with_otlp_tracing_enabled();
let mut config = create_config_with_otlp_tracing_enabled();
config.exporters.tracing.common.parent_based_sampler = false;
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"Tracer provider should reload when parent_based_sampler changes"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tracing_common_span_limits_change() {
let previous_config = create_config_with_otlp_tracing_enabled();
let mut config = create_config_with_otlp_tracing_enabled();
config.exporters.tracing.common.max_events_per_span = 256;
config.exporters.tracing.common.max_attributes_per_span = 64;
let previous_config_opt = Some(previous_config);
let builder = Builder::new(&previous_config_opt, &config);
let (activation, _endpoints, _sender) = builder.build().unwrap();
let instr = activation.test_instrumentation();
assert!(
instr.tracer_provider_set,
"Tracer provider should reload when span limits change"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_propagation_only_passes() {
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_with_baggage_propagation_passes() {
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
baggage: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_jaeger_propagation_only_passes() {
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
jaeger: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_with_jaeger_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
jaeger: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"datadog propagation should not be used with any other propagator except for baggage to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_with_trace_context_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
trace_context: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"datadog propagation should not be used with any other propagator except for baggage to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_with_zipkin_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
zipkin: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"datadog propagation should not be used with any other propagator except for baggage to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_with_aws_xray_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
aws_xray: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"datadog propagation should not be used with any other propagator except for baggage to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_datadog_propagation_only_passes() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_datadog_baggage_propagation_passes() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
baggage: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_jaeger_propagation_only_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
jaeger: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"datadog propagation should be explicitly disabled if the datadog exporter is enabled and any propagator other than baggage is enabled to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_datadog_jaeger_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
jaeger: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"if the datadog exporter is enabled and any other propagator except for baggage is enabled, the datadog propagator should be disabled to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_datadog_trace_context_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
trace_context: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"if the datadog exporter is enabled and any other propagator except for baggage is enabled, the datadog propagator should be disabled to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_datadog_zipkin_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
zipkin: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"if the datadog exporter is enabled and any other propagator except for baggage is enabled, the datadog propagator should be disabled to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_datadog_aws_xray_propagation_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.propagation = Propagation {
datadog: Some(true),
aws_xray: true,
..Default::default()
};
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"if the datadog exporter is enabled and any other propagator except for baggage is enabled, the datadog propagator should be disabled to avoid trace id conflicts",
));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_otlp_exporter_enabled_passes() {
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.otlp.enabled = true;
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_propagation_with_otlp_exporter_enabled_passes() {
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.otlp.enabled = true;
config.exporters.tracing.propagation.datadog = Some(true);
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_datadog_exporter_enabled_with_zipkin_exporter_enabled_fails() {
use crate::test_harness::tracing_test;
let _guard = tracing_test::dispatcher_guard();
let mut config = create_config_with_apollo_enabled();
config.exporters.tracing.datadog.enabled = true;
config.exporters.tracing.zipkin.enabled = true;
let builder = Builder::new(&None, &config);
assert!(builder.build().is_ok());
assert!(tracing_test::logs_contain(
"datadog propagation should be explicitly disabled if the datadog exporter is enabled and any propagator other than baggage is enabled to avoid trace id conflicts",
));
}
}