@sentry/node
Version:
Sentry Node SDK using OpenTelemetry for performance instrumentation
65 lines (62 loc) • 2.1 kB
JavaScript
import { GLOBAL_OBJ, getActiveSpan, getRootSpan } from '@sentry/core';
import { _INTERNAL_safeUnref } from '@sentry/core/server';
import { subscribeDiagnosticsChannel } from '@sentry/server-utils';
const HTTP_ON_SERVER_RESPONSE_FINISH = "http.server.response.finish";
const REQUEST_END_TIMEOUT_MS = 2e3;
let keepAliveClient;
function setupVercelKeepAlive(client) {
const isRegistered = !!keepAliveClient;
keepAliveClient = client;
if (isRegistered) {
return;
}
process.on("SIGTERM", async () => {
await keepAliveClient?.flush(200);
});
subscribeDiagnosticsChannel(HTTP_ON_SERVER_RESPONSE_FINISH, (message) => {
const { response } = message;
const requestContextGlobal = (
// @ts-expect-error Vercel sets this global, so `GLOBAL_OBJ` does not type it
GLOBAL_OBJ[/* @__PURE__ */ Symbol.for("@vercel/request-context")]
);
const requestContext = requestContextGlobal?.get?.();
const client2 = keepAliveClient;
if (!client2 || !response || !requestContext?.waitUntil) {
return;
}
const activeSpan = getActiveSpan();
const rootSpan = activeSpan && getRootSpan(activeSpan);
requestContext.waitUntil(
waitForRequestEnd(client2, response, rootSpan, REQUEST_END_TIMEOUT_MS).then(() => client2.flush(2e3))
);
});
}
function waitForRequestEnd(client, response, rootSpan, timeout) {
return new Promise((resolve) => {
let responseClosed = false;
let rootSpanEnded = !rootSpan?.isRecording();
const done = () => {
clearTimeout(timer);
unsubscribe();
resolve();
};
const doneIfRequestEnded = () => {
if (responseClosed && rootSpanEnded) {
done();
}
};
const timer = _INTERNAL_safeUnref(setTimeout(done, timeout));
const unsubscribe = client.on("spanEnd", (span) => {
if (span === rootSpan) {
rootSpanEnded = true;
doneIfRequestEnded();
}
});
response.once("close", () => {
responseClosed = true;
doneIfRequestEnded();
});
});
}
export { setupVercelKeepAlive };
//# sourceMappingURL=vercel.js.map