From 6747dd455ecacefd002c681191a19c22bb85d2b0 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Fri, 21 Jun 2024 14:38:32 +0200 Subject: [PATCH] 128 bit trace IDs --- packages/core/src/DdSdkReactNative.tsx | 1 + .../__tests__/distributedTracing.test.ts | 84 +++++-- .../distributedTracing/distributedTracing.tsx | 230 +++++++++++++++--- .../distributedTracingHeaders.ts | 49 ++-- .../DatadogRumResource/ResourceReporter.ts | 9 +- .../XHRProxy/__tests__/XHRProxy.test.ts | 41 +++- 6 files changed, 329 insertions(+), 85 deletions(-) diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index 110a6b1f8..20fd1eeca 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -47,6 +47,7 @@ export class DdSdkReactNative { private static readonly DD_SDK_VERSION = '_dd.sdk_version'; private static readonly DD_VERSION = '_dd.version'; private static readonly DD_VERSION_SUFFIX = '_dd.version_suffix'; + private static wasAutoInstrumented = false; private static features?: AutoInstrumentationConfiguration; private static _isInitialized = false; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts index 3868d34e1..9759eb25f 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts @@ -1,6 +1,9 @@ -import { TraceIdentifier } from '../distributedTracing'; +import { + TracingIdentifier, + TracingIdRepresentation +} from '../distributedTracing'; -describe('TraceIdentifier', () => { +describe('TracingIdentifier', () => { it('M return an unique identifier W toString', async () => { // GIVEN const generatedIds = new Set(); @@ -9,51 +12,86 @@ describe('TraceIdentifier', () => { // WHEN while (counter-- > 0) { - generatedIds.add(new TraceIdentifier().toString(10)); + generatedIds.add( + new TracingIdentifier().toString( + TracingIdRepresentation.decimal + ) + ); } // THEN expect(generatedIds.size).toBe(iterations); }); - it('M return an 64 bits positive integer W toString', async () => { + it('M return a valid 128 bits positive integer W toString', async () => { let iterations = 100; while (iterations-- > 0) { // GIVEN - const id = new TraceIdentifier().toString(10); + const id = new TracingIdentifier(); + const idStr128 = id.toString(TracingIdRepresentation.decimal); // THEN - expect(id).toMatch(/[1-9]{1,19}/); - // should be less than the max 64 bits integer - if (id.length === 19) { - expect(id < '9223372036854775807').toBeTruthy(); - } + expect(idStr128).toMatch(/[1-9]{1,19}/); + expect(TracingIdentifier.is64Bits(idStr128)).toBe(false); + expect(TracingIdentifier.is128Bits(idStr128)).toBe(true); } }); - it('M return an 64 bits positive hex W toString(16)', async () => { + it('M return a valid 64 bits low and high part integer W toString', async () => { let iterations = 100; while (iterations-- > 0) { // GIVEN - const trace = new TraceIdentifier(); - const id = trace.toString(16); - const paddedId = trace.toPaddedString(16, 16); + const id = new TracingIdentifier(); + const idStrLow64 = id.toString(TracingIdRepresentation.lowDecimal); + const idStrHigh64 = id.highToString(10); // THEN - expect(id).toMatch(/[1-9a-f]{1,16}/); - expect(paddedId).toMatch(/[0-9a-f]{16}/); + expect(TracingIdentifier.is64Bits(idStrLow64)).toBe(true); + expect(TracingIdentifier.is64Bits(idStrHigh64)).toBe(true); } }); - it('M return an 64 bits positive padded hex W toPaddedString(16, 32)', async () => { + it('M return a valid timestamp in the high part of the ID w toString', () => { + const tracingId = new TracingIdentifier(); + const idHex = tracingId.toString(TracingIdRepresentation.hex); + const timestamp = TracingIdentifier.extractTimestamp(idHex); + + const currentUnixTime = Math.floor(Date.now() / 1000); + const fiveMinutesInSeconds = 5 * 60; + + expect(timestamp).toBeGreaterThan( + currentUnixTime - fiveMinutesInSeconds + ); + expect(timestamp).toBeLessThan(currentUnixTime + fiveMinutesInSeconds); + }); + + it('M return a 128 bits positive hex W toString(16)', async () => { + let iterations = 100; + while (iterations-- > 0) { + // GIVEN + const trace = new TracingIdentifier(); + const id = trace.toString(TracingIdRepresentation.hex); + const paddedId = trace.toString(TracingIdRepresentation.hex16Chars); + + // THEN + expect(id).toMatch(/^[0-9a-f]{1,32}$/); + expect(paddedId).toMatch(/^[0-9a-f]{16}$/); + + expect(TracingIdentifier.is128Bits(id, 16)).toBe(true); + } + }); + + it('M return a 128 bits positive padded hex W toPaddedString(16, 64)', async () => { let iterations = 100; while (iterations-- > 0) { // GIVEN - const id = new TraceIdentifier().toPaddedString(16, 32); + const id = new TracingIdentifier().toPaddedString(16, 64); // THEN - expect(id).not.toMatch(/[0]{32}/); - expect(id).toMatch(/[0]{16}[0-9a-f]{16}/); + expect(id).not.toMatch(/^[0]{32}$/); + expect(id).toMatch(/^[0]{32}[0-9a-f]{32}$/); + + expect(TracingIdentifier.is128Bits(id, 16)).toBe(true); } }); @@ -61,11 +99,11 @@ describe('TraceIdentifier', () => { let iterations = 100; while (iterations-- > 0) { // GIVEN - const id = new TraceIdentifier().toPaddedString(16, 10); + const id = new TracingIdentifier().toPaddedString(16, 10); // THEN - expect(id).not.toMatch(/[0]{10}/); - expect(id).toMatch(/[0-9a-f]{10}/); + expect(id).not.toMatch(/^[0]{10}$/); + expect(id).toMatch(/^[0-9a-f]{32}$/); } }); }); diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx index 579eb0c3e..7afc11dae 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx @@ -10,6 +10,38 @@ import type { RegexMap } from '../requestProxy/interfaces/RequestProxy'; import type { Hostname } from './firstPartyHosts'; import { getPropagatorsForHost } from './firstPartyHosts'; +export enum TracingIdRepresentation { + /** + * Decimal string representation of the tracing id. + */ + decimal, + + /** + * The low 64-bits of rhe tracing id as a decimal. + */ + lowDecimal, + + /** + * Hexadecimal string representation of the full tracing id. + */ + hex, + + /** + * Hexadecimal string representation of the low 64-bits of the tracing id. + */ + hex16Chars, + + /** + * Hexadecimal string representation of the high 64-bits of the tracing id. + */ + highHex16Chars, + + /** + * Hexadecimal string representation of the full 128-bits of the tracing id. + */ + hex32Chars +} + export type DdRumResourceTracingAttributes = | { tracingStrategy: 'KEEP'; @@ -62,8 +94,8 @@ const generateTracingAttributesWithSampling = ( ): DdRumResourceTracingAttributes => { const isSampled = Math.random() * 100 <= tracingSamplingRate; const tracingAttributes: DdRumResourceTracingAttributes = { - traceId: new TraceIdentifier() as TraceId, - spanId: new TraceIdentifier() as SpanId, + traceId: new TracingIdentifier() as TraceId, + spanId: new TracingIdentifier() as SpanId, samplingPriorityHeader: isSampled ? '1' : '0', tracingStrategy: 'KEEP', rulePsr: tracingSamplingRate / 100, @@ -77,55 +109,177 @@ const generateTracingAttributesWithSampling = ( * Using branded types will ensure we don't accidentally use * traceId for spanId when generating headers. */ -export type TraceId = TraceIdentifier & { +export type TraceId = TracingIdentifier & { _brand: 'traceId'; }; -export type SpanId = TraceIdentifier & { +export type SpanId = TracingIdentifier & { _brand: 'spanId'; }; -/* - * This code was inspired from browser-sdk at (https://github.com/DataDog/browser-sdk/blob/0e9722d5b06f6d49264bc82cd254a207d647d66c/packages/rum-core/src/domain/tracing/tracer.ts#L190) - */ -const MAX_32_BITS_NUMBER = 4294967295; // 2^32-1 -const MAX_31_BITS_NUMBER = 2147483647; // 2^31-1 -export class TraceIdentifier { - private low: number; - private high: number; +export class TracingIdentifier { + private id: string; + private high: string; + private low: string; constructor() { - // We need to have a 63 bits number max - this.high = Math.floor(Math.random() * MAX_31_BITS_NUMBER); - this.low = Math.floor(Math.random() * MAX_32_BITS_NUMBER); + const idData = this.generate128BitID(); + this.id = idData.id; + this.high = idData.high; + this.low = idData.low; } - toString = (radix: number) => { - let low = this.low; - let high = this.high; - let str = ''; + /** + * Generates a 128-bit ID in the format: + * <32-bit Unix timestamp> <32 bits of zero> <64 random bits>. + * @returns An object containing the full 128-bit ID, high 64 bits, and low 64 bits as hexadecimal strings. + */ + private generate128BitID(): { id: string; high: string; low: string } { + // Get the current Unix timestamp in seconds + const unixSeconds = Math.floor(Date.now() / 1000); - while (high > 0 || low > 0) { - const mod = (high % radix) * (MAX_32_BITS_NUMBER + 1) + low; - high = Math.floor(high / radix); - low = Math.floor(mod / radix); - str = (mod % radix).toString(radix) + str; - } - return str; - }; + // Ensure the Unix timestamp is 32 bits + const unixSeconds32 = unixSeconds & 0xffffffff; + + // 32 bits of zero + const zeros32 = 0; + + // Generate 64 random bits using Math.random() + const random32Bit1 = Math.floor(Math.random() * 0xffffffff); + const random32Bit2 = Math.floor(Math.random() * 0xffffffff); + + // Convert parts to hexadecimal strings + const unixSecondsHex = unixSeconds32.toString(16).padStart(8, '0'); + const zerosHex = zeros32.toString(16).padStart(8, '0'); + const random64Hex = + random32Bit1.toString(16).padStart(8, '0') + + random32Bit2.toString(16).padStart(8, '0'); + + // Combine parts to form the 128-bit ID + const id = unixSecondsHex + zerosHex + random64Hex; + + // The high 64 bits is the combination of the Unix timestamp and zeros + const high = unixSecondsHex + zerosHex; + + // The low 64 bits is the random part + const low = random64Hex; + + return { id, high, low }; + } /** - * This function pads the trace with `0`. - * It should not be used with a `length` lower than the trace, as we return the full trace in this case. - * @param radix radix for the trace - * @param length minimum length - * @returns padded string + * Returns a string representation of the TracingId. + * @param representation - The type of representation to use. + * @returns The ID as a string in the specified representation type. */ - toPaddedString = (radix: number, length: number) => { - const traceId = this.toString(radix); - if (traceId.length > length) { - return traceId; + public toString(representation: TracingIdRepresentation): string { + switch (representation) { + case TracingIdRepresentation.decimal: + return this.toStringWithRadix(10); + case TracingIdRepresentation.lowDecimal: + return this.lowToString(10); + case TracingIdRepresentation.hex: + return this.toStringWithRadix(16); + case TracingIdRepresentation.hex16Chars: + return this.lowToPaddedString(16, 16); + case TracingIdRepresentation.highHex16Chars: + return this.highToPaddedString(16, 16); + case TracingIdRepresentation.hex32Chars: + return this.toPaddedString(16, 32); } - return Array(length - traceId.length + 1).join('0') + traceId; - }; + } + + /** + * Converts the full 128-bit ID to a string using the specified radix. + * @param radix - The base to use for the conversion. + * @returns The ID as a string in the specified radix. + */ + public toStringWithRadix(radix: number): string { + return BigInt(`0x${this.id}`).toString(radix); + } + + /** + * Converts the full 128-bit ID to a string using the specified radix and pads it with zeros to the specified length. + * @param radix - The base to use for the conversion. + * @param length - The desired length of the output string. + * @returns The ID as a padded string in the specified radix. + */ + public toPaddedString(radix: number, length: number): string { + return BigInt(`0x${this.id}`).toString(radix).padStart(length, '0'); + } + + /** + * Converts the high 64 bits of the ID to a string using the specified radix. + * @param radix - The base to use for the conversion. + * @returns The high 64 bits of the ID as a string in the specified radix. + */ + public highToString(radix: number): string { + return BigInt(`0x${this.high}`).toString(radix); + } + + /** + * Converts the high 64 bits of the ID to a string using the specified radix and pads it with zeros to the specified length. + * @param radix - The base to use for the conversion. + * @param length - The desired length of the output string. + * @returns The high 64 bits of the ID as a padded string in the specified radix. + */ + public highToPaddedString(radix: number, length: number): string { + return BigInt(`0x${this.high}`).toString(radix).padStart(length, '0'); + } + + /** + * Converts the low 64 bits of the ID to a string using the specified radix. + * @param radix - The base to use for the conversion. + * @returns The low 64 bits of the ID as a string in the specified radix. + */ + public lowToString(radix: number): string { + return BigInt(`0x${this.low}`).toString(radix); + } + + /** + * Converts the low 64 bits of the ID to a string using the specified radix and pads it with zeros to the specified length. + * @param radix - The base to use for the conversion. + * @param length - The desired length of the output string. + * @returns The low 64 bits of the ID as a padded string in the specified radix. + */ + public lowToPaddedString(radix: number, length: number): string { + return BigInt(`0x${this.low}`).toString(radix).padStart(length, '0'); + } + + /** + * Extracts the Unix timestamp from the 128-bit hex string representation. + * @param idHex - The 128-bit ID as a hexadecimal string. + * @returns The Unix timestamp as a number. + */ + public static extractTimestamp(idHex: string): number { + // Extract the first 8 characters which represent the 32-bit Unix timestamp + const timestampHex = idHex.substring(0, 8); + + // Convert the hexadecimal string to a number + const timestamp = parseInt(timestampHex, 16); + + return timestamp; + } + + /** + * Checks if a string representation of an ID in a given radix is within 64 bits. + * @param idString - The string representation of the ID. + * @param radix - Optional base to use for the conversion (default is 10). + * @returns True if the ID is within 64 bits, otherwise false. + */ + static is64Bits(idString: string, radix: number = 10): boolean { + const bigIntValue = BigInt(parseInt(idString, radix)); + return bigIntValue < BigInt(1) << BigInt(64); + } + + /** + * Checks if a string representation of an ID in a given radix is within 128 bits. + * @param idString - The string representation of the ID. + * @param radix - Optional base to use for the conversion (default is 10). + * @returns True if the ID is within 128 bits, otherwise false. + */ + static is128Bits(idString: string, radix: number = 10): boolean { + const bigIntValue = BigInt(parseInt(idString, radix)); + return bigIntValue < BigInt(1) << BigInt(128); + } } diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts index 32b409d2f..ae7f4052b 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts @@ -11,6 +11,7 @@ import type { SpanId, TraceId } from './distributedTracing'; +import { TracingIdRepresentation } from './distributedTracing'; export const SAMPLING_PRIORITY_HEADER_KEY = 'x-datadog-sampling-priority'; /** @@ -20,6 +21,9 @@ export const ORIGIN_HEADER_KEY = 'x-datadog-origin'; export const ORIGIN_RUM = 'rum'; export const TRACE_ID_HEADER_KEY = 'x-datadog-trace-id'; export const PARENT_ID_HEADER_KEY = 'x-datadog-parent-id'; +export const TAGS_HEADER_KEY = 'x-datadog-tags'; +export const DD_TRACE_ID_TAG = '_dd.p.tid'; + /** * OTel headers */ @@ -51,11 +55,21 @@ export const getTracingHeaders = ( }, { header: TRACE_ID_HEADER_KEY, - value: tracingAttributes.traceId.toString(10) + value: tracingAttributes.traceId.toString( + TracingIdRepresentation.lowDecimal + ) }, { header: PARENT_ID_HEADER_KEY, - value: tracingAttributes.spanId.toString(10) + value: tracingAttributes.spanId.toString( + TracingIdRepresentation.lowDecimal + ) + }, + { + header: TAGS_HEADER_KEY, + value: `${DD_TRACE_ID_TAG}=${tracingAttributes.traceId.toString( + TracingIdRepresentation.highHex16Chars + )}` } ); break; @@ -99,11 +113,15 @@ export const getTracingHeaders = ( headers.push( { header: B3_MULTI_TRACE_ID_HEADER_KEY, - value: tracingAttributes.traceId.toPaddedString(16, 32) + value: tracingAttributes.traceId.toString( + TracingIdRepresentation.hex32Chars + ) }, { header: B3_MULTI_SPAN_ID_HEADER_KEY, - value: tracingAttributes.spanId.toPaddedString(16, 16) + value: tracingAttributes.spanId.toString( + TracingIdRepresentation.hex16Chars + ) }, { header: B3_MULTI_SAMPLED_HEADER_KEY, @@ -114,6 +132,10 @@ export const getTracingHeaders = ( } }); + // TODO: Remove debug log + // eslint-disable-next-line no-console + console.log(`TRACING HEADERS:\n${JSON.stringify(headers, null, 4)}`); + return headers; }; @@ -129,10 +151,9 @@ const generateTraceContextHeader = ({ isSampled: boolean; }) => { const flags = isSampled ? '01' : '00'; - return `${version}-${traceId.toPaddedString( - 16, - 32 - )}-${parentId.toPaddedString(16, 16)}-${flags}`; + return `${version}-${traceId.toString( + TracingIdRepresentation.hex32Chars + )}-${parentId.toString(TracingIdRepresentation.hex16Chars)}-${flags}`; }; const generateTraceStateHeader = ({ @@ -144,7 +165,7 @@ const generateTraceStateHeader = ({ }) => { const sampled = `s:${isSampled ? '1' : '0'}`; const origin = 'o:rum'; - const parent = `p:${parentId.toPaddedString(16, 16)}`; + const parent = `p:${parentId.toString(TracingIdRepresentation.hex16Chars)}`; return `dd=${sampled};${origin};${parent}`; }; @@ -158,9 +179,9 @@ const generateB3Header = ({ spanId: SpanId; isSampled: boolean; }) => { - const flags = isSampled ? '1' : '0'; - return `${traceId.toPaddedString(16, 32)}-${spanId.toPaddedString( - 16, - 16 - )}-${flags}`; + return [ + traceId.toString(TracingIdRepresentation.hex32Chars), + spanId.toString(TracingIdRepresentation.hex16Chars), + isSampled ? '1' : '0' + ].join('-'); }; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/DatadogRumResource/ResourceReporter.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/DatadogRumResource/ResourceReporter.ts index 84c9986de..e49ee5f78 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/DatadogRumResource/ResourceReporter.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/DatadogRumResource/ResourceReporter.ts @@ -5,6 +5,7 @@ */ import { DdRum } from '../../../../../DdRum'; +import { TracingIdRepresentation } from '../../../distributedTracing/distributedTracing'; import type { RUMResource } from '../../interfaces/RumResource'; import { createTimings } from './resourceTiming'; @@ -37,8 +38,12 @@ const formatResourceStartContext = ( ): Record => { const attributes: Record = {}; if (tracingAttributes.samplingPriorityHeader !== '0') { - attributes['_dd.span_id'] = tracingAttributes.spanId.toString(10); - attributes['_dd.trace_id'] = tracingAttributes.traceId.toString(10); + attributes['_dd.span_id'] = tracingAttributes.spanId.toString( + TracingIdRepresentation.lowDecimal + ); + attributes['_dd.trace_id'] = tracingAttributes.traceId.toString( + TracingIdRepresentation.hex32Chars + ); attributes['_dd.rule_psr'] = tracingAttributes.rulePsr; } diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index 4bbfa0de7..a0870c801 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -23,7 +23,8 @@ import { B3_MULTI_SAMPLED_HEADER_KEY, ORIGIN_RUM, ORIGIN_HEADER_KEY, - TRACESTATE_HEADER_KEY + TRACESTATE_HEADER_KEY, + TAGS_HEADER_KEY } from '../../../distributedTracing/distributedTracingHeaders'; import { firstPartyHostsRegexMapBuilder } from '../../../distributedTracing/firstPartyHosts'; import { @@ -493,13 +494,33 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const datadogTraceValue = xhr.requestHeaders[TRACE_ID_HEADER_KEY]; - const datadogParentValue = xhr.requestHeaders[PARENT_ID_HEADER_KEY]; + // x-datadog-trace-id is expressed is just the low 64 bits (DECIMAL) + const datadogLowTraceValue = + xhr.requestHeaders[TRACE_ID_HEADER_KEY]; + + // We convert the low 64 bits to HEX + const datadogLowTraceValueHex = `${BigInt( + datadogLowTraceValue + ).toString(16)}`; + + // The high 64 bits are expressed in x-datadog-tags (HEX) + const datadogHighTraceValueHex = xhr.requestHeaders[ + TAGS_HEADER_KEY + ].split('=')[1]; // High HEX 64 bits + + // We re-compose the full 128 bit trace-id by joining the strings + const datadogTraceValue128BitHex = `${datadogHighTraceValueHex}${datadogLowTraceValueHex}`; + + // We the then get the decimal value of the trace-id + const datadogTraceValue128BitDec = hexToDecimal( + datadogTraceValue128BitHex + ); + + 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 = @@ -509,13 +530,17 @@ describe('XHRProxy', () => { const traceB3Value = b3Header.split('-')[0]; const parentB3Value = b3Header.split('-')[1]; - expect(hexToDecimal(traceContextValue)).toBe(datadogTraceValue); + expect(hexToDecimal(traceContextValue)).toBe( + datadogTraceValue128BitDec + ); expect(hexToDecimal(parentContextValue)).toBe(datadogParentValue); - - expect(hexToDecimal(b3MultiTraceHeader)).toBe(datadogTraceValue); + // + expect(hexToDecimal(b3MultiTraceHeader)).toBe( + datadogTraceValue128BitDec + ); expect(hexToDecimal(b3MultiParentHeader)).toBe(datadogParentValue); - expect(hexToDecimal(traceB3Value)).toBe(datadogTraceValue); + expect(hexToDecimal(traceB3Value)).toBe(datadogTraceValue128BitDec); expect(hexToDecimal(parentB3Value)).toBe(datadogParentValue); });