generated from baselime/template
-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.ts
99 lines (88 loc) · 3.71 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import type { Handle, MaybePromise, RequestEvent, ResolveOptions } from '@sveltejs/kit';
import { trace } from '@opentelemetry/api';
import { flatten } from 'flat';
type TraceOptions = {
captureRequestBody?: boolean;
captureResponseBody?: boolean;
requestIdHeader?: string;
}
const possibleRequestIdHeaders = [
"x-request-id",
"x-vercel-id"
];
/**
*
* @param fn {Handle} The handle hook runs every time the SvelteKit server receives a request and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
* @param opts.captureRequestBody {boolean} Capture the request body in the span.
* @param opts.captureResponseBody {boolean} Capture the response body in the span.
* @param opts.requestIdHeader {string} The header to look for the request id in. By default it will check x-request-id and x-vercel-id.
* @returns {Handle}
*/
export function withOpenTelemetry(fn: Handle, opts?: TraceOptions): Handle {
opts = opts || {};
const tracer = trace.getTracer('@baselime/sveltekit-opentelemetry-middleware');
return async function (args: {
event: RequestEvent;
resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
}) {
const name = `${args.event.request.method} ${args.event.route.id}`;
let requestId: string | undefined = undefined;
if (opts.requestIdHeader) {
possibleRequestIdHeaders.push(opts.requestIdHeader.toLowerCase());
}
for (let header of possibleRequestIdHeaders) {
const val = args.event.request.headers.get(header);
if (header === "x-vercel-id" && val) {
// Only match the last part of the id because otherwise it can differ from the logs as it appends the edge location region to it.
const requestIdParts = val.split("::");
const id = requestIdParts[requestIdParts.length - 1];
requestId = id;
}
if (val) {
requestId = val;
break;
}
}
const url = new URL(args.event.request.url);
return tracer.startActiveSpan(name, {
// SpanKind.SERVER -> 1
kind: 1,
attributes: flatten({
requestId,
http: {
headers: args.event.request.headers,
pathname: url.pathname,
method: args.event.request.method,
url: args.event.request.url,
...opts.captureRequestBody && {
request: {
// todo format based on headers
body: args.event.request.body,
}
},
},
svelte: {
route: args.event.route,
isDataRequest: args.event.isDataRequest,
isSubRequest: args.event.isSubRequest,
params: args.event.params,
platform: args.event.platform
}
})
}, async (span) => {
const res = await fn(args);
span.setAttributes(flatten({
http: {
status: res.status,
...opts.captureResponseBody && {
response: {
body: res.body
}
}
}
}));
span.end();
return res;
});
};
}