diff --git a/lib/datadog/tracing/distributed/propagation.rb b/lib/datadog/tracing/distributed/propagation.rb index 1cc8959b5de..b18de48809a 100644 --- a/lib/datadog/tracing/distributed/propagation.rb +++ b/lib/datadog/tracing/distributed/propagation.rb @@ -101,10 +101,22 @@ def extract(data) # Only parse if it represent the same trace as the successfully extracted one next unless tracecontext_digest.trace_id == extracted_trace_digest.trace_id - # Preserve the `tracestate` + parent_id = extracted_trace_digest.span_id + distributed_tags = extracted_trace_digest.trace_distributed_tags + unless extracted_trace_digest.span_id == tracecontext_digest.span_id + # span_id in the tracecontext header takes precedence over the value in all conflicting headers + parent_id = tracecontext_digest.span_id + if (lp_id = last_datadog_parent_id(data, tracecontext_digest.trace_distributed_tags)) + distributed_tags = extracted_trace_digest.trace_distributed_tags&.dup || {} + distributed_tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID] = lp_id + end + end + # Preserve the trace state and last datadog span id extracted_trace_digest = extracted_trace_digest.merge( + span_id: parent_id, trace_state: tracecontext_digest.trace_state, - trace_state_unknown_fields: tracecontext_digest.trace_state_unknown_fields + trace_state_unknown_fields: tracecontext_digest.trace_state_unknown_fields, + trace_distributed_tags: distributed_tags ) end rescue => e @@ -115,6 +127,22 @@ def extract(data) extracted_trace_digest end + + private + + def last_datadog_parent_id(headers, tracecontext_tags) + dd_propagator = @propagation_style_extract.find { |propagator| propagator.is_a?(Datadog) } + if tracecontext_tags&.fetch( + Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID, + Tracing::Metadata::Ext::Distributed::DD_PARENT_ID_DEFAULT + ) != Tracing::Metadata::Ext::Distributed::DD_PARENT_ID_DEFAULT + # tracecontext headers contain a p value, ensure this value is sent to backend + tracecontext_tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID] + elsif dd_propagator && (dd_digest = dd_propagator.extract(headers)) + # if p value is not present in tracestate, use the parent id from the datadog headers + format('%016x', dd_digest.span_id) + end + end end end end diff --git a/lib/datadog/tracing/distributed/trace_context.rb b/lib/datadog/tracing/distributed/trace_context.rb index 44c716711ff..c5871f03175 100644 --- a/lib/datadog/tracing/distributed/trace_context.rb +++ b/lib/datadog/tracing/distributed/trace_context.rb @@ -57,7 +57,8 @@ def extract(data) end tags ||= {} - tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID] = ts_parent_id || '0000000000000000' + tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID] = + ts_parent_id || Tracing::Metadata::Ext::Distributed::DD_PARENT_ID_DEFAULT TraceDigest.new( span_id: parent_id, diff --git a/lib/datadog/tracing/metadata/ext.rb b/lib/datadog/tracing/metadata/ext.rb index 1f31cb63cbb..0034cd77a46 100644 --- a/lib/datadog/tracing/metadata/ext.rb +++ b/lib/datadog/tracing/metadata/ext.rb @@ -55,6 +55,7 @@ module Distributed TAG_SAMPLING_PRIORITY = '_sampling_priority_v1' TAG_DD_PARENT_ID = '_dd.parent_id' + DD_PARENT_ID_DEFAULT = '0000000000000000' # Trace tags with this prefix will propagate from a trace through distributed tracing. # Distributed headers tags with this prefix will be injected into the active trace. diff --git a/spec/datadog/tracing/distributed/propagation_spec.rb b/spec/datadog/tracing/distributed/propagation_spec.rb index 313a4cfb910..68297248d9d 100644 --- a/spec/datadog/tracing/distributed/propagation_spec.rb +++ b/spec/datadog/tracing/distributed/propagation_spec.rb @@ -334,6 +334,30 @@ end end end + + context 'and span_id is not matching' do + let(:data) { super().merge(prepare_key['x-datadog-parent-id'] => '15') } + + context 'without tracestate' do + it 'extracts span_id from traceparent and stores x-datadog-parent-id in trace_distributed_tags' do + expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) + expect(trace_digest.span_id).to eq(73456) + expect(trace_digest.trace_id).to eq(61185) + expect(trace_digest.trace_distributed_tags).to include('_dd.parent_id' => '000000000000000f') + end + end + + context 'with tracestate' do + let(:data) { super().merge(prepare_key['tracestate'] => 'other=gg,dd=s:1;p:000000000000000a') } + + it 'extracts span_id from traceparent and stores tracestate p value in trace_distributed_tags' do + expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) + expect(trace_digest.span_id).to eq(73456) + expect(trace_digest.trace_id).to eq(61185) + expect(trace_digest.trace_distributed_tags).to eq({ '_dd.parent_id' => '000000000000000a' }) + end + end + end end end