#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub(crate) mod core;
pub use core::configuration;
pub use core::log;
#[cfg(feature = "test-utils")]
#[allow(missing_docs)]
pub mod core_pub_hack {
pub use crate::core::*;
}
#[cfg(feature = "test-utils")]
pub mod mappings;
#[cfg(feature = "test-utils")]
pub mod propagation;
#[cfg(feature = "test-utils")]
pub mod sampler;
#[cfg(feature = "test-utils")]
pub mod sampling;
#[cfg(feature = "test-utils")]
pub mod span_processor;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod mappings;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod propagation;
#[cfg(not(feature = "test-utils"))]
mod sampler;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod sampling;
#[cfg(not(feature = "test-utils"))]
mod span_processor;
mod ddtrace_transform;
mod exporter;
#[cfg(any(feature = "logs-grpc", feature = "logs-http"))]
mod logs_reader;
#[cfg(any(feature = "metrics-grpc", feature = "metrics-http"))]
mod metrics_reader;
mod otlp_utils;
mod span_exporter;
mod spans_metrics;
#[cfg(any(feature = "logs-grpc", feature = "logs-http"))]
mod telemetry_logs_exporter;
#[cfg(any(feature = "metrics-grpc", feature = "metrics-http"))]
mod telemetry_metrics_exporter;
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) {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
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(),
Some(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)
})) {
Ok(result) => result,
Err(_) => {
crate::dd_error!("Failed to initialize Datadog tracer provider. A no-op tracer provider will be used instead. Traces will not be exported.");
(
SdkTracerProvider::builder().build(),
DatadogPropagator::new(config.clone(), TraceRegistry::new(config)),
)
}
}
}
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) {
#![allow(missing_docs)]
make_tracer(
shared_config,
opentelemetry_sdk::trace::TracerProviderBuilder::default(),
None,
)
}
#[cfg(any(feature = "metrics-grpc", feature = "metrics-http", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
pub struct DatadogMetricsBuilder {
config: Option<Config>,
resource: Option<Resource>,
export_interval: Option<std::time::Duration>,
}
#[cfg(any(feature = "metrics-grpc", feature = "metrics-http", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
impl DatadogMetricsBuilder {
pub fn with_config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub fn with_resource(mut self, resource: Resource) -> Self {
self.resource = Some(resource);
self
}
pub fn with_export_interval(mut self, interval: std::time::Duration) -> Self {
self.export_interval = Some(interval);
self
}
pub fn init(self) -> opentelemetry_sdk::metrics::SdkMeterProvider {
let meter_provider = self.init_local();
opentelemetry::global::set_meter_provider(meter_provider.clone());
meter_provider
}
pub fn init_local(self) -> opentelemetry_sdk::metrics::SdkMeterProvider {
let config = self.config.unwrap_or_else(|| Config::builder().build());
metrics_reader::create_meter_provider(Arc::new(config), self.resource, self.export_interval)
}
}
#[cfg(any(feature = "metrics-grpc", feature = "metrics-http", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
pub fn metrics() -> DatadogMetricsBuilder {
DatadogMetricsBuilder {
config: None,
resource: None,
export_interval: None,
}
}
#[cfg(any(feature = "logs-grpc", feature = "logs-http", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
pub struct DatadogLogsBuilder {
config: Option<Config>,
resource: Option<Resource>,
}
#[cfg(any(feature = "logs-grpc", feature = "logs-http", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
impl DatadogLogsBuilder {
pub fn with_config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub fn with_resource(mut self, resource: Resource) -> Self {
self.resource = Some(resource);
self
}
pub fn init(self) -> opentelemetry_sdk::logs::SdkLoggerProvider {
self.init_local()
}
pub fn init_local(self) -> opentelemetry_sdk::logs::SdkLoggerProvider {
let config = self.config.unwrap_or_else(|| Config::builder().build());
logs_reader::create_logger_provider(Arc::new(config), self.resource)
}
}
#[cfg(any(feature = "logs-grpc", feature = "logs-http", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
pub fn logs() -> DatadogLogsBuilder {
DatadogLogsBuilder {
config: None,
resource: None,
}
}