use opentelemetry_sdk::Resource;
use opentelemetry_sdk::trace::{
BatchSpanProcessor, SdkTracerProvider, SpanLimits, TracerProviderBuilder,
};
use crate::InitError;
use crate::config::BatchProcessorConfig;
use crate::config::RateLimitedProcessorConfig;
use crate::config::traces::{
BatchSpanProcessorConfig, RateLimitedSpanProcessorConfig, SpanLimitsConfig,
};
use crate::config::{OpenTelemetryConfig, SpanExporter, SpanProcessor};
use crate::error::ExporterKind;
#[cfg(any(
feature = "otlp",
feature = "honeycomb",
feature = "new-relic",
feature = "grafana-cloud",
feature = "datadog-otlp"
))]
use crate::exporters::instrumented::InstrumentedSpanExporter;
use crate::processors::trace::RateLimitedSpanProcessor;
pub(crate) fn build_tracer_provider(
config: &OpenTelemetryConfig,
resource: Resource,
mut builder: TracerProviderBuilder,
) -> Result<SdkTracerProvider, InitError> {
builder = builder.with_resource(resource);
let sampler = opentelemetry_sdk::trace::Sampler::from(&config.tracer_provider.sampler);
builder = builder.with_sampler(sampler);
builder = builder.with_span_limits(SpanLimits::from(&config.tracer_provider.limits));
for processor in &config.tracer_provider.processors {
builder = add_span_processor(builder, processor)?;
}
Ok(builder.build())
}
impl From<&SpanLimitsConfig> for SpanLimits {
fn from(config: &SpanLimitsConfig) -> Self {
SpanLimits {
max_attributes_per_span: config.attribute_count_limit,
max_events_per_span: config.event_count_limit,
max_links_per_span: config.link_count_limit,
max_attributes_per_event: config.event_attribute_count_limit,
max_attributes_per_link: config.link_attribute_count_limit,
}
}
}
fn add_span_processor(
builder: TracerProviderBuilder,
config: &SpanProcessor,
) -> Result<TracerProviderBuilder, InitError> {
match config {
SpanProcessor::Batch(batch_config) => add_batch_span_processor(builder, batch_config),
SpanProcessor::Simple(simple_config) => {
add_simple_span_processor(builder, &simple_config.exporter)
}
SpanProcessor::RateLimited(config) => add_rate_limited_span_processor(builder, config),
}
}
fn add_simple_span_processor(
#[allow(unused_variables)] builder: TracerProviderBuilder,
exporter_config: &SpanExporter,
) -> Result<TracerProviderBuilder, InitError> {
match exporter_config {
#[cfg(feature = "otlp")]
SpanExporter::OtlpHttp(config) => {
let exporter = crate::exporters::otlp::build_otlp_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "otlp")]
SpanExporter::OtlpGrpc(config) => {
let exporter = crate::exporters::otlp::build_otlp_grpc_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "honeycomb")]
SpanExporter::HoneycombHttp(config) => {
let exporter = crate::exporters::honeycomb::build_http_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "honeycomb")]
SpanExporter::HoneycombGrpc(config) => {
let exporter = crate::exporters::honeycomb::build_grpc_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "new-relic")]
SpanExporter::NewRelicHttp(config) => {
let exporter = crate::exporters::new_relic::build_http_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "new-relic")]
SpanExporter::NewRelicGrpc(config) => {
let exporter = crate::exporters::new_relic::build_grpc_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "grafana-cloud")]
SpanExporter::GrafanaCloud(config) => {
let exporter = crate::exporters::grafana_cloud::build_http_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "datadog-otlp")]
SpanExporter::DatadogOtlpHttp(config) => {
let exporter = crate::exporters::datadog_otlp::build_http_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "datadog-otlp")]
SpanExporter::DatadogOtlpGrpc(config) => {
let exporter = crate::exporters::datadog_otlp::build_grpc_span_exporter(config)?;
Ok(builder.with_simple_exporter(exporter))
}
#[cfg(feature = "datadog")]
SpanExporter::Datadog(_) => Err(InitError::Exporter {
exporter: ExporterKind::DatadogNative,
reason: "Datadog native exporter is not yet implemented".to_string(),
}),
SpanExporter::Console(config) => {
let exporter = apollo_opentelemetry_stdout::__private::StdoutSpanExporter::with_writer(
config.format.into(),
config.target.into(),
config.use_color(),
);
Ok(builder.with_simple_exporter(exporter))
}
}
}
fn add_batch_span_processor(
#[allow(unused_variables)] builder: TracerProviderBuilder,
config: &BatchSpanProcessorConfig,
) -> Result<TracerProviderBuilder, InitError> {
#[allow(unused_variables)]
let batch_config = config.to_trace_batch_config();
match &config.exporter {
#[cfg(feature = "otlp")]
SpanExporter::OtlpHttp(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::OtlpHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "otlp")]
SpanExporter::OtlpGrpc(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::OtlpGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "honeycomb")]
SpanExporter::HoneycombHttp(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::HoneycombHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "honeycomb")]
SpanExporter::HoneycombGrpc(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::HoneycombGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "new-relic")]
SpanExporter::NewRelicHttp(exporter_config) => {
let exporter = crate::exporters::new_relic::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::NewRelicHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "new-relic")]
SpanExporter::NewRelicGrpc(exporter_config) => {
let exporter = crate::exporters::new_relic::build_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::NewRelicGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "grafana-cloud")]
SpanExporter::GrafanaCloud(exporter_config) => {
let exporter =
crate::exporters::grafana_cloud::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::GrafanaCloudHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "datadog-otlp")]
SpanExporter::DatadogOtlpHttp(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::DatadogHttp,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "datadog-otlp")]
SpanExporter::DatadogOtlpGrpc(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::DatadogGrpc,
Some(&exporter_config.endpoint),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
#[cfg(feature = "datadog")]
SpanExporter::Datadog(_) => Err(InitError::Exporter {
exporter: ExporterKind::DatadogNative,
reason: "Datadog native exporter is not yet implemented".to_string(),
}),
SpanExporter::Console(config) => {
let exporter = apollo_opentelemetry_stdout::__private::StdoutSpanExporter::with_writer(
config.format.into(),
config.target.into(),
config.use_color(),
);
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
Ok(builder.with_span_processor(batch_processor))
}
}
}
#[allow(unused_variables)]
fn add_rate_limited_span_processor(
builder: TracerProviderBuilder,
config: &RateLimitedSpanProcessorConfig,
) -> Result<TracerProviderBuilder, InitError> {
let batch_config = config.to_trace_batch_config();
let max_rate = config.max_rate();
match &config.exporter {
#[cfg(feature = "otlp")]
SpanExporter::OtlpHttp(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::OtlpHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::OtlpHttp,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "otlp")]
SpanExporter::OtlpGrpc(exporter_config) => {
let exporter = crate::exporters::otlp::build_otlp_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::OtlpGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::OtlpGrpc,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "honeycomb")]
SpanExporter::HoneycombHttp(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::HoneycombHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::HoneycombHttp,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "honeycomb")]
SpanExporter::HoneycombGrpc(exporter_config) => {
let exporter = crate::exporters::honeycomb::build_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::HoneycombGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::HoneycombGrpc,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "new-relic")]
SpanExporter::NewRelicHttp(exporter_config) => {
let exporter = crate::exporters::new_relic::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::NewRelicHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::NewRelicHttp,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "new-relic")]
SpanExporter::NewRelicGrpc(exporter_config) => {
let exporter = crate::exporters::new_relic::build_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::NewRelicGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::NewRelicGrpc,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "grafana-cloud")]
SpanExporter::GrafanaCloud(exporter_config) => {
let exporter =
crate::exporters::grafana_cloud::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::GrafanaCloudHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::GrafanaCloudHttp,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "datadog-otlp")]
SpanExporter::DatadogOtlpHttp(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_http_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::DatadogHttp,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::DatadogHttp,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "datadog-otlp")]
SpanExporter::DatadogOtlpGrpc(exporter_config) => {
let exporter =
crate::exporters::datadog_otlp::build_grpc_span_exporter(exporter_config)?;
let exporter = InstrumentedSpanExporter::new(
exporter,
ExporterKind::DatadogGrpc,
Some(&exporter_config.endpoint),
);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::DatadogGrpc,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
#[cfg(feature = "datadog")]
SpanExporter::Datadog(_) => Err(InitError::Exporter {
exporter: ExporterKind::DatadogNative,
reason: "Datadog native exporter is not yet implemented".to_string(),
}),
SpanExporter::Console(exporter_config) => {
let exporter = apollo_opentelemetry_stdout::__private::StdoutSpanExporter::with_writer(
exporter_config.format.into(),
exporter_config.target.into(),
exporter_config.use_color(),
);
let exporter = InstrumentedSpanExporter::new(exporter, ExporterKind::Console, None);
let instance_id = exporter.instance_id();
let batch_processor = BatchSpanProcessor::builder(exporter)
.with_batch_config(batch_config)
.build();
let rate_limited = RateLimitedSpanProcessor::new(
batch_processor,
max_rate,
ExporterKind::Console,
instance_id,
);
Ok(builder.with_span_processor(rate_limited))
}
}
}
#[cfg(test)]
mod tests {
use apollo_configuration::parse_yaml;
use opentelemetry_sdk::trace::SdkTracerProvider;
use super::*;
#[test]
fn span_limits_conversion() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
limits:
attribute_count_limit: 64
event_count_limit: 256
link_count_limit: 32
event_attribute_count_limit: 16
link_attribute_count_limit: 8
"},
&Default::default(),
)
.unwrap();
let limits_config = &config.tracer_provider.limits;
let limits: SpanLimits = limits_config.into();
assert_eq!(limits.max_attributes_per_span, 64);
assert_eq!(limits.max_events_per_span, 256);
assert_eq!(limits.max_links_per_span, 32);
assert_eq!(limits.max_attributes_per_event, 16);
assert_eq!(limits.max_attributes_per_link, 8);
}
#[test]
fn span_limits_default_values() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors: []
"},
&Default::default(),
)
.unwrap();
let limits_config = &config.tracer_provider.limits;
let limits: SpanLimits = limits_config.into();
assert_eq!(limits.max_attributes_per_span, 128);
assert_eq!(limits.max_events_per_span, 128);
assert_eq!(limits.max_links_per_span, 128);
assert_eq!(limits.max_attributes_per_event, 128);
assert_eq!(limits.max_attributes_per_link, 128);
}
#[cfg(feature = "otlp")]
#[test]
fn build_tracer_provider_with_otlp_http() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- batch:
exporter:
otlp_http:
endpoint: http://localhost:4318
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_tracer_provider_with_resource() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
resource:
attributes:
- name: service.name
value: test-service
tracer_provider:
processors:
- batch:
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_tracer_provider_with_sampler() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
sampler:
trace_id_ratio_based:
ratio: 0.5
processors:
- batch:
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_tracer_provider_with_simple_processor() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- simple:
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_tracer_provider_with_batch_processor_custom_settings() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- batch:
schedule_delay: 1000
max_export_batch_size: 2500
max_queue_size: 1024
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_tracer_provider_with_rate_limited_processor() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[test]
fn build_tracer_provider_with_rate_limited_processor_custom_batch_settings() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- rate_limited:
max_rate: 500
schedule_delay: 1000
max_export_batch_size: 2500
max_queue_size: 1024
exporter:
console: {}
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "otlp")]
#[tokio::test]
async fn build_tracer_provider_with_rate_limited_otlp_grpc() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
otlp_grpc:
endpoint: http://localhost:4317
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "honeycomb")]
#[tokio::test]
async fn build_tracer_provider_with_rate_limited_honeycomb() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
honeycomb_http:
api_key: test-key
endpoint: https://api.honeycomb.io
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "new-relic")]
#[tokio::test]
async fn build_tracer_provider_with_rate_limited_new_relic() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
new_relic_http:
license_key: test-key
endpoint: https://otlp.nr-data.net:4318
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
#[cfg(feature = "datadog-otlp")]
#[tokio::test]
async fn build_tracer_provider_with_rate_limited_datadog() {
let config: OpenTelemetryConfig = parse_yaml(
indoc::indoc! {"
tracer_provider:
processors:
- rate_limited:
max_rate: 100
exporter:
datadog_otlp_http:
api_key: test-key
endpoint: https://trace-otlp.intake.datadoghq.com/api/v0.2/traces
"},
&Default::default(),
)
.unwrap();
let builder = SdkTracerProvider::builder();
let result = build_tracer_provider(&config, (&config.resource).into(), builder);
assert!(result.is_ok());
}
}