diff --git a/lib/datadog/tracing/distributed/propagation.rb b/lib/datadog/tracing/distributed/propagation.rb index a5f95078f33..814ff42b304 100644 --- a/lib/datadog/tracing/distributed/propagation.rb +++ b/lib/datadog/tracing/distributed/propagation.rb @@ -18,6 +18,12 @@ class Propagation # @param propagation_styles [Hash] def initialize(propagation_styles:) @propagation_styles = propagation_styles + # We need to make sure propagation_style option is evaluated. + # Our options are lazy evaluated and it happens that propagation_style has the on_set callback + # that affect Datadog.configuration.tracing.distributed_tracing.propagation_inject_style and + # Datadog.configuration.tracing.distributed_tracing.propagation_extract_style + # By calling it here, we make sure if the customers has set any value either via code or ENV variable is applied. + ::Datadog.configuration.tracing.distributed_tracing.propagation_style end # inject! populates the env with span ID, trace ID and sampling priority @@ -72,8 +78,10 @@ def inject!(digest, data) # # @param data [Hash] def extract(data) - trace_digest = nil - dd_trace_digest = nil + return unless data + return if data.empty? + + extracted_trace_digest = nil ::Datadog.configuration.tracing.distributed_tracing.propagation_extract_style.each do |style| propagator = @propagation_styles[style] @@ -83,42 +91,14 @@ def extract(data) extracted_trace_digest = propagator.extract(data) rescue => e ::Datadog.logger.error( - 'Error extracting distributed trace data. ' \ - "Cause: #{e} Location: #{Array(e.backtrace).first}" + "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}" ) end - # Skip if no valid data was found - next if extracted_trace_digest.nil? - - # Keep track of the Datadog extracted digest, we want to return it if we have it. - if extracted_trace_digest && style == Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG - dd_trace_digest = extracted_trace_digest - end - - # No previously extracted trace data, use the one we just extracted - if trace_digest.nil? - trace_digest = extracted_trace_digest - else - unless trace_digest.trace_id == extracted_trace_digest.trace_id \ - && trace_digest.span_id == extracted_trace_digest.span_id - # We have two mismatched propagation contexts - ::Datadog.logger.debug do - 'Cannot extract distributed trace data: extracted styles differ, ' \ - "#{trace_digest.trace_id} != #{extracted_trace_digest.trace_id} && " \ - "#{trace_digest.span_id} != #{extracted_trace_digest.span_id}" - end - - # It's safer to create a new context than to attach ourselves to the wrong context - return TraceDigest.new # Early return from the whole method - end - end + break if extracted_trace_digest end - # Return the extracted trace digest if we found one or else a new empty digest. - # Always return the Datadog trace digest if one exists since it has more - # information than the B3 digest, e.g. origin, priority sampling values. - dd_trace_digest || trace_digest || nil + extracted_trace_digest end end end diff --git a/spec/datadog/tracing/distributed/propagation_spec.rb b/spec/datadog/tracing/distributed/propagation_spec.rb index 2fb9b5febd0..6158999385b 100644 --- a/spec/datadog/tracing/distributed/propagation_spec.rb +++ b/spec/datadog/tracing/distributed/propagation_spec.rb @@ -125,11 +125,16 @@ subject(:extract) { propagator.extract(data) } let(:trace_digest) { extract } - context 'given empty data' do + context 'given `nil`' do let(:data) { nil } it { is_expected.to be nil } end + context 'given empty hash' do + let(:data) { {} } + it { is_expected.to be nil } + end + context 'given an data containing' do context 'datadog trace id and parent id' do let(:data) do @@ -323,24 +328,6 @@ end end end - - context 'with mismatched values' do - let(:data) do - { - prepare_key['x-datadog-trace-id'] => '7', - prepare_key['x-datadog-parent-id'] => '8', - prepare_key['x-b3-traceid'] => '00ef01', - prepare_key['x-b3-spanid'] => '011ef0' - } - end - - it do - expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) - expect(trace_digest.span_id).to be_nil - expect(trace_digest.trace_id).to be_nil - expect(trace_digest.trace_sampling_priority).to be nil - end - end end context 'datadog, b3, and b3 single header' do @@ -381,26 +368,6 @@ expect(trace_digest.trace_sampling_priority).to eq(1) end end - - context 'with mismatched values' do - let(:data) do - # DEV: We only need 1 to be mismatched - { - prepare_key['x-datadog-trace-id'] => '7', - prepare_key['x-datadog-parent-id'] => '8', - prepare_key['x-b3-traceid'] => '00ef01', - prepare_key['x-b3-spanid'] => '011ef0', - prepare_key['b3'] => '00ef01-011ef0' - } - end - - it do - expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) - expect(trace_digest.span_id).to be_nil - expect(trace_digest.trace_id).to be_nil - expect(trace_digest.trace_sampling_priority).to be nil - end - end end context 'datadog, and b3 single header' do @@ -436,21 +403,78 @@ expect(trace_digest.trace_sampling_priority).to eq(1) end end + end - context 'with mismatched values' do - let(:data) do - { - prepare_key['x-datadog-trace-id'] => '7', - prepare_key['x-datadog-parent-id'] => '8', - prepare_key['b3'] => '00ef01-011ef0' - } + context 'when conflict across different extractions' do + let(:datadog_trace_id) { 0xabcdef } + let(:b3_multi_trace_id) { 0x123456 } + let(:b3_trace_id) { 0xfedcba } + let(:span_id) { 0xfffffff } + let(:data) do + { + prepare_key['x-datadog-trace-id'] => datadog_trace_id.to_s(10), + prepare_key['x-datadog-parent-id'] => span_id.to_s(10), + prepare_key['x-b3-traceid'] => b3_multi_trace_id.to_s(16), + prepare_key['x-b3-spanid'] => span_id.to_s(16), + prepare_key['b3'] => "#{b3_trace_id.to_s(16)}-#{span_id.to_s(16)}" + } + end + + after do + Datadog.configuration.reset! + end + + context "when extraction styles ['Datadog', 'b3multi', 'b3']" do + before do + Datadog.configure do |c| + c.tracing.distributed_tracing.propagation_extract_style = [ + 'Datadog', + 'b3multi', + 'b3', + ] + end end - it do + it 'returns trace digest from `Datadog` extraction' do expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) - expect(trace_digest.span_id).to be nil - expect(trace_digest.trace_id).to be nil - expect(trace_digest.trace_sampling_priority).to be nil + expect(trace_digest.trace_id).to eq(datadog_trace_id) + expect(trace_digest.span_id).to eq(0xfffffff) + end + end + + context "when extraction styles ['b3multi', 'b3', 'Datadog']" do + before do + Datadog.configure do |c| + c.tracing.distributed_tracing.propagation_extract_style = [ + 'b3multi', + 'b3', + 'Datadog', + ] + end + end + + it 'returns trace digest from `b3multi` extraction' do + expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) + expect(trace_digest.trace_id).to eq(b3_multi_trace_id) + expect(trace_digest.span_id).to eq(0xfffffff) + end + end + + context "when extraction styles ['b3', 'Datadog', 'b3multi']" do + before do + Datadog.configure do |c| + c.tracing.distributed_tracing.propagation_extract_style = [ + 'b3', + 'Datadog', + 'b3multi', + ] + end + end + + it 'returns trace digest from `b3` extraction' do + expect(trace_digest).to be_a_kind_of(Datadog::Tracing::TraceDigest) + expect(trace_digest.trace_id).to eq(b3_trace_id) + expect(trace_digest.span_id).to eq(0xfffffff) end end end