pub use core::configuration;
pub use core::log;
#[cfg(feature = "test-utils")]
pub mod core;
#[cfg(feature = "test-utils")]
pub mod mappings;
#[cfg(feature = "test-utils")]
pub mod propagation;
#[cfg(feature = "test-utils")]
pub mod sampling;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod core;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod mappings;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod propagation;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod sampling;
mod ddtrace_transform;
mod sampler;
mod span_exporter;
mod span_processor;
mod spans_metrics;
mod text_map_propagator;
mod trace_id;
use std::sync::{Arc, RwLock};
use opentelemetry::{Key, KeyValue, Value};
use opentelemetry_sdk::{trace::SdkTracerProvider, Resource};
use opentelemetry_semantic_conventions::resource::{DEPLOYMENT_ENVIRONMENT_NAME, SERVICE_NAME};
use crate::{
core::configuration::{Config, RemoteConfigUpdate},
sampler::Sampler,
span_processor::{DatadogSpanProcessor, TraceRegistry},
text_map_propagator::DatadogPropagator,
};
pub struct DatadogTracingBuilder {
config: Option<Config>,
resource: Option<opentelemetry_sdk::Resource>,
tracer_provider: opentelemetry_sdk::trace::TracerProviderBuilder,
}
impl DatadogTracingBuilder {
pub fn with_config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub fn with_resource(mut self, resource: opentelemetry_sdk::Resource) -> Self {
self.resource = Some(resource);
self
}
pub fn init(self) -> SdkTracerProvider {
let (tracer_provider, propagator) = self.init_local();
opentelemetry::global::set_text_map_propagator(propagator);
opentelemetry::global::set_tracer_provider(tracer_provider.clone());
tracer_provider
}
pub fn init_local(self) -> (SdkTracerProvider, DatadogPropagator) {
let config = self.config.unwrap_or_else(|| Config::builder().build());
make_tracer(Arc::new(config), self.tracer_provider, self.resource)
}
}
impl DatadogTracingBuilder {
pub fn with_span_processor<T: opentelemetry_sdk::trace::SpanProcessor + 'static>(
mut self,
processor: T,
) -> Self {
self.tracer_provider = self.tracer_provider.with_span_processor(processor);
self
}
pub fn with_max_events_per_span(mut self, max_events: u32) -> Self {
self.tracer_provider = self.tracer_provider.with_max_events_per_span(max_events);
self
}
pub fn with_max_attributes_per_span(mut self, max_attributes: u32) -> Self {
self.tracer_provider = self
.tracer_provider
.with_max_attributes_per_span(max_attributes);
self
}
pub fn with_max_links_per_span(mut self, max_links: u32) -> Self {
self.tracer_provider = self.tracer_provider.with_max_links_per_span(max_links);
self
}
pub fn with_max_attributes_per_event(mut self, max_attributes: u32) -> Self {
self.tracer_provider = self
.tracer_provider
.with_max_attributes_per_event(max_attributes);
self
}
pub fn with_max_attributes_per_link(mut self, max_attributes: u32) -> Self {
self.tracer_provider = self
.tracer_provider
.with_max_attributes_per_link(max_attributes);
self
}
pub fn with_span_limits(mut self, span_limits: opentelemetry_sdk::trace::SpanLimits) -> Self {
self.tracer_provider = self.tracer_provider.with_span_limits(span_limits);
self
}
}
pub fn tracing() -> DatadogTracingBuilder {
DatadogTracingBuilder {
config: None,
tracer_provider: opentelemetry_sdk::trace::SdkTracerProvider::builder(),
resource: None,
}
}
fn make_tracer(
config: Arc<Config>,
mut tracer_provider_builder: opentelemetry_sdk::trace::TracerProviderBuilder,
resource: Option<Resource>,
) -> (SdkTracerProvider, DatadogPropagator) {
let registry = TraceRegistry::new(config.clone());
let resource_slot = Arc::new(RwLock::new(Resource::builder_empty().build()));
let sampler = Sampler::new(config.clone(), resource_slot.clone(), registry.clone());
let agent_response_handler = sampler.on_agent_response();
let dd_resource = create_dd_resource(resource.unwrap_or(Resource::builder().build()), &config);
tracer_provider_builder = tracer_provider_builder.with_resource(dd_resource);
let propagator = DatadogPropagator::new(config.clone(), registry.clone());
if config.remote_config_enabled() {
let sampler_callback = sampler.on_rules_update();
config.set_sampling_rules_callback(move |update| match update {
RemoteConfigUpdate::SamplingRules(rules) => {
sampler_callback(rules);
}
});
};
let mut tracer_provider_builder = tracer_provider_builder
.with_sampler(sampler) .with_id_generator(trace_id::TraceidGenerator);
if config.enabled() {
let span_processor = DatadogSpanProcessor::new(
config.clone(),
registry.clone(),
resource_slot.clone(),
Some(agent_response_handler),
);
tracer_provider_builder = tracer_provider_builder.with_span_processor(span_processor);
}
let tracer_provider = tracer_provider_builder.build();
(tracer_provider, propagator)
}
fn merge_resource<I: IntoIterator<Item = (Key, Value)>>(
base: Option<Resource>,
additional: I,
) -> Resource {
let mut builder = opentelemetry_sdk::Resource::builder_empty();
if let Some(base) = base {
if let Some(schema_url) = base.schema_url() {
builder = builder.with_schema_url(
base.iter()
.map(|(k, v)| KeyValue::new(k.clone(), v.clone())),
schema_url.to_string(),
);
} else {
builder = builder.with_attributes(
base.iter()
.map(|(k, v)| KeyValue::new(k.clone(), v.clone())),
);
}
}
builder = builder.with_attributes(additional.into_iter().map(|(k, v)| KeyValue::new(k, v)));
builder.build()
}
fn create_dd_resource(resource: Resource, cfg: &Config) -> Resource {
let otel_service_name: Option<Value> = resource.get(&Key::from_static_str(SERVICE_NAME));
let mut attributes = Vec::new();
if otel_service_name.is_none() || otel_service_name.unwrap().as_str() == "unknown_service" {
attributes.push((
Key::from_static_str(SERVICE_NAME),
Value::from(cfg.service().to_string()),
));
} else if !cfg.service_is_default() {
attributes.push((
Key::from_static_str(SERVICE_NAME),
Value::from(cfg.service().to_string()),
));
}
if let Some(env) = cfg.env() {
let otel_env: Option<Value> =
resource.get(&Key::from_static_str(DEPLOYMENT_ENVIRONMENT_NAME));
if otel_env.is_none() {
attributes.push((
Key::from_static_str(DEPLOYMENT_ENVIRONMENT_NAME),
Value::from(env.to_string()),
));
}
}
if attributes.is_empty() {
resource
} else {
merge_resource(Some(resource), attributes)
}
}
#[cfg(feature = "test-utils")]
pub fn make_test_tracer(shared_config: Arc<Config>) -> (SdkTracerProvider, DatadogPropagator) {
make_tracer(
shared_config,
opentelemetry_sdk::trace::TracerProviderBuilder::default(),
None,
)
}