Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions integration_tests/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ functions:
environment:
DD_FLUSH_TO_LOG: true

# process-input-traced
with-span-links-traced_node:
name: integration-tests-js-${sls:stage}-with-span-links-traced_${env:RUNTIME}
handler: process-input-traced.handle
runtime: ${env:SERVERLESS_RUNTIME}
layers:
- { Ref: NodeLambdaLayer }
environment:
DD_FLUSH_TO_LOG: true
DD_USE_SPAN_LINKS: true

# throw-error-traced
throw-error-traced_node:
name: integration-tests-js-${sls:stage}-throw-error-traced_${env:RUNTIME}
Expand Down
8 changes: 4 additions & 4 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,19 @@ let mockTraceSource: TraceSource | undefined = undefined;

jest.mock("./trace/trace-context-service", () => {
class MockTraceContextService {
extract(event: any, context: Context): SpanContextWrapper {
return mockSpanContextWrapper;
extract(event: any, context: Context): SpanContextWrapper[] {
return [mockSpanContextWrapper];
}

get traceSource() {
return mockTraceSource;
}
get currentTraceContext() {
return mockSpanContextWrapper;
return [mockSpanContextWrapper];
}

get currentTraceHeaders() {
return mockTraceHeaders;
return [mockTraceHeaders];
}
}
return {
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const minColdStartTraceDurationEnvVar = "DD_MIN_COLD_START_DURATION";
export const coldStartTraceSkipLibEnvVar = "DD_COLD_START_TRACE_SKIP_LIB";
export const localTestingEnvVar = "DD_LOCAL_TESTING";
export const addSpanPointersEnvVar = "DD_TRACE_AWS_ADD_SPAN_POINTERS";
export const useSpanLinksEnvVar = "DD_USE_SPAN_LINKS";

interface GlobalConfig {
/**
Expand Down Expand Up @@ -97,6 +98,7 @@ export const defaultConfig: Config = {
coldStartTraceSkipLib: "",
localTesting: false,
addSpanPointers: true,
useSpanLinks: false
} as const;

export const _metricsQueue: MetricsQueue = new MetricsQueue();
Expand Down Expand Up @@ -322,7 +324,7 @@ export function getTraceHeaders(): Partial<DatadogTraceHeaders> {
if (currentTraceListener === undefined) {
return {};
}
return currentTraceListener.currentTraceHeaders;
return currentTraceListener.currentTraceHeaders[0];
}

function getConfig(userConfig?: Partial<Config>): Config {
Expand Down Expand Up @@ -419,6 +421,11 @@ function getConfig(userConfig?: Partial<Config>): Config {
config.addSpanPointers = result === "true";
}

if (userConfig === undefined || userConfig.useSpanLinks === undefined) {
const result = getEnvValue(useSpanLinksEnvVar, "false").toLowerCase();
config.useSpanLinks = result === "true";
}

return config;
}

Expand Down
6 changes: 3 additions & 3 deletions src/trace/context/extractor-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ describe("extractor-utils", () => {
expect(result).not.toBeNull();
});

it("returns null when no trace context can be extracted", () => {
it("returns an empty array when no trace context can be extracted", () => {
const emptyEvent = {
someOtherProperty: "value",
};

const tracerWrapper = new TracerWrapper();
const result = extractTraceContext(emptyEvent, tracerWrapper);

expect(result).toBeNull();
expect(result).toStrictEqual([]);
});

it("extracts context from LambdaRootStepFunctionContext", () => {
Expand Down Expand Up @@ -158,7 +158,7 @@ describe("extractor-utils", () => {

const result = extractFromAWSTraceHeader(invalidHeader, eventType);

expect(result).toBeNull();
expect(result).toStrictEqual([]);
});
});
});
14 changes: 7 additions & 7 deletions src/trace/context/extractor-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ import { XrayService } from "../xray-service";
* @param tracerWrapper The tracer wrapper instance
* @returns SpanContextWrapper or null
*/
export function extractTraceContext(headers: any, tracerWrapper: TracerWrapper): SpanContextWrapper | null {
export function extractTraceContext(headers: any, tracerWrapper: TracerWrapper): SpanContextWrapper[] {
// First try to extract as regular trace headers
const traceContext = tracerWrapper.extract(headers);
if (traceContext) {
return traceContext;
return [traceContext];
}

// If that fails, check if this is a Step Function context
const stepFunctionInstance = StepFunctionContextService.instance(headers);

if (stepFunctionInstance.context !== undefined) {
if (stepFunctionInstance.spanContext !== null) {
return stepFunctionInstance.spanContext;
return [stepFunctionInstance.spanContext];
}
}

return null;
return [];
}

/**
Expand All @@ -39,14 +39,14 @@ export function extractTraceContext(headers: any, tracerWrapper: TracerWrapper):
* @param eventType The type of event (for logging)
* @returns SpanContextWrapper or null
*/
export function extractFromAWSTraceHeader(awsTraceHeader: string, eventType: string): SpanContextWrapper | null {
export function extractFromAWSTraceHeader(awsTraceHeader: string, eventType: string): SpanContextWrapper[] {
const traceContext = XrayService.extraceDDContextFromAWSTraceHeader(awsTraceHeader);
if (traceContext) {
logDebug(`Extracted trace context from ${eventType} event attributes AWSTraceHeader`);
return traceContext;
return [traceContext];
} else {
logDebug(`No Datadog trace context found from ${eventType} event attributes AWSTraceHeader`);
return null;
return [];
}
}

Expand Down
122 changes: 61 additions & 61 deletions src/trace/context/extractor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ describe("TraceContextExtractor", () => {

expect(spyCustomExtractor).toHaveBeenCalled();

expect(traceContext?.toTraceId()).toBe("4110911582297405551");
expect(traceContext?.toSpanId()).toBe("797643193680388251");
expect(traceContext?.sampleMode()).toBe("2");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("4110911582297405551");
expect(traceContext?.[0].toSpanId()).toBe("797643193680388251");
expect(traceContext?.[0].sampleMode()).toBe("2");
expect(traceContext?.[0].source).toBe("event");
});
});
describe("event", () => {
Expand Down Expand Up @@ -180,10 +180,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "4110911582297405551",
});

expect(traceContext?.toTraceId()).toBe("4110911582297405551");
expect(traceContext?.toSpanId()).toBe("797643193680388251");
expect(traceContext?.sampleMode()).toBe("2");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("4110911582297405551");
expect(traceContext?.[0].toSpanId()).toBe("797643193680388251");
expect(traceContext?.[0].sampleMode()).toBe("2");
expect(traceContext?.[0].source).toBe("event");
});

// SNS message event (String Value)
Expand Down Expand Up @@ -242,10 +242,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "6966585609680374559",
});

expect(traceContext?.toTraceId()).toBe("6966585609680374559");
expect(traceContext?.toSpanId()).toBe("4297634551783724228");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("6966585609680374559");
expect(traceContext?.[0].toSpanId()).toBe("4297634551783724228");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// SNS message event (Binary Value)
Expand Down Expand Up @@ -303,10 +303,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "7102291628443134919",
});

expect(traceContext?.toTraceId()).toBe("7102291628443134919");
expect(traceContext?.toSpanId()).toBe("4247550101648618618");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("7102291628443134919");
expect(traceContext?.[0].toSpanId()).toBe("4247550101648618618");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// SNS message delivered to SQS queue event (String Value)
Expand Down Expand Up @@ -354,10 +354,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "2776434475358637757",
});

expect(traceContext?.toTraceId()).toBe("2776434475358637757");
expect(traceContext?.toSpanId()).toBe("4493917105238181843");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("2776434475358637757");
expect(traceContext?.[0].toSpanId()).toBe("4493917105238181843");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// SNS message delivered to SQS queue event (Binary Value)
Expand Down Expand Up @@ -404,10 +404,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "7102291628443134919",
});

expect(traceContext?.toTraceId()).toBe("7102291628443134919");
expect(traceContext?.toSpanId()).toBe("4247550101648618618");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("7102291628443134919");
expect(traceContext?.[0].toSpanId()).toBe("4247550101648618618");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// EventBridge message delivered to SQS queue event
Expand Down Expand Up @@ -456,10 +456,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "7379586022458917877",
});

expect(traceContext?.toTraceId()).toBe("7379586022458917877");
expect(traceContext?.toSpanId()).toBe("2644033662113726488");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("7379586022458917877");
expect(traceContext?.[0].toSpanId()).toBe("2644033662113726488");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// AppSync event
Expand Down Expand Up @@ -496,10 +496,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "4110911582297405557",
});

expect(traceContext?.toTraceId()).toBe("797643193680388254");
expect(traceContext?.toSpanId()).toBe("4110911582297405557");
expect(traceContext?.sampleMode()).toBe("2");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("797643193680388254");
expect(traceContext?.[0].toSpanId()).toBe("4110911582297405557");
expect(traceContext?.[0].sampleMode()).toBe("2");
expect(traceContext?.[0].source).toBe("event");
});

// SQS queue message event
Expand Down Expand Up @@ -554,10 +554,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "4555236104497098341",
});

expect(traceContext?.toTraceId()).toBe("4555236104497098341");
expect(traceContext?.toSpanId()).toBe("3369753143434738315");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("4555236104497098341");
expect(traceContext?.[0].toSpanId()).toBe("3369753143434738315");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// Kinesis stream event
Expand Down Expand Up @@ -604,10 +604,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "667309514221035538",
});

expect(traceContext?.toTraceId()).toBe("667309514221035538");
expect(traceContext?.toSpanId()).toBe("1350735035497811828");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("667309514221035538");
expect(traceContext?.[0].toSpanId()).toBe("1350735035497811828");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// EventBridge message event
Expand Down Expand Up @@ -651,10 +651,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-sampling-priority": "1",
});

expect(traceContext?.toTraceId()).toBe("5827606813695714842");
expect(traceContext?.toSpanId()).toBe("4726693487091824375");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("5827606813695714842");
expect(traceContext?.[0].toSpanId()).toBe("4726693487091824375");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

// StepFunction context event
Expand Down Expand Up @@ -688,10 +688,10 @@ describe("TraceContextExtractor", () => {
const traceContext = await extractor.extract(event, {} as Context);
expect(traceContext).not.toBeNull();

expect(traceContext?.toTraceId()).toBe("1139193989631387307");
expect(traceContext?.toSpanId()).toBe("7747304477664363642");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("1139193989631387307");
expect(traceContext?.[0].toSpanId()).toBe("7747304477664363642");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});

it("extracts and applies deterministic trace ID from StepFunction event end-to-end", async () => {
Expand Down Expand Up @@ -721,25 +721,25 @@ describe("TraceContextExtractor", () => {
expect(traceContext).not.toBeNull();

// Verify the trace context was extracted from Step Function
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].source).toBe("event");

// Verify the trace ID is deterministic based on execution ID
// The trace ID should be generated from SHA256 hash of the execution ID
const traceId = traceContext?.toTraceId();
const traceId = traceContext?.[0].toTraceId();
expect(traceId).toBeDefined();
expect(traceId).not.toBe("0"); // Should not be zero
expect(traceId).toMatch(/^\d+$/); // Should be numeric string

// Verify the span ID is deterministic based on execution ID, state name, and entered time
const spanId = traceContext?.toSpanId();
const spanId = traceContext?.[0].toSpanId();
expect(spanId).toBeDefined();
expect(spanId).not.toBe("0"); // Should not be zero
expect(spanId).toMatch(/^\d+$/); // Should be numeric string

// Verify that extracting the same event produces the same trace IDs (deterministic)
const traceContext2 = await extractor.extract(event, {} as Context);
expect(traceContext2?.toTraceId()).toBe(traceId);
expect(traceContext2?.toSpanId()).toBe(spanId);
expect(traceContext2?.[0].toTraceId()).toBe(traceId);
expect(traceContext2?.[0].toSpanId()).toBe(spanId);
});
});

Expand Down Expand Up @@ -775,10 +775,10 @@ describe("TraceContextExtractor", () => {
"x-datadog-trace-id": "667309514221035538",
});

expect(traceContext?.toTraceId()).toBe("667309514221035538");
expect(traceContext?.toSpanId()).toBe("1350735035497811828");
expect(traceContext?.sampleMode()).toBe("1");
expect(traceContext?.source).toBe("event");
expect(traceContext?.[0].toTraceId()).toBe("667309514221035538");
expect(traceContext?.[0].toSpanId()).toBe("1350735035497811828");
expect(traceContext?.[0].sampleMode()).toBe("1");
expect(traceContext?.[0].source).toBe("event");
});
});
});
Expand All @@ -794,10 +794,10 @@ describe("TraceContextExtractor", () => {
const traceContext = await extractor.extract({}, {} as Context);
expect(traceContext).not.toBeNull();

expect(traceContext?.toTraceId()).toBe("4110911582297405557");
expect(traceContext?.toSpanId()).toBe("797643193680388254");
expect(traceContext?.sampleMode()).toBe("2");
expect(traceContext?.source).toBe("xray");
expect(traceContext?.[0].toTraceId()).toBe("4110911582297405557");
expect(traceContext?.[0].toSpanId()).toBe("797643193680388254");
expect(traceContext?.[0].sampleMode()).toBe("2");
expect(traceContext?.[0].source).toBe("xray");
});
});
});
Expand Down
Loading