Skip to content

Commit 823864f

Browse files
author
Raphael Manke
committed
add workaround
1 parent d78f20f commit 823864f

File tree

3 files changed

+811
-364
lines changed

3 files changed

+811
-364
lines changed

Diff for: lib/node-18-fetch-with-tracing.ts

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { LambdaInterface } from "@aws-lambda-powertools/commons";
2+
import { Tracer } from "@aws-lambda-powertools/tracer";
3+
import nodeFetch from "node-fetch";
4+
import * as diagnosticsChannel from "diagnostics_channel";
5+
6+
import { Subsegment, Segment } from "aws-xray-sdk-core";
7+
8+
const lambdaTracer = new Tracer();
9+
10+
export class Lambda implements LambdaInterface {
11+
// Decorate your handler class method
12+
13+
constructor() {
14+
addNativeFetchTracing(lambdaTracer);
15+
}
16+
17+
@lambdaTracer.captureMethod()
18+
public async nativeFetch() {
19+
const response = await fetch("https://api.github.com/users/aws-samples");
20+
const json = await response.json();
21+
return json;
22+
}
23+
24+
@lambdaTracer.captureMethod()
25+
public async nodeFetch() {
26+
const response = await nodeFetch(
27+
"https://api.github.com/users/aws-samples"
28+
);
29+
const json = await response.json();
30+
return json;
31+
}
32+
33+
public async handler(_event: unknown, _context: unknown): Promise<void> {
34+
await this.nativeFetch();
35+
await this.nodeFetch();
36+
}
37+
}
38+
39+
const handlerClass = new Lambda();
40+
export const handler = handlerClass.handler.bind(handlerClass); //
41+
42+
const segments = new Map<
43+
any,
44+
{ subsegment: Subsegment; parentSubsegment: Segment | Subsegment }
45+
>();
46+
47+
function addNativeFetchTracing(tracer: Tracer) {
48+
/**
49+
* This is the first event emitted when a request is created.
50+
* Based on this event a new subsegment is created.
51+
* To have a reference to the subsegment created, it is stored in a map.
52+
*/
53+
diagnosticsChannel
54+
.channel("undici:request:create")
55+
.subscribe(({ request }: any) => {
56+
const parentSubsegment = tracer.getSegment(); // This is the subsegment currently active
57+
let subsegment;
58+
if (parentSubsegment) {
59+
const [_, baseUrl] = request.origin.split("//");
60+
61+
subsegment = parentSubsegment.addNewSubsegment(baseUrl);
62+
tracer.setSegment(subsegment);
63+
subsegment.addAttribute("namespace", "remote");
64+
}
65+
segments.set(request, {
66+
subsegment: subsegment!,
67+
parentSubsegment: parentSubsegment!,
68+
});
69+
});
70+
71+
/**
72+
* When the response is received, the response data and headers are added to the subsegment.
73+
*/
74+
diagnosticsChannel
75+
.channel("undici:request:headers")
76+
.subscribe(async ({ request, response }: any) => {
77+
const { subsegment, parentSubsegment } = segments.get(request)!;
78+
79+
if (parentSubsegment && subsegment) {
80+
const [protocol, host] = request.origin.split("//");
81+
82+
const headers = arrayToObject(response.headers);
83+
subsegment.addRemoteRequestData(
84+
{
85+
...request,
86+
host,
87+
agent: {
88+
protocol,
89+
},
90+
},
91+
{
92+
...response,
93+
headers: headers,
94+
}
95+
);
96+
}
97+
});
98+
99+
/**
100+
* The subsegment is closed when the response is finished.
101+
*/
102+
diagnosticsChannel
103+
.channel("undici:request:trailers")
104+
.subscribe(({ request }: any) => {
105+
const { subsegment, parentSubsegment } = segments.get(request)!;
106+
107+
if (parentSubsegment && subsegment) {
108+
subsegment.close();
109+
tracer.setSegment(parentSubsegment);
110+
segments.delete(request);
111+
}
112+
});
113+
}
114+
115+
function arrayToObject(arr: any[]): { [key: string]: any } {
116+
const result: { [key: string]: any } = {};
117+
118+
if (arr.length % 2 !== 0) {
119+
throw new Error("Array length should be even to form key-value pairs.");
120+
}
121+
122+
for (let i = 0; i < arr.length; i += 2) {
123+
const key = arr[i].toString().toLowerCase();
124+
const value = arr[i + 1].toString();
125+
result[key] = value;
126+
}
127+
128+
return result;
129+
}

0 commit comments

Comments
 (0)