Skip to content

Commit 54ddaab

Browse files
authored
feat(traces): migrate trace propagation to dd-trace-rs (#1089)
## Overview Migrate trace context propagation from a local implementation to the datadog-opentelemetry crate in dd-trace-rs. This removes ~750 lines of propagation logic (Datadog headers, W3C TraceContext) that we were maintaining locally in favor of the shared upstream implementation. Key changes: - Add datadog-opentelemetry as a dependency - Delete local propagation modules: traces/context.rs, traces/propagation/error.rs, traces/propagation/text_map_propagator.rs - Keep a thin DatadogCompositePropagator wrapper that adds ot-baggage-* header extraction, which is not yet supported upstream - Update all consumers to import SpanContext, Sampling, TracePropagationStyle, and header constants directly from datadog-opentelemetry - Adapt to upstream types: u128 trace IDs (cast to u64 for Datadog protocol), Sampling struct, SamplingPriority newtype # Motivation Not have to own this piece as its tracer logic and [SVLS-7466](https://datadoghq.atlassian.net/browse/SVLS-7466) ## Testing - All 476 existing tests pass - Propagation tests updated to use Datadog/TraceContext styles (upstream does not expose B3/B3Multi variants) - Config parsing tests updated to reflect valid upstream style names - [Trace Propagation e2e tests pass](https://gitlab.ddbuild.io/DataDog/serverless-e2e-tests/-/pipelines/101405643) Co-authored-by: jordan.gonzalez <jordan.gonzalez@datadoghq.com>
1 parent 55659d9 commit 54ddaab

File tree

21 files changed

+1034
-1841
lines changed

21 files changed

+1034
-1841
lines changed

bottlecap/Cargo.lock

Lines changed: 757 additions & 197 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bottlecap/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ libdd-trace-utils = { git = "https://github.com/DataDog/libdatadog", rev = "c812
7777
libdd-trace-normalization = { git = "https://github.com/DataDog/libdatadog", rev = "c8121f422d2c8d219f8d421ff3cdb1fcbe9e8b09" }
7878
libdd-trace-obfuscation = { git = "https://github.com/DataDog/libdatadog", rev = "c8121f422d2c8d219f8d421ff3cdb1fcbe9e8b09" }
7979
libdd-trace-stats = { git = "https://github.com/DataDog/libdatadog", rev = "c8121f422d2c8d219f8d421ff3cdb1fcbe9e8b09" }
80+
datadog-opentelemetry = { git = "https://github.com/DataDog/dd-trace-rs", rev = "f51cefc4ad24bec81b38fb2f36b1ed93f21ae913", default-features = false }
8081
dogstatsd = { git = "https://github.com/DataDog/serverless-components", rev = "28f796bf767fff56caf08153ade5cd80c8e8f705", default-features = false }
8182
datadog-fips = { git = "https://github.com/DataDog/serverless-components", rev = "28f796bf767fff56caf08153ade5cd80c8e8f705", default-features = false }
8283
libddwaf = { version = "1.28.1", git = "https://github.com/DataDog/libddwaf-rust", rev = "d1534a158d976bd4f747bf9fcc58e0712d2d17fc", default-features = false, features = ["serde"] }

bottlecap/LICENSE-3rdparty.csv

Lines changed: 31 additions & 0 deletions
Large diffs are not rendered by default.

bottlecap/src/config/env.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ use crate::{
2424
},
2525
processing_rule::{ProcessingRule, deserialize_processing_rules},
2626
service_mapping::deserialize_service_mapping,
27-
trace_propagation_style::{TracePropagationStyle, deserialize_trace_propagation_style},
27+
trace_propagation_style::deserialize_trace_propagation_style,
2828
},
2929
merge_hashmap, merge_option, merge_option_to_value, merge_string, merge_vec,
3030
};
31+
use datadog_opentelemetry::propagation::TracePropagationStyle;
3132

3233
#[derive(Debug, PartialEq, Deserialize, Clone, Default)]
3334
#[serde(default)]
@@ -719,8 +720,8 @@ mod tests {
719720
flush_strategy::{FlushStrategy, PeriodicStrategy},
720721
log_level::LogLevel,
721722
processing_rule::{Kind, ProcessingRule},
722-
trace_propagation_style::TracePropagationStyle,
723723
};
724+
use datadog_opentelemetry::propagation::TracePropagationStyle;
724725

725726
#[test]
726727
#[allow(clippy::too_many_lines)]
@@ -802,7 +803,7 @@ mod tests {
802803
jail.set_env("DD_METRICS_CONFIG_COMPRESSION_LEVEL", "3");
803804
// Trace Propagation
804805
jail.set_env("DD_TRACE_PROPAGATION_STYLE", "datadog");
805-
jail.set_env("DD_TRACE_PROPAGATION_STYLE_EXTRACT", "b3");
806+
jail.set_env("DD_TRACE_PROPAGATION_STYLE_EXTRACT", "tracecontext");
806807
jail.set_env("DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true");
807808
jail.set_env("DD_TRACE_PROPAGATION_HTTP_BAGGAGE_ENABLED", "true");
808809
jail.set_env("DD_TRACE_AWS_SERVICE_REPRESENTATION_ENABLED", "true");
@@ -984,7 +985,7 @@ mod tests {
984985
"debug:^true$".to_string(),
985986
]),
986987
trace_propagation_style: vec![TracePropagationStyle::Datadog],
987-
trace_propagation_style_extract: vec![TracePropagationStyle::B3],
988+
trace_propagation_style_extract: vec![TracePropagationStyle::TraceContext],
988989
trace_propagation_extract_first: true,
989990
trace_propagation_http_baggage_enabled: true,
990991
trace_aws_service_representation_enabled: true,

bottlecap/src/config/mod.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ use crate::config::{
2929
log_level::LogLevel,
3030
logs_additional_endpoints::LogsAdditionalEndpoint,
3131
processing_rule::{ProcessingRule, deserialize_processing_rules},
32-
trace_propagation_style::TracePropagationStyle,
3332
yaml::YamlConfigSource,
3433
};
34+
use datadog_opentelemetry::propagation::TracePropagationStyle;
3535

3636
/// Helper macro to merge Option<String> fields to String fields
3737
///
@@ -488,6 +488,38 @@ impl Default for Config {
488488
}
489489
}
490490

491+
impl datadog_opentelemetry::propagation::PropagationConfig for Config {
492+
fn trace_propagation_style(&self) -> Option<&[TracePropagationStyle]> {
493+
if self.trace_propagation_style.is_empty() {
494+
None
495+
} else {
496+
Some(&self.trace_propagation_style)
497+
}
498+
}
499+
500+
fn trace_propagation_style_extract(&self) -> Option<&[TracePropagationStyle]> {
501+
if self.trace_propagation_style_extract.is_empty() {
502+
None
503+
} else {
504+
Some(&self.trace_propagation_style_extract)
505+
}
506+
}
507+
508+
fn trace_propagation_style_inject(&self) -> Option<&[TracePropagationStyle]> {
509+
// Bottlecap does not configure injection styles separately
510+
None
511+
}
512+
513+
fn trace_propagation_extract_first(&self) -> bool {
514+
self.trace_propagation_extract_first
515+
}
516+
517+
fn datadog_tags_max_length(&self) -> usize {
518+
// Default max length matching dd-trace-rs
519+
512
520+
}
521+
}
522+
491523
#[allow(clippy::module_name_repetitions)]
492524
#[inline]
493525
#[must_use]
@@ -819,8 +851,8 @@ pub mod tests {
819851
flush_strategy::{FlushStrategy, PeriodicStrategy},
820852
log_level::LogLevel,
821853
processing_rule::ProcessingRule,
822-
trace_propagation_style::TracePropagationStyle,
823854
};
855+
use datadog_opentelemetry::propagation::TracePropagationStyle;
824856

825857
#[test]
826858
fn test_default_logs_intake_url() {
@@ -1299,17 +1331,12 @@ pub mod tests {
12991331
fn test_parse_trace_propagation_style() {
13001332
figment::Jail::expect_with(|jail| {
13011333
jail.clear_env();
1302-
jail.set_env(
1303-
"DD_TRACE_PROPAGATION_STYLE",
1304-
"datadog,tracecontext,b3,b3multi",
1305-
);
1334+
jail.set_env("DD_TRACE_PROPAGATION_STYLE", "datadog,tracecontext");
13061335
let config = get_config(Path::new(""));
13071336

13081337
let expected_styles = vec![
13091338
TracePropagationStyle::Datadog,
13101339
TracePropagationStyle::TraceContext,
1311-
TracePropagationStyle::B3,
1312-
TracePropagationStyle::B3Multi,
13131340
];
13141341
assert_eq!(config.trace_propagation_style, expected_styles);
13151342
assert_eq!(config.trace_propagation_style_extract, expected_styles);

bottlecap/src/config/trace_propagation_style.rs

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,9 @@
1-
use std::{fmt::Display, str::FromStr};
1+
use std::str::FromStr;
22

3+
use datadog_opentelemetry::propagation::TracePropagationStyle;
34
use serde::{Deserialize, Deserializer};
45
use tracing::error;
56

6-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7-
pub enum TracePropagationStyle {
8-
Datadog,
9-
B3Multi,
10-
B3,
11-
TraceContext,
12-
None,
13-
}
14-
15-
impl FromStr for TracePropagationStyle {
16-
type Err = String;
17-
18-
fn from_str(s: &str) -> Result<Self, Self::Err> {
19-
match s.to_lowercase().as_str() {
20-
"datadog" => Ok(TracePropagationStyle::Datadog),
21-
"b3multi" => Ok(TracePropagationStyle::B3Multi),
22-
"b3" => Ok(TracePropagationStyle::B3),
23-
"tracecontext" => Ok(TracePropagationStyle::TraceContext),
24-
"none" => Ok(TracePropagationStyle::None),
25-
_ => {
26-
error!("Trace propagation style is invalid: {:?}, using None", s);
27-
Ok(TracePropagationStyle::None)
28-
}
29-
}
30-
}
31-
}
32-
33-
impl Display for TracePropagationStyle {
34-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35-
let style = match self {
36-
TracePropagationStyle::Datadog => "datadog",
37-
TracePropagationStyle::B3Multi => "b3multi",
38-
TracePropagationStyle::B3 => "b3",
39-
TracePropagationStyle::TraceContext => "tracecontext",
40-
TracePropagationStyle::None => "none",
41-
};
42-
write!(f, "{style}")
43-
}
44-
}
45-
467
#[allow(clippy::module_name_repetitions)]
478
pub fn deserialize_trace_propagation_style<'de, D>(
489
deserializer: D,
@@ -57,7 +18,7 @@ where
5718
|style| match TracePropagationStyle::from_str(style.trim()) {
5819
Ok(parsed_style) => Some(parsed_style),
5920
Err(e) => {
60-
tracing::error!("Failed to parse trace propagation style: {}, ignoring", e);
21+
error!("Failed to parse trace propagation style: {}, ignoring", e);
6122
None
6223
}
6324
},

bottlecap/src/config/yaml.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
use std::time::Duration;
22
use std::{collections::HashMap, path::PathBuf};
33

4+
use datadog_opentelemetry::propagation::TracePropagationStyle;
5+
46
use crate::{
57
config::{
68
Config, ConfigError, ConfigSource, ProcessingRule,
7-
additional_endpoints::deserialize_additional_endpoints,
8-
deserialize_apm_replace_rules, deserialize_key_value_pair_array_to_hashmap,
9-
deserialize_option_lossless, deserialize_optional_bool_from_anything,
10-
deserialize_optional_duration_from_microseconds,
9+
additional_endpoints::deserialize_additional_endpoints, deserialize_apm_replace_rules,
10+
deserialize_key_value_pair_array_to_hashmap, deserialize_option_lossless,
11+
deserialize_optional_bool_from_anything, deserialize_optional_duration_from_microseconds,
1112
deserialize_optional_duration_from_seconds,
1213
deserialize_optional_duration_from_seconds_ignore_zero, deserialize_optional_string,
13-
deserialize_processing_rules, deserialize_string_or_int,
14-
flush_strategy::FlushStrategy,
15-
log_level::LogLevel,
16-
logs_additional_endpoints::LogsAdditionalEndpoint,
14+
deserialize_processing_rules, deserialize_string_or_int, flush_strategy::FlushStrategy,
15+
log_level::LogLevel, logs_additional_endpoints::LogsAdditionalEndpoint,
1716
service_mapping::deserialize_service_mapping,
18-
trace_propagation_style::{TracePropagationStyle, deserialize_trace_propagation_style},
17+
trace_propagation_style::deserialize_trace_propagation_style,
1918
},
2019
merge_hashmap, merge_option, merge_option_to_value, merge_string, merge_vec,
2120
};
@@ -826,7 +825,7 @@ service_mapping: old-service:new-service
826825
827826
# Trace Propagation
828827
trace_propagation_style: "datadog"
829-
trace_propagation_style_extract: "b3"
828+
trace_propagation_style_extract: "tracecontext"
830829
trace_propagation_extract_first: true
831830
trace_propagation_http_baggage_enabled: true
832831
trace_aws_service_representation_enabled: true
@@ -972,7 +971,7 @@ api_security_sample_delay: 60 # Seconds
972971
),
973972
]),
974973
trace_propagation_style: vec![TracePropagationStyle::Datadog],
975-
trace_propagation_style_extract: vec![TracePropagationStyle::B3],
974+
trace_propagation_style_extract: vec![TracePropagationStyle::TraceContext],
976975
trace_propagation_extract_first: true,
977976
trace_propagation_http_baggage_enabled: true,
978977
trace_aws_service_representation_enabled: true,

bottlecap/src/lifecycle/invocation/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
lifecycle::invocation::processor::MS_TO_NS, metrics::enhanced::lambda::EnhancedMetricData,
3-
traces::context::SpanContext,
43
};
4+
use datadog_opentelemetry::propagation::context::SpanContext;
55
use std::{
66
collections::{HashMap, VecDeque},
77
time::{SystemTime, UNIX_EPOCH},

bottlecap/src/lifecycle/invocation/processor.rs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ use std::{
55
};
66

77
use chrono::{DateTime, Utc};
8+
use datadog_opentelemetry::propagation::{
9+
context::SpanContext,
10+
datadog::{
11+
DATADOG_PARENT_ID_KEY, DATADOG_SAMPLING_PRIORITY_KEY, DATADOG_TAGS_KEY,
12+
DATADOG_TRACE_ID_KEY,
13+
},
14+
};
815
use libdd_trace_protobuf::pb::Span;
916
use libdd_trace_utils::tracer_header_tags;
1017
use serde_json::Value;
@@ -31,14 +38,7 @@ use crate::{
3138
},
3239
tags::{lambda::tags::resolve_runtime_from_proc, provider},
3340
traces::{
34-
context::SpanContext,
35-
propagation::{
36-
DatadogCompositePropagator, Propagator,
37-
text_map_propagator::{
38-
DATADOG_PARENT_ID_KEY, DATADOG_SAMPLING_PRIORITY_KEY, DATADOG_SPAN_ID_KEY,
39-
DATADOG_TRACE_ID_KEY, DatadogHeaderPropagator,
40-
},
41-
},
41+
propagation::{DatadogCompositePropagator, carrier::JsonCarrier},
4242
trace_processor::SendingTraceProcessor,
4343
},
4444
};
@@ -52,6 +52,7 @@ pub const DATADOG_INVOCATION_ERROR_MESSAGE_KEY: &str = "x-datadog-invocation-err
5252
pub const DATADOG_INVOCATION_ERROR_TYPE_KEY: &str = "x-datadog-invocation-error-type";
5353
pub const DATADOG_INVOCATION_ERROR_STACK_KEY: &str = "x-datadog-invocation-error-stack";
5454
pub const DATADOG_INVOCATION_ERROR_KEY: &str = "x-datadog-invocation-error";
55+
const DATADOG_SPAN_ID_KEY: &str = "x-datadog-span-id";
5556
const TAG_SAMPLING_PRIORITY: &str = "_sampling_priority_v1";
5657

5758
pub struct Processor {
@@ -987,7 +988,10 @@ impl Processor {
987988

988989
// Set the extracted trace context to the spans
989990
if let Some(sc) = &context.extracted_span_context {
990-
context.invocation_span.trace_id = sc.trace_id;
991+
#[allow(clippy::cast_possible_truncation)] // Datadog protocol uses lower 64 bits
992+
{
993+
context.invocation_span.trace_id = sc.trace_id as u64;
994+
}
991995
context.invocation_span.parent_id = sc.span_id;
992996

993997
// Set the right data to the correct root level span,
@@ -1090,7 +1094,7 @@ impl Processor {
10901094
pub fn extract_span_context(
10911095
headers: &HashMap<String, String>,
10921096
payload_value: &Value,
1093-
propagator: Arc<impl Propagator>,
1097+
propagator: Arc<DatadogCompositePropagator>,
10941098
) -> Option<SpanContext> {
10951099
if let Some(sc) =
10961100
span_inferrer::extract_span_context(payload_value, Arc::clone(&propagator))
@@ -1101,14 +1105,14 @@ impl Processor {
11011105
if let Some(sc) = payload_value
11021106
.get("request")
11031107
.and_then(|req| req.get("headers"))
1104-
.and_then(|headers| propagator.extract(headers))
1108+
.and_then(|headers| propagator.extract(&JsonCarrier(headers)))
11051109
{
11061110
debug!("Extracted trace context from event.request.headers");
11071111
return Some(sc);
11081112
}
11091113

11101114
if let Some(payload_headers) = payload_value.get("headers")
1111-
&& let Some(sc) = propagator.extract(payload_headers)
1115+
&& let Some(sc) = propagator.extract(&JsonCarrier(payload_headers))
11121116
{
11131117
debug!("Extracted trace context from event headers");
11141118
return Some(sc);
@@ -1210,14 +1214,17 @@ impl Processor {
12101214
self.inferrer.set_status_code(status_code_as_string);
12111215
}
12121216

1213-
let mut trace_id = 0;
1214-
let mut parent_id = 0;
1217+
let mut trace_id: u64 = 0;
1218+
let mut parent_id: u64 = 0;
12151219
let mut tags: HashMap<String, String> = HashMap::new();
12161220

12171221
// If we have a trace context, this means we got it from
12181222
// distributed tracing
12191223
if let Some(sc) = &context.extracted_span_context {
1220-
trace_id = sc.trace_id;
1224+
#[allow(clippy::cast_possible_truncation)] // Datadog protocol uses lower 64 bits
1225+
{
1226+
trace_id = sc.trace_id as u64;
1227+
}
12211228
parent_id = sc.span_id;
12221229
tags.extend(sc.tags.clone());
12231230
}
@@ -1243,9 +1250,9 @@ impl Processor {
12431250
.insert(TAG_SAMPLING_PRIORITY.to_string(), priority);
12441251
}
12451252

1246-
// Extract tags from headers
1247-
// Used for 128 bit trace ids
1248-
tags = DatadogHeaderPropagator::extract_tags(&headers);
1253+
// Extract _dd.p.* propagation tags from x-datadog-tags header
1254+
let carrier_tags = headers.get(DATADOG_TAGS_KEY).map_or("", String::as_str);
1255+
tags = crate::traces::propagation::extract_propagation_tags(carrier_tags);
12491256
}
12501257

12511258
// We should always use the generated span id from the tracer
@@ -2057,8 +2064,8 @@ mod tests {
20572064
fn test_extract_span_context_priority_order() {
20582065
let config = Arc::new(config::Config {
20592066
trace_propagation_style_extract: vec![
2060-
config::trace_propagation_style::TracePropagationStyle::Datadog,
2061-
config::trace_propagation_style::TracePropagationStyle::TraceContext,
2067+
datadog_opentelemetry::propagation::TracePropagationStyle::Datadog,
2068+
datadog_opentelemetry::propagation::TracePropagationStyle::TraceContext,
20622069
],
20632070
..config::Config::default()
20642071
});
@@ -2095,8 +2102,8 @@ mod tests {
20952102
fn test_extract_span_context_no_request_headers() {
20962103
let config = Arc::new(config::Config {
20972104
trace_propagation_style_extract: vec![
2098-
config::trace_propagation_style::TracePropagationStyle::Datadog,
2099-
config::trace_propagation_style::TracePropagationStyle::TraceContext,
2105+
datadog_opentelemetry::propagation::TracePropagationStyle::Datadog,
2106+
datadog_opentelemetry::propagation::TracePropagationStyle::TraceContext,
21002107
],
21012108
..config::Config::default()
21022109
});

bottlecap/src/lifecycle/invocation/processor_service.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{collections::HashMap, sync::Arc};
22

33
use chrono::{DateTime, Utc};
4+
use datadog_opentelemetry::propagation::context::SpanContext;
45
use dogstatsd::aggregator::AggregatorHandle;
56
use libdd_trace_protobuf::pb::Span;
67
use serde_json::Value;
@@ -18,10 +19,7 @@ use crate::{
1819
processor::Processor,
1920
},
2021
tags::provider,
21-
traces::{
22-
context::SpanContext, propagation::DatadogCompositePropagator,
23-
trace_processor::SendingTraceProcessor,
24-
},
22+
traces::{propagation::DatadogCompositePropagator, trace_processor::SendingTraceProcessor},
2523
};
2624

2725
#[derive(Error, Debug)]

0 commit comments

Comments
 (0)