Skip to content

Commit 92bd463

Browse files
fe tracing
fe-tracing
2 parents b76eeb4 + 0eb9fdb commit 92bd463

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed

src/frontend/utils/telemetry/Instrumentation.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ const { awsEc2Detector, awsEksDetector } = require('@opentelemetry/resource-dete
88
const { containerDetector } = require('@opentelemetry/resource-detector-container');
99
const { gcpDetector } = require('@opentelemetry/resource-detector-gcp');
1010
const { envDetector, hostDetector, osDetector, processDetector } = require('@opentelemetry/resources');
11+
const { httpInstrumentationConfig } = require('./otel-custom/http');
1112

1213
const sdk = new opentelemetry.NodeSDK({
1314
traceExporter: new OTLPTraceExporter(),
1415
instrumentations: [
1516
getNodeAutoInstrumentations({
17+
'@opentelemetry/instrumentation-http': httpInstrumentationConfig,
1618
'@opentelemetry/instrumentation-fs': {
1719
enabled: false,
1820
},
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const HttpExtendedAttribute = {
2+
HTTP_REQUEST_HEADERS: 'http.request.headers',
3+
HTTP_REQUEST_BODY: 'http.request.body',
4+
HTTP_RESPONSE_HEADERS: 'http.response.headers',
5+
HTTP_RESPONSE_BODY: 'http.response.body',
6+
HTTP_PATH: 'http.path',
7+
}
8+
9+
module.exports = {
10+
HttpExtendedAttribute,
11+
};
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
const { IncomingMessage, ClientRequest } = require('http');
2+
const { HttpExtendedAttribute } = require('./constants');
3+
const { shouldCaptureBodyByMimeType } = require('./mime-type');
4+
const { StreamChunks } = require('./stream-chunks');
5+
6+
const streamChunksKey = Symbol('opentelemetry.instrumentation.http.StreamChunks');
7+
8+
const httpCustomAttributes = (
9+
span,
10+
request,
11+
response
12+
) => {
13+
if (request instanceof ClientRequest) {
14+
const reqPath = request.path.split('?')[0];
15+
span.setAttribute(HttpExtendedAttribute.HTTP_PATH, reqPath);
16+
span.setAttribute(
17+
HttpExtendedAttribute.HTTP_REQUEST_HEADERS,
18+
JSON.stringify((request).getHeaders())
19+
);
20+
}
21+
if (response instanceof IncomingMessage) {
22+
span.setAttribute(
23+
HttpExtendedAttribute.HTTP_RESPONSE_HEADERS,
24+
JSON.stringify((response).headers)
25+
);
26+
}
27+
28+
const requestBody = request[streamChunksKey];
29+
if (requestBody) {
30+
span.setAttribute(HttpExtendedAttribute.HTTP_REQUEST_BODY, requestBody.getBody());
31+
}
32+
33+
const responseBody = response[streamChunksKey];
34+
if (responseBody) {
35+
span.setAttribute(HttpExtendedAttribute.HTTP_RESPONSE_BODY, responseBody.getBody());
36+
}
37+
};
38+
39+
const httpCustomAttributesOnRequest = (span, request) => {
40+
if (request instanceof ClientRequest) {
41+
const requestMimeType = request.getHeader('content-type');
42+
if (!shouldCaptureBodyByMimeType(requestMimeType)) {
43+
span.setAttribute(
44+
HttpExtendedAttribute.HTTP_REQUEST_BODY,
45+
`Request body not collected due to unsupported mime type: ${requestMimeType}`
46+
);
47+
return;
48+
}
49+
50+
let oldWrite = request.write;
51+
request[streamChunksKey] = new StreamChunks();
52+
request.write = function (data) {
53+
const expectDevData = request[streamChunksKey];
54+
expectDevData?.addChunk(data);
55+
return oldWrite.call(request, data);
56+
};
57+
}
58+
};
59+
60+
const httpCustomAttributesOnResponse = (span, response) => {
61+
if (response instanceof IncomingMessage) {
62+
const responseMimeType = response.headers?.['content-type'];
63+
if (!shouldCaptureBodyByMimeType(responseMimeType)) {
64+
span.setAttribute(
65+
HttpExtendedAttribute.HTTP_RESPONSE_BODY,
66+
`Response body not collected due to unsupported mime type: ${responseMimeType}`
67+
);
68+
return;
69+
}
70+
71+
response[streamChunksKey] = new StreamChunks();
72+
const origPush = response.push;
73+
response.push = function (chunk) {
74+
if (chunk) {
75+
const expectDevData = response[streamChunksKey];
76+
expectDevData?.addChunk(chunk);
77+
}
78+
return origPush.apply(this, arguments);
79+
};
80+
}
81+
};
82+
83+
const httpInstrumentationConfig = {
84+
applyCustomAttributesOnSpan: httpCustomAttributes,
85+
requestHook: httpCustomAttributesOnRequest,
86+
responseHook: httpCustomAttributesOnResponse,
87+
headersToSpanAttributes: {
88+
client: {
89+
requestHeaders: ['traceloop_id'],
90+
responseHeaders: ['traceloop_id'],
91+
},
92+
server: {
93+
requestHeaders: ['traceloop_id'],
94+
responseHeaders: ['traceloop_id'],
95+
},
96+
},
97+
};
98+
99+
module.exports = {
100+
httpInstrumentationConfig
101+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const allowedMimeTypePrefix = [
2+
'text',
3+
'multipart/form-data',
4+
'application/json',
5+
'application/ld+json',
6+
'application/rtf',
7+
'application/x-www-form-urlencoded',
8+
'application/xml',
9+
'application/xhtml',
10+
];
11+
12+
const shouldCaptureBodyByMimeType = (mimeType) => {
13+
try {
14+
return !mimeType || allowedMimeTypePrefix.some((prefix) => mimeType.startsWith(prefix));
15+
} catch {
16+
return true;
17+
}
18+
};
19+
20+
module.exports = {
21+
shouldCaptureBodyByMimeType,
22+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// for body with at most this length, full body will be captured.
2+
// for large body with more than this amount of bytes, we will
3+
// collect at least this amount of bytes, but might truncate after it
4+
const MIN_COLLECTED_BODY_LENGTH = 524288;
5+
6+
class StreamChunks {
7+
chunks;
8+
length;
9+
10+
constructor() {
11+
this.chunks = [];
12+
this.length = 0;
13+
}
14+
15+
addChunk(chunk) {
16+
if (this.length >= MIN_COLLECTED_BODY_LENGTH) return;
17+
18+
const chunkLength = chunk?.length;
19+
if (!chunkLength) return;
20+
21+
this.chunks.push(chunk);
22+
this.length += chunkLength;
23+
}
24+
25+
getBody() {
26+
return this.chunks.join('');
27+
}
28+
}
29+
30+
module.exports = {
31+
StreamChunks,
32+
MIN_COLLECTED_BODY_LENGTH,
33+
};

0 commit comments

Comments
 (0)