Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions datadog-opentelemetry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,21 @@ pub mod core_pub_hack {
pub use crate::core::*;
}

/// Distributed trace propagation logic.
///
/// This module provides types and traits for extracting and injecting trace
/// context across service boundaries using multiple propagation formats
/// (Datadog, W3C Trace Context).
pub mod propagation;

#[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 mappings;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod propagation;
#[cfg(not(feature = "test-utils"))]
pub(crate) mod sampling;

mod ddtrace_transform;
Expand Down
8 changes: 5 additions & 3 deletions datadog-opentelemetry/src/propagation/config.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use crate::configuration::{Config, TracePropagationStyle};
use crate::configuration::TracePropagationStyle;

pub(super) fn get_extractors(config: &Config) -> &[TracePropagationStyle] {
use crate::propagation::PropagationConfig;

pub(super) fn get_extractors(config: &impl PropagationConfig) -> &[TracePropagationStyle] {
if let Some(extractors) = config.trace_propagation_style_extract() {
extractors
} else {
config.trace_propagation_style().unwrap_or_default()
}
}

pub(super) fn get_injectors(config: &Config) -> &[TracePropagationStyle] {
pub(super) fn get_injectors(config: &impl PropagationConfig) -> &[TracePropagationStyle] {
if let Some(injectors) = config.trace_propagation_style_inject() {
injectors
} else {
Expand Down
17 changes: 12 additions & 5 deletions datadog-opentelemetry/src/propagation/datadog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use std::{collections::HashMap, str::FromStr, sync::LazyLock};

use super::{
use crate::propagation::{
carrier::{Extractor, Injector},
context::{
combine_trace_id, split_trace_id, InjectSpanContext, Sampling, SpanContext,
Expand All @@ -14,11 +14,11 @@ use super::{

use crate::{
core::{
configuration::Config,
constants::SAMPLING_DECISION_MAKER_TAG_KEY,
sampling::{SamplingMechanism, SamplingPriority},
},
dd_debug, dd_error, dd_warn,
propagation::PropagationConfig,
};

// Datadog Keys
Expand All @@ -41,7 +41,11 @@ static DATADOG_HEADER_KEYS: LazyLock<[String; 5]> = LazyLock::new(|| {
]
});

pub fn inject(context: &mut InjectSpanContext, carrier: &mut dyn Injector, config: &Config) {
pub fn inject(
context: &mut InjectSpanContext,
carrier: &mut dyn Injector,
config: &(impl PropagationConfig + ?Sized),
) {
let tags = &mut context.tags;

inject_trace_id(context.trace_id, carrier, tags);
Expand Down Expand Up @@ -179,7 +183,10 @@ fn validate_tag_value(value: &str) -> bool {
.all(|c| matches!(c, b' '..=b'+' | b'-'..=b'~'))
}

pub fn extract(carrier: &dyn Extractor, config: &Config) -> Option<SpanContext> {
pub fn extract(
carrier: &dyn Extractor,
config: &(impl PropagationConfig + ?Sized),
) -> Option<SpanContext> {
let lower_trace_id = match extract_trace_id(carrier) {
Ok(trace_id) => trace_id?,
Err(e) => {
Expand Down Expand Up @@ -368,7 +375,7 @@ pub fn keys() -> &'static [String] {
#[allow(clippy::unwrap_used)]
mod test {
use crate::core::{
configuration::TracePropagationStyle,
configuration::{Config, TracePropagationStyle},
sampling::{mechanism, priority},
};

Expand Down
89 changes: 70 additions & 19 deletions datadog-opentelemetry/src/propagation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use std::sync::Arc;

use crate::{
core::configuration::{Config, TracePropagationStyle},
dd_debug,
propagation::context::{InjectSpanContext, SpanContext, SpanLink},
};
Expand All @@ -23,32 +22,63 @@ mod error;
pub(crate) mod trace_propagation_style;
pub(crate) mod tracecontext;

pub(crate) trait Propagator {
fn extract(&self, carrier: &dyn Extractor, config: &Config) -> Option<SpanContext>;
fn inject(&self, context: &mut InjectSpanContext, carrier: &mut dyn Injector, config: &Config);
pub use crate::core::configuration::TracePropagationStyle;

/// Configuration required for trace context propagation.
///
/// This trait abstracts the configuration needed by propagators, allowing
/// consumers to provide their own configuration implementation without
/// depending on the full [`Config`](crate::configuration::Config) struct.
pub trait PropagationConfig: Send + Sync {
/// Returns the default trace propagation styles (used as fallback for both
/// extract and inject if specific styles are not configured).
fn trace_propagation_style(&self) -> Option<&[TracePropagationStyle]>;

/// Returns the trace propagation styles to use for extraction.
/// Falls back to [`trace_propagation_style`](Self::trace_propagation_style) if `None`.
fn trace_propagation_style_extract(&self) -> Option<&[TracePropagationStyle]>;

/// Returns the trace propagation styles to use for injection.
/// Falls back to [`trace_propagation_style`](Self::trace_propagation_style) if `None`.
fn trace_propagation_style_inject(&self) -> Option<&[TracePropagationStyle]>;

/// Whether to stop extraction after the first successful propagator.
fn trace_propagation_extract_first(&self) -> bool;

/// Maximum length of the `x-datadog-tags` header value.
fn datadog_tags_max_length(&self) -> usize;
}

pub(crate) trait Propagator<C: PropagationConfig + ?Sized> {
fn extract(&self, carrier: &dyn Extractor, config: &C) -> Option<SpanContext>;
fn inject(&self, context: &mut InjectSpanContext, carrier: &mut dyn Injector, config: &C);
fn keys(&self) -> &[String];
}

/// A composite propagator that supports multiple trace context propagation formats.
///
/// This propagator can extract and inject trace context using multiple formats
/// (e.g., Datadog, W3C Trace Context) based on the provided configuration.
///
/// The type parameter `C` is the configuration type, which must implement
/// [`PropagationConfig`]. Use [`crate::configuration::Config`] for the default
/// Datadog configuration, or provide your own implementation.
#[derive(Debug)]
pub struct DatadogCompositePropagator {
config: Arc<Config>,
pub struct DatadogCompositePropagator<C: PropagationConfig> {
config: Arc<C>,
extractors: Vec<TracePropagationStyle>,
injectors: Vec<TracePropagationStyle>,
keys: Vec<String>,
}

impl DatadogCompositePropagator {
impl<C: PropagationConfig> DatadogCompositePropagator<C> {
/// Creates a new composite propagator with the given configuration.
///
/// The propagator will use the extraction and injection styles configured in the
/// provided [`Config`], respecting settings like `trace_propagation_extract_first`.
/// provided configuration, respecting settings like `trace_propagation_extract_first`.
#[must_use]
pub fn new(config: Arc<Config>) -> Self {
let extractors = get_extractors(&config);
pub fn new(config: Arc<C>) -> Self {
let extractors = get_extractors(config.as_ref());
let mut num_propagators = extractors.len();
if config.trace_propagation_extract_first() {
num_propagators = 1;
Expand All @@ -61,15 +91,14 @@ impl DatadogCompositePropagator {
.copied()
.collect();

let injectors: Vec<TracePropagationStyle> = get_injectors(&config)
let injectors: Vec<TracePropagationStyle> = get_injectors(config.as_ref())
.iter()
.filter(|style| **style != TracePropagationStyle::None)
.copied()
.collect();

let keys = extractors.iter().fold(Vec::new(), |mut keys, extractor| {
extractor
.keys()
<TracePropagationStyle as Propagator<C>>::keys(extractor)
.iter()
.for_each(|key| keys.push(key.clone()));
keys
Expand All @@ -87,7 +116,7 @@ impl DatadogCompositePropagator {
///
/// Returns `None` if no valid trace context is found in the carrier.
pub fn extract(&self, carrier: &dyn Extractor) -> Option<SpanContext> {
let contexts = self.extract_available_contexts(carrier, &self.config);
let contexts = self.extract_available_contexts(carrier);
if contexts.is_empty() {
return None;
}
Expand All @@ -101,7 +130,7 @@ impl DatadogCompositePropagator {
pub fn inject(&self, context: &mut InjectSpanContext, carrier: &mut dyn Injector) {
self.injectors
.iter()
.for_each(|propagator| propagator.inject(context, carrier, &self.config));
.for_each(|propagator| propagator.inject(context, carrier, self.config.as_ref()));
}

/// Returns the header keys used by the configured extractors.
Expand All @@ -112,12 +141,11 @@ impl DatadogCompositePropagator {
fn extract_available_contexts(
&self,
carrier: &dyn Extractor,
config: &Config,
) -> Vec<(SpanContext, TracePropagationStyle)> {
let mut contexts = vec![];

for propagator in self.extractors.iter() {
if let Some(context) = propagator.extract(carrier, config) {
if let Some(context) = propagator.extract(carrier, self.config.as_ref()) {
dd_debug!("Propagator ({propagator}): extracted {context:#?}");
contexts.push((context, *propagator));
}
Expand Down Expand Up @@ -199,6 +227,28 @@ impl DatadogCompositePropagator {
}
}

impl PropagationConfig for crate::core::configuration::Config {
fn trace_propagation_style(&self) -> Option<&[TracePropagationStyle]> {
self.trace_propagation_style()
}

fn trace_propagation_style_extract(&self) -> Option<&[TracePropagationStyle]> {
self.trace_propagation_style_extract()
}

fn trace_propagation_style_inject(&self) -> Option<&[TracePropagationStyle]> {
self.trace_propagation_style_inject()
}

fn trace_propagation_extract_first(&self) -> bool {
self.trace_propagation_extract_first()
}

fn datadog_tags_max_length(&self) -> usize {
self.datadog_tags_max_length()
}
}

pub(crate) const fn const_append(source: &[u8], dest: &mut [u8], at: usize) {
let mut i = 0;
loop {
Expand Down Expand Up @@ -235,6 +285,7 @@ pub(crate) mod tests {

use assert_unordered::assert_eq_unordered;

use crate::core::configuration::Config;
use crate::core::sampling::{mechanism, priority};
use pretty_assertions::assert_eq;

Expand Down Expand Up @@ -945,7 +996,7 @@ pub(crate) mod tests {
"_dd.p.test=value,_dd.p.tid=9291375655657946024,any=tag".to_string(),
),
]);
let contexts = propagator.extract_available_contexts(&carrier, &config);
let contexts = propagator.extract_available_contexts(&carrier);

assert_eq!(contexts.len(), 2);
}
Expand All @@ -967,7 +1018,7 @@ pub(crate) mod tests {
"dd=p:00f067aa0ba902b7;s:2;o:rum".to_string(),
),
]);
let contexts = propagator.extract_available_contexts(&carrier, &config);
let contexts = propagator.extract_available_contexts(&carrier);

assert_eq!(contexts.len(), 0);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use crate::core::configuration::{Config, TracePropagationStyle};
use crate::core::configuration::TracePropagationStyle;
use serde::{Deserialize, Deserializer};

use super::{
use crate::propagation::{
carrier::{Extractor, Injector},
context::{InjectSpanContext, SpanContext},
datadog, tracecontext, Propagator,
datadog, tracecontext, PropagationConfig, Propagator,
};

const NONE_KEYS: [String; 0] = [];

impl Propagator for TracePropagationStyle {
fn extract(&self, carrier: &dyn Extractor, config: &Config) -> Option<SpanContext> {
impl<C: PropagationConfig + ?Sized> Propagator<C> for TracePropagationStyle {
fn extract(&self, carrier: &dyn Extractor, config: &C) -> Option<SpanContext> {
match self {
Self::Datadog => datadog::extract(carrier, config),
Self::TraceContext => tracecontext::extract(carrier),
_ => None,
}
}

fn inject(&self, context: &mut InjectSpanContext, carrier: &mut dyn Injector, config: &Config) {
fn inject(&self, context: &mut InjectSpanContext, carrier: &mut dyn Injector, config: &C) {
match self {
Self::Datadog => datadog::inject(context, carrier, config),
Self::TraceContext => tracecontext::inject(context, carrier),
Expand Down
2 changes: 1 addition & 1 deletion datadog-opentelemetry/src/text_map_propagator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl DatadogExtractData {

#[derive(Debug)]
pub struct DatadogPropagator {
inner: DatadogCompositePropagator,
inner: DatadogCompositePropagator<Config>,
registry: TraceRegistry,
cfg: Arc<Config>,
}
Expand Down
Loading