From 3ed31e69d618abc06c53ca58d05e878c73575557 Mon Sep 17 00:00:00 2001 From: Jordan Gonzalez <30836115+duncanista@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:35:07 -0500 Subject: [PATCH 1/3] feat(propagation): decouple propagation from Config with PropagationConfig trait Introduce a `PropagationConfig` trait that abstracts the 5 configuration fields needed by propagators, allowing external consumers (e.g. Lambda extension) to reuse the propagation module without depending on the full `Config` struct. - Make `propagation` module unconditionally public (was gated behind `test-utils` feature) - Add `PropagationConfig` trait with methods for propagation styles, extract-first behavior, and tags max length - Make `DatadogCompositePropagator` generic over `C: PropagationConfig` for zero-cost monomorphization - Implement `PropagationConfig` for `Config` to maintain backward compatibility - Re-export `TracePropagationStyle` from the propagation module --- datadog-opentelemetry/src/lib.rs | 11 ++- .../src/propagation/config.rs | 8 +- .../src/propagation/datadog.rs | 17 +++- datadog-opentelemetry/src/propagation/mod.rs | 94 +++++++++++++++---- .../propagation/trace_propagation_style.rs | 17 ++-- .../src/text_map_propagator.rs | 2 +- 6 files changed, 111 insertions(+), 38 deletions(-) diff --git a/datadog-opentelemetry/src/lib.rs b/datadog-opentelemetry/src/lib.rs index edd94f5d..b34d4b70 100644 --- a/datadog-opentelemetry/src/lib.rs +++ b/datadog-opentelemetry/src/lib.rs @@ -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; diff --git a/datadog-opentelemetry/src/propagation/config.rs b/datadog-opentelemetry/src/propagation/config.rs index a747349e..ea31576f 100644 --- a/datadog-opentelemetry/src/propagation/config.rs +++ b/datadog-opentelemetry/src/propagation/config.rs @@ -1,9 +1,11 @@ // 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 { @@ -11,7 +13,7 @@ pub(super) fn get_extractors(config: &Config) -> &[TracePropagationStyle] { } } -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 { diff --git a/datadog-opentelemetry/src/propagation/datadog.rs b/datadog-opentelemetry/src/propagation/datadog.rs index 3cb837a2..51b388db 100644 --- a/datadog-opentelemetry/src/propagation/datadog.rs +++ b/datadog-opentelemetry/src/propagation/datadog.rs @@ -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, @@ -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 @@ -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); @@ -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 { +pub fn extract( + carrier: &dyn Extractor, + config: &(impl PropagationConfig + ?Sized), +) -> Option { let lower_trace_id = match extract_trace_id(carrier) { Ok(trace_id) => trace_id?, Err(e) => { @@ -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}, }; diff --git a/datadog-opentelemetry/src/propagation/mod.rs b/datadog-opentelemetry/src/propagation/mod.rs index 7ec08ae2..7db39b26 100644 --- a/datadog-opentelemetry/src/propagation/mod.rs +++ b/datadog-opentelemetry/src/propagation/mod.rs @@ -6,7 +6,6 @@ use std::sync::Arc; use crate::{ - core::configuration::{Config, TracePropagationStyle}, dd_debug, propagation::context::{InjectSpanContext, SpanContext, SpanLink}, }; @@ -23,9 +22,41 @@ 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; - 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 { + fn extract(&self, carrier: &dyn Extractor, config: &C) -> Option; + fn inject( + &self, + context: &mut InjectSpanContext, + carrier: &mut dyn Injector, + config: &C, + ); fn keys(&self) -> &[String]; } @@ -33,22 +64,26 @@ pub(crate) trait Propagator { /// /// 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, +pub struct DatadogCompositePropagator { + config: Arc, extractors: Vec, injectors: Vec, keys: Vec, } -impl DatadogCompositePropagator { +impl DatadogCompositePropagator { /// 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) -> Self { - let extractors = get_extractors(&config); + pub fn new(config: Arc) -> Self { + let extractors = get_extractors(config.as_ref()); let mut num_propagators = extractors.len(); if config.trace_propagation_extract_first() { num_propagators = 1; @@ -61,15 +96,14 @@ impl DatadogCompositePropagator { .copied() .collect(); - let injectors: Vec = get_injectors(&config) + let injectors: Vec = 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() + >::keys(extractor) .iter() .for_each(|key| keys.push(key.clone())); keys @@ -87,7 +121,7 @@ impl DatadogCompositePropagator { /// /// Returns `None` if no valid trace context is found in the carrier. pub fn extract(&self, carrier: &dyn Extractor) -> Option { - let contexts = self.extract_available_contexts(carrier, &self.config); + let contexts = self.extract_available_contexts(carrier); if contexts.is_empty() { return None; } @@ -101,7 +135,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. @@ -112,12 +146,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)); } @@ -199,6 +232,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 { @@ -235,6 +290,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; @@ -945,7 +1001,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); } @@ -967,7 +1023,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); } diff --git a/datadog-opentelemetry/src/propagation/trace_propagation_style.rs b/datadog-opentelemetry/src/propagation/trace_propagation_style.rs index 27e1534d..b035aea3 100644 --- a/datadog-opentelemetry/src/propagation/trace_propagation_style.rs +++ b/datadog-opentelemetry/src/propagation/trace_propagation_style.rs @@ -1,19 +1,19 @@ // 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 { +impl Propagator for TracePropagationStyle { + fn extract(&self, carrier: &dyn Extractor, config: &C) -> Option { match self { Self::Datadog => datadog::extract(carrier, config), Self::TraceContext => tracecontext::extract(carrier), @@ -21,7 +21,12 @@ impl Propagator for TracePropagationStyle { } } - 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), diff --git a/datadog-opentelemetry/src/text_map_propagator.rs b/datadog-opentelemetry/src/text_map_propagator.rs index 026b225d..cfb4d278 100644 --- a/datadog-opentelemetry/src/text_map_propagator.rs +++ b/datadog-opentelemetry/src/text_map_propagator.rs @@ -61,7 +61,7 @@ impl DatadogExtractData { #[derive(Debug)] pub struct DatadogPropagator { - inner: DatadogCompositePropagator, + inner: DatadogCompositePropagator, registry: TraceRegistry, cfg: Arc, } From e17fadb1d56661f5cccf20027e68912d320f541f Mon Sep 17 00:00:00 2001 From: Jordan Gonzalez <30836115+duncanista@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:35:54 -0500 Subject: [PATCH 2/3] fmt --- datadog-opentelemetry/src/propagation/mod.rs | 7 +------ .../src/propagation/trace_propagation_style.rs | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/datadog-opentelemetry/src/propagation/mod.rs b/datadog-opentelemetry/src/propagation/mod.rs index 7db39b26..30ab50a5 100644 --- a/datadog-opentelemetry/src/propagation/mod.rs +++ b/datadog-opentelemetry/src/propagation/mod.rs @@ -51,12 +51,7 @@ pub trait PropagationConfig: Send + Sync { pub(crate) trait Propagator { fn extract(&self, carrier: &dyn Extractor, config: &C) -> Option; - fn inject( - &self, - context: &mut InjectSpanContext, - carrier: &mut dyn Injector, - config: &C, - ); + fn inject(&self, context: &mut InjectSpanContext, carrier: &mut dyn Injector, config: &C); fn keys(&self) -> &[String]; } diff --git a/datadog-opentelemetry/src/propagation/trace_propagation_style.rs b/datadog-opentelemetry/src/propagation/trace_propagation_style.rs index b035aea3..735424fa 100644 --- a/datadog-opentelemetry/src/propagation/trace_propagation_style.rs +++ b/datadog-opentelemetry/src/propagation/trace_propagation_style.rs @@ -21,12 +21,7 @@ impl Propagator for TracePropagationStyle { } } - fn inject( - &self, - context: &mut InjectSpanContext, - carrier: &mut dyn Injector, - config: &C, - ) { + 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), From 5f85bc332f056e40287aecda8ab0ab7328a24c80 Mon Sep 17 00:00:00 2001 From: Jordan Gonzalez <30836115+duncanista@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:35:53 -0400 Subject: [PATCH 3/3] retrigger ci