diff --git a/packages/dd-trace/src/config.js b/packages/dd-trace/src/config.js index 330275be3da..2ce3a8f3bf5 100644 --- a/packages/dd-trace/src/config.js +++ b/packages/dd-trace/src/config.js @@ -309,6 +309,10 @@ class Config { options.tracePropagationStyle, defaultPropagationStyle ) + const DD_TRACE_PROPAGATION_EXTRACT_FIRST = coalesce( + process.env.DD_TRACE_PROPAGATION_EXTRACT_FIRST, + false + ) const DD_TRACE_RUNTIME_ID_ENABLED = coalesce( options.experimental && options.experimental.runtimeId, process.env.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED, @@ -579,6 +583,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?) inject: DD_TRACE_PROPAGATION_STYLE_INJECT, extract: DD_TRACE_PROPAGATION_STYLE_EXTRACT } + this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST) this.experimental = { runtimeId: isTrue(DD_TRACE_RUNTIME_ID_ENABLED), exporter: DD_TRACE_EXPORTER, diff --git a/packages/dd-trace/src/id.js b/packages/dd-trace/src/id.js index f63964d74e3..9f437f1fa1a 100644 --- a/packages/dd-trace/src/id.js +++ b/packages/dd-trace/src/id.js @@ -42,6 +42,18 @@ class Identifier { toJSON () { return this.toString() } + + equals (other) { + const length = this._buffer.length + const otherLength = other._buffer.length + + // Only compare the bytes available in both IDs. + for (let i = length, j = otherLength; i >= 0 && j >= 0; i--, j--) { + if (this._buffer[i] !== other._buffer[j]) return false + } + + return true + } } // Create a buffer, using an optional hexadecimal value if provided. diff --git a/packages/dd-trace/src/opentracing/propagation/text_map.js b/packages/dd-trace/src/opentracing/propagation/text_map.js index 9335231c845..20a257bb61a 100644 --- a/packages/dd-trace/src/opentracing/propagation/text_map.js +++ b/packages/dd-trace/src/opentracing/propagation/text_map.js @@ -236,11 +236,20 @@ class TextMapPropagator { _extractDatadogContext (carrier) { const spanContext = this._extractGenericContext(carrier, traceKey, spanKey, 10) - if (spanContext) { - this._extractOrigin(carrier, spanContext) - this._extractBaggageItems(carrier, spanContext) - this._extractSamplingPriority(carrier, spanContext) - this._extractTags(carrier, spanContext) + if (!spanContext) return spanContext + + this._extractOrigin(carrier, spanContext) + this._extractBaggageItems(carrier, spanContext) + this._extractSamplingPriority(carrier, spanContext) + this._extractTags(carrier, spanContext) + + if (this._config.tracePropagationExtractFirst) return spanContext + + const tc = this._extractTraceparentContext(carrier) + + if (tc && spanContext._traceId.equals(tc._traceId)) { + spanContext._traceparent = tc._traceparent + spanContext._tracestate = tc._tracestate } return spanContext diff --git a/packages/dd-trace/test/opentracing/propagation/text_map.spec.js b/packages/dd-trace/test/opentracing/propagation/text_map.spec.js index 1fd69c54c81..469126010f0 100644 --- a/packages/dd-trace/test/opentracing/propagation/text_map.spec.js +++ b/packages/dd-trace/test/opentracing/propagation/text_map.spec.js @@ -464,6 +464,40 @@ describe('TextMapPropagator', () => { expect(first._spanId.toString(16)).to.equal(spanId) }) + it('should always extract tracestate from tracecontext when trace IDs match', () => { + textMap['traceparent'] = '00-0000000000000000000000000000007B-0000000000000456-01' + textMap['tracestate'] = 'other=bleh,dd=t.foo_bar_baz_:abc_!@#$%^&*()_+`-~;s:2;o:foo;t.dm:-4' + config.tracePropagationStyle.extract = ['datadog', 'tracecontext'] + + const carrier = textMap + const spanContext = propagator.extract(carrier) + + expect(spanContext._tracestate.get('other')).to.equal('bleh') + }) + + it(`should not extract tracestate from tracecontext when trace IDs don't match`, () => { + textMap['traceparent'] = '00-00000000000000000000000000000789-0000000000000456-01' + textMap['tracestate'] = 'other=bleh,dd=t.foo_bar_baz_:abc_!@#$%^&*()_+`-~;s:2;o:foo;t.dm:-4' + config.tracePropagationStyle.extract = ['datadog', 'tracecontext'] + + const carrier = textMap + const spanContext = propagator.extract(carrier) + + expect(spanContext._tracestate).to.be.undefined + }) + + it(`should not extract tracestate from tracecontext when configured to extract first`, () => { + textMap['traceparent'] = '00-0000000000000000000000000000007B-0000000000000456-01' + textMap['tracestate'] = 'other=bleh,dd=t.foo_bar_baz_:abc_!@#$%^&*()_+`-~;s:2;o:foo;t.dm:-4' + config.tracePropagationStyle.extract = ['datadog', 'tracecontext'] + config.tracePropagationExtractFirst = true + + const carrier = textMap + const spanContext = propagator.extract(carrier) + + expect(spanContext._tracestate).to.be.undefined + }) + describe('with B3 propagation as multiple headers', () => { beforeEach(() => { config.tracePropagationStyle.extract = ['b3multi'] @@ -746,6 +780,19 @@ describe('TextMapPropagator', () => { expect(spanContext._trace.tags['_dd.p.dm']).to.eql('-4') }) + it('should propagate other vendors', () => { + textMap['traceparent'] = '01-1111aaaa2222bbbb3333cccc4444dddd-5555eeee6666ffff-01' + textMap['tracestate'] = 'other=bleh,dd=t.foo_bar_baz_:abc_!@#$%^&*()_+`-~;s:2;o:foo;t.dm:-4' + config.tracePropagationStyle.extract = ['tracecontext'] + + const carrier = {} + const spanContext = propagator.extract(textMap) + + propagator.inject(spanContext, carrier) + + expect(carrier['tracestate']).to.include('other=bleh') + }) + it('should fix _dd.p.dm if invalid (non-hyphenated) input is received', () => { textMap['traceparent'] = '01-1111aaaa2222bbbb3333cccc4444dddd-5555eeee6666ffff-01' textMap['tracestate'] = 'other=bleh,dd=t.foo_bar_baz_:abc_!@#$%^&*()_+`-~;s:2;o:foo;t.dm:4'