Skip to content

Commit

Permalink
Merge pull request #550 from DataDog/louiszawadzki/rum-1926/send-trac…
Browse files Browse the repository at this point in the history
…estate-with-w3c-headers

Send tracestate with w3c headers by default
  • Loading branch information
louiszawadzki authored Nov 7, 2023
2 parents b669a61 + 6608975 commit a06020c
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 19 deletions.
5 changes: 4 additions & 1 deletion packages/core/src/DdSdkReactNativeConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ export const formatFirstPartyHosts = (
if (isLegacyFirstPartyHost(host)) {
return {
match: host,
propagatorTypes: [PropagatorType.DATADOG]
propagatorTypes: [
PropagatorType.DATADOG,
PropagatorType.TRACECONTEXT
]
};
}
return host;
Expand Down
20 changes: 16 additions & 4 deletions packages/core/src/__tests__/DdSdkReactNative.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,14 @@ describe('DdSdkReactNative', () => {
'_dd.native_view_tracking': false,
'_dd.native_interaction_tracking': false,
'_dd.first_party_hosts': [
{ match: 'api.example.com', propagatorTypes: ['datadog'] },
{ match: 'something.fr', propagatorTypes: ['datadog'] }
{
match: 'api.example.com',
propagatorTypes: ['datadog', 'tracecontext']
},
{
match: 'something.fr',
propagatorTypes: ['datadog']
}
]
});
expect(DdRumResourceTracking.startTracking).toHaveBeenCalledTimes(
Expand All @@ -524,8 +530,14 @@ describe('DdSdkReactNative', () => {
expect(DdRumResourceTracking.startTracking).toHaveBeenCalledWith({
tracingSamplingRate: 42,
firstPartyHosts: [
{ match: 'api.example.com', propagatorTypes: ['datadog'] },
{ match: 'something.fr', propagatorTypes: ['datadog'] }
{
match: 'api.example.com',
propagatorTypes: ['datadog', 'tracecontext']
},
{
match: 'something.fr',
propagatorTypes: ['datadog']
}
]
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const PARENT_ID_HEADER_KEY = 'x-datadog-parent-id';
* OTel headers
*/
export const TRACECONTEXT_HEADER_KEY = 'traceparent';
export const TRACESTATE_HEADER_KEY = 'tracestate';
export const B3_HEADER_KEY = 'b3';
export const B3_MULTI_TRACE_ID_HEADER_KEY = 'X-B3-TraceId';
export const B3_MULTI_SPAN_ID_HEADER_KEY = 'X-B3-SpanId';
Expand Down Expand Up @@ -60,16 +61,26 @@ export const getTracingHeaders = (
break;
}
case PropagatorType.TRACECONTEXT: {
headers.push({
header: TRACECONTEXT_HEADER_KEY,
value: generateTraceContextHeader({
version: '00',
traceId: tracingAttributes.traceId,
parentId: tracingAttributes.spanId,
isSampled:
tracingAttributes.samplingPriorityHeader === '1'
})
});
headers.push(
{
header: TRACECONTEXT_HEADER_KEY,
value: generateTraceContextHeader({
version: '00',
traceId: tracingAttributes.traceId,
parentId: tracingAttributes.spanId,
isSampled:
tracingAttributes.samplingPriorityHeader === '1'
})
},
{
header: TRACESTATE_HEADER_KEY,
value: generateTraceStateHeader({
parentId: tracingAttributes.spanId,
isSampled:
tracingAttributes.samplingPriorityHeader === '1'
})
}
);
break;
}
case PropagatorType.B3: {
Expand Down Expand Up @@ -124,6 +135,20 @@ const generateTraceContextHeader = ({
)}-${parentId.toPaddedString(16, 16)}-${flags}`;
};

const generateTraceStateHeader = ({
parentId,
isSampled
}: {
parentId: SpanId;
isSampled: boolean;
}) => {
const sampled = `s:${isSampled ? '1' : '0'}`;
const origin = 'o:rum';
const parent = `p:${parentId.toPaddedString(16, 16)}`;

return `dd=${sampled};${origin};${parent}`;
};

const generateB3Header = ({
traceId,
spanId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
B3_MULTI_SPAN_ID_HEADER_KEY,
B3_MULTI_SAMPLED_HEADER_KEY,
ORIGIN_RUM,
ORIGIN_HEADER_KEY
ORIGIN_HEADER_KEY,
TRACESTATE_HEADER_KEY
} from '../../../distributedTracing/distributedTracingHeaders';
import { firstPartyHostsRegexMapBuilder } from '../../../distributedTracing/firstPartyHosts';
import {
Expand Down Expand Up @@ -54,6 +55,10 @@ const flushPromises = () =>
new Promise(jest.requireActual('timers').setImmediate);
let xhrProxy;

const hexToDecimal = (hex: string): string => {
return BigInt(`0x${hex}`).toString(10);
};

beforeEach(() => {
DdNativeRum.startResource.mockClear();
DdNativeRum.stopResource.mockClear();
Expand Down Expand Up @@ -444,8 +449,74 @@ describe('XHRProxy', () => {
await flushPromises();

// THEN
const headerValue = xhr.requestHeaders[TRACECONTEXT_HEADER_KEY];
expect(headerValue).toMatch(/^00-[0-9a-f]{32}-[0-9a-f]{16}-01$/);
const contextHeader = xhr.requestHeaders[TRACECONTEXT_HEADER_KEY];
expect(contextHeader).toMatch(/^00-[0-9a-f]{32}-[0-9a-f]{16}-01$/);

// Parent value of the context header is the 3rd part of it
const parentValue = contextHeader.split('-')[2];
const stateHeader = xhr.requestHeaders[TRACESTATE_HEADER_KEY];
expect(stateHeader).toBe(`dd=s:1;o:rum;p:${parentValue}`);
});

it('adds tracing headers with matching value when all headers are added', async () => {
// GIVEN
const method = 'GET';
const url = 'https://api.example.com:443/v2/user';
xhrProxy.onTrackingStart({
tracingSamplingRate: 100,
firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([
{
match: 'example.com',
propagatorTypes: [PropagatorType.DATADOG]
},
{
match: 'example.com',
propagatorTypes: [PropagatorType.TRACECONTEXT]
},
{
match: 'example.com',
propagatorTypes: [PropagatorType.B3]
},
{
match: 'example.com',
propagatorTypes: [PropagatorType.B3MULTI]
}
])
});

// WHEN
const xhr = new XMLHttpRequestMock();
xhr.open(method, url);
xhr.send();
xhr.notifyResponseArrived();
xhr.complete(200, 'ok');
await flushPromises();

// THEN
const datadogTraceValue = xhr.requestHeaders[TRACE_ID_HEADER_KEY];
const datadogParentValue = xhr.requestHeaders[PARENT_ID_HEADER_KEY];

const contextHeader = xhr.requestHeaders[TRACECONTEXT_HEADER_KEY];
const traceContextValue = contextHeader.split('-')[1];
const parentContextValue = contextHeader.split('-')[2];

const b3MultiTraceHeader =
xhr.requestHeaders[B3_MULTI_TRACE_ID_HEADER_KEY];
const b3MultiParentHeader =
xhr.requestHeaders[B3_MULTI_SPAN_ID_HEADER_KEY];

const b3Header = xhr.requestHeaders[B3_HEADER_KEY];
const traceB3Value = b3Header.split('-')[0];
const parentB3Value = b3Header.split('-')[1];

expect(hexToDecimal(traceContextValue)).toBe(datadogTraceValue);
expect(hexToDecimal(parentContextValue)).toBe(datadogParentValue);

expect(hexToDecimal(b3MultiTraceHeader)).toBe(datadogTraceValue);
expect(hexToDecimal(b3MultiParentHeader)).toBe(datadogParentValue);

expect(hexToDecimal(traceB3Value)).toBe(datadogTraceValue);
expect(hexToDecimal(parentB3Value)).toBe(datadogParentValue);
});

it('adds tracecontext request headers when the host is instrumented with tracecontext and request is sampled', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,12 @@ describe('DatadogProvider', () => {
expect(
NativeModules.DdSdk.initialize.mock.calls[0][0]
.additionalConfig['_dd.first_party_hosts']
).toEqual([{ match: 'api.com', propagatorTypes: ['datadog'] }]);
).toEqual([
{
match: 'api.com',
propagatorTypes: ['datadog', 'tracecontext']
}
]);
expect(NativeModules.DdRum.addAction).toHaveBeenCalledTimes(1);
});
});
Expand Down

0 comments on commit a06020c

Please sign in to comment.