Skip to content

Commit 7cb5350

Browse files
committed
extract trace context from client context
1 parent 7d1b8a7 commit 7cb5350

File tree

3 files changed

+130
-9
lines changed

3 files changed

+130
-9
lines changed

src/trace/context.spec.ts

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
readStepFunctionContextFromEvent,
2020
readTraceFromSQSEvent,
2121
readTraceFromHTTPEvent,
22+
readTraceFromLambdaContext,
2223
} from "./context";
2324

2425
let sentSegment: any;
@@ -388,6 +389,41 @@ describe("readTraceFromSQSEvent", () => {
388389
});
389390
});
390391

392+
describe("readTraceFromLambdaContext", () => {
393+
it("can read from lambda context source", () => {
394+
const result = readTraceFromLambdaContext({
395+
clientContext: {
396+
custom: {
397+
_datadog: {
398+
"x-datadog-trace-id":"666",
399+
"x-datadog-parent-id":"777",
400+
"x-datadog-sampled":"1",
401+
"x-datadog-sampling-priority":"1"
402+
}
403+
}
404+
}
405+
});
406+
expect(result).toEqual({
407+
parentID: "777",
408+
sampleMode: SampleMode.AUTO_KEEP,
409+
traceID: "666",
410+
source: Source.Event,
411+
});
412+
});
413+
it("can handle no `custom` key", () => {
414+
const result = readTraceFromLambdaContext({
415+
clientContext: {
416+
foo: 'bar'
417+
}
418+
});
419+
expect(result).toBeUndefined();
420+
});
421+
it("can handle no context", () => {
422+
const result = readTraceFromLambdaContext(undefined);
423+
expect(result).toBeUndefined();
424+
});
425+
});
426+
391427
describe("readStepFunctionContextFromEvent", () => {
392428
const stepFunctionEvent = {
393429
dd: {
@@ -532,7 +568,7 @@ describe("extractTraceContext", () => {
532568
"x-datadog-sampling-priority": "2",
533569
"x-datadog-trace-id": "4110911582297405551",
534570
},
535-
});
571+
}, {});
536572
expect(result).toEqual({
537573
parentID: "797643193680388251",
538574
sampleMode: SampleMode.USER_KEEP,
@@ -567,7 +603,53 @@ describe("extractTraceContext", () => {
567603
awsRegion: "sa-east-1",
568604
},
569605
],
606+
}, {});
607+
expect(result).toEqual({
608+
parentID: "3369753143434738315",
609+
sampleMode: SampleMode.AUTO_KEEP,
610+
traceID: "4555236104497098341",
611+
source: Source.Event,
570612
});
613+
});
614+
it("returns trace read from Lambda Context as third highest priority", () => {
615+
process.env["_X_AMZN_TRACE_ID"] = "Root=1-5ce31dc2-2c779014b90ce44db5e03875;Parent=0b11cc4230d3e09e;Sampled=1";
616+
const lambdaContext = {
617+
clientContext: {
618+
custom: {
619+
_datadog: {
620+
"x-datadog-trace-id":"4555236104497098341",
621+
"x-datadog-parent-id":"3369753143434738315",
622+
"x-datadog-sampled":"1",
623+
"x-datadog-sampling-priority":"1"
624+
}
625+
}
626+
}
627+
};
628+
const result = extractTraceContext({
629+
Records: [
630+
{
631+
body: "Hello world",
632+
attributes: {
633+
ApproximateReceiveCount: "1",
634+
SentTimestamp: "1605544528092",
635+
SenderId: "AROAYYB64AB3JHSRKO6XR:sqs-trace-dev-producer",
636+
ApproximateFirstReceiveTimestamp: "1605544528094",
637+
},
638+
messageAttributes: {
639+
_datadog: {
640+
stringValue:
641+
'{"x-datadog-parent-id":"666","x-datadog-sampled":"1","x-datadog-sampling-priority":"1"}',
642+
stringListValues: [],
643+
binaryListValues: [],
644+
dataType: "String",
645+
},
646+
},
647+
eventSource: "aws:sqs",
648+
eventSourceARN: "arn:aws:sqs:sa-east-1:601427279990:metal-queue",
649+
awsRegion: "sa-east-1",
650+
},
651+
],
652+
}, lambdaContext);
571653
expect(result).toEqual({
572654
parentID: "3369753143434738315",
573655
sampleMode: SampleMode.AUTO_KEEP,
@@ -578,7 +660,7 @@ describe("extractTraceContext", () => {
578660
it("returns trace read from env if no headers present", () => {
579661
process.env["_X_AMZN_TRACE_ID"] = "Root=1-5ce31dc2-2c779014b90ce44db5e03875;Parent=0b11cc4230d3e09e;Sampled=1";
580662

581-
const result = extractTraceContext({});
663+
const result = extractTraceContext({}, {});
582664
expect(result).toEqual({
583665
parentID: "797643193680388254",
584666
sampleMode: SampleMode.USER_KEEP,
@@ -589,7 +671,7 @@ describe("extractTraceContext", () => {
589671
it("returns trace read from env if no headers present", () => {
590672
process.env["_X_AMZN_TRACE_ID"] = "Root=1-5ce31dc2-2c779014b90ce44db5e03875;Parent=0b11cc4230d3e09e;Sampled=1";
591673

592-
const result = extractTraceContext({});
674+
const result = extractTraceContext({}, {});
593675
expect(result).toEqual({
594676
parentID: "797643193680388254",
595677
sampleMode: SampleMode.USER_KEEP,
@@ -608,7 +690,7 @@ describe("extractTraceContext", () => {
608690
"x-datadog-sampling-priority": "2",
609691
"x-datadog-trace-id": "4110911582297405551",
610692
},
611-
});
693+
}, {});
612694

613695
expect(sentSegment instanceof Buffer).toBeTruthy();
614696
expect(closedSocket).toBeTruthy();
@@ -628,7 +710,7 @@ describe("extractTraceContext", () => {
628710
"x-datadog-sampling-priority": "2",
629711
"x-datadog-trace-id": "4110911582297405551",
630712
},
631-
});
713+
}, {});
632714

633715
expect(sentSegment).toBeUndefined();
634716
});
@@ -654,7 +736,7 @@ describe("extractTraceContext", () => {
654736
process.env[xrayTraceEnvVar] = "Root=1-5e272390-8c398be037738dc042009320;Parent=94ae789b969f1cc5;Sampled=1";
655737
process.env[awsXrayDaemonAddressEnvVar] = "localhost:127.0.0.1:2000";
656738

657-
extractTraceContext(stepFunctionEvent);
739+
extractTraceContext(stepFunctionEvent, {});
658740

659741
expect(sentSegment instanceof Buffer).toBeTruthy();
660742

src/trace/context.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Context } from "aws-lambda";
12
import { BigNumber } from "bignumber.js";
23
import { randomBytes } from "crypto";
34
import { createSocket, Socket } from "dgram";
@@ -47,8 +48,13 @@ function isSQSEvent(event: any): event is SQSEvent {
4748
* Reads the trace context from either an incoming lambda event, or the current xray segment.
4849
* @param event An incoming lambda event. This must have incoming trace headers in order to be read.
4950
*/
50-
export function extractTraceContext(event: any) {
51-
const trace = readTraceFromEvent(event);
51+
export function extractTraceContext(event: any, context: any) {
52+
let trace = readTraceFromEvent(event);
53+
54+
if (trace === undefined) {
55+
trace = readTraceFromLambdaContext(context)
56+
}
57+
5258
const stepFuncContext = readStepFunctionContextFromEvent(event);
5359
if (stepFuncContext) {
5460
try {
@@ -188,6 +194,39 @@ export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined
188194
return;
189195
}
190196

197+
export function readTraceFromLambdaContext(context: any): TraceContext | undefined {
198+
if (!context || typeof context !== "object") {
199+
return;
200+
}
201+
202+
const traceData = context.clientContext?.custom?._datadog;
203+
204+
if (!traceData || typeof traceData !== "object") {
205+
return;
206+
}
207+
208+
const traceID = traceData[traceIDHeader];
209+
if (typeof traceID !== "string") {
210+
return;
211+
}
212+
const parentID = traceData[parentIDHeader];
213+
if (typeof parentID !== "string") {
214+
return;
215+
}
216+
const sampledHeader = traceData[samplingPriorityHeader];
217+
if (typeof sampledHeader !== "string") {
218+
return;
219+
}
220+
const sampleMode = parseInt(sampledHeader, 10);
221+
222+
return {
223+
parentID,
224+
sampleMode,
225+
source: Source.Event,
226+
traceID,
227+
};
228+
}
229+
191230
export function readTraceFromHTTPEvent(event: any): TraceContext | undefined {
192231
const headers = event.headers;
193232
const lowerCaseHeaders: { [key: string]: string } = {};

src/trace/listener.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class TraceListener {
6060
}
6161

6262
this.context = context;
63-
this.contextService.rootTraceContext = extractTraceContext(event);
63+
this.contextService.rootTraceContext = extractTraceContext(event, context);
6464
this.stepFunctionContext = readStepFunctionContextFromEvent(event);
6565
}
6666

0 commit comments

Comments
 (0)