fix: scope request logs to incoming traffic
This commit is contained in:
+87
-26
@@ -1,3 +1,5 @@
|
||||
import { AsyncLocalStorage } from "node:async_hooks";
|
||||
|
||||
export type ProcessLogEntry = Record<string, unknown>;
|
||||
|
||||
export type ProcessLogger = {
|
||||
@@ -5,6 +7,7 @@ export type ProcessLogger = {
|
||||
};
|
||||
|
||||
const MAX_RESPONSE_LOG_BYTES = 64 * 1024;
|
||||
const requestContext = new AsyncLocalStorage<{ active: boolean }>();
|
||||
|
||||
export function logValue(
|
||||
value: unknown,
|
||||
@@ -42,10 +45,63 @@ export function logValue(
|
||||
}
|
||||
}
|
||||
|
||||
const METHOD_LABELS: Record<string, string> = {
|
||||
GET: "GET",
|
||||
POST: "PST",
|
||||
PUT: "PUT",
|
||||
PATCH: "PTC",
|
||||
DELETE: "DEL",
|
||||
HEAD: "HED",
|
||||
OPTIONS: "OPT",
|
||||
CONNECT: "CON",
|
||||
TRACE: "TRC",
|
||||
};
|
||||
|
||||
function requestLine(entry: ProcessLogEntry): string | undefined {
|
||||
const event = entry.event;
|
||||
if (
|
||||
event === "kuber.server.request.start" ||
|
||||
event === "kuber.k8s.request.start" ||
|
||||
event === "kuber.server.response.body"
|
||||
)
|
||||
return "";
|
||||
const kubernetes =
|
||||
event === "kuber.k8s.request.end" ||
|
||||
event === "kuber.k8s.request.failed";
|
||||
if (kubernetes && !requestContext.getStore()?.active) return "";
|
||||
if (
|
||||
!kubernetes &&
|
||||
event !== "kuber.server.request.end" &&
|
||||
event !== "kuber.server.request.failed"
|
||||
)
|
||||
return;
|
||||
if (typeof entry.method !== "string" || typeof entry.pathname !== "string")
|
||||
return;
|
||||
if (
|
||||
!kubernetes &&
|
||||
entry.method.toUpperCase() === "GET" &&
|
||||
entry.pathname === "/api/v2/health"
|
||||
)
|
||||
return "";
|
||||
|
||||
const method = entry.method.toUpperCase();
|
||||
const label = METHOD_LABELS[method] ?? method.slice(0, 3).padEnd(3, "_");
|
||||
const status =
|
||||
event === "kuber.k8s.request.failed" ||
|
||||
event === "kuber.server.request.failed"
|
||||
? "ERR"
|
||||
: entry.status;
|
||||
if (typeof status !== "number" && status !== "ERR") return;
|
||||
const indent = kubernetes ? " " : "";
|
||||
return `${indent}${label} ${entry.pathname} ${status}`;
|
||||
}
|
||||
|
||||
export const processLogger: ProcessLogger = {
|
||||
log(entry) {
|
||||
try {
|
||||
console.log(`KUBER_REQUEST ${JSON.stringify(logValue(entry))}`);
|
||||
const line = requestLine(entry);
|
||||
if (line === "") return;
|
||||
console.log(line ?? `KUBER_REQUEST ${JSON.stringify(logValue(entry))}`);
|
||||
} catch {
|
||||
// Process logging must never change request behavior.
|
||||
}
|
||||
@@ -168,33 +224,38 @@ export async function logServerRequest<T>(
|
||||
headers: headers(request.headers),
|
||||
};
|
||||
safeLog(logger, { event: "kuber.server.request.start", ...base });
|
||||
try {
|
||||
const response = await handler();
|
||||
safeLog(logger, {
|
||||
event: "kuber.server.request.end",
|
||||
...base,
|
||||
status: response instanceof Response ? response.status : 101,
|
||||
durationMs: Date.now() - startedAt,
|
||||
...(response instanceof Response && {
|
||||
responseHeaders: headers(response.headers),
|
||||
}),
|
||||
});
|
||||
if (response instanceof Response)
|
||||
logResponseBody(response, logger, {
|
||||
event: "kuber.server.response.body",
|
||||
const context = { active: true };
|
||||
return requestContext.run(context, async () => {
|
||||
try {
|
||||
const response = await handler();
|
||||
safeLog(logger, {
|
||||
event: "kuber.server.request.end",
|
||||
...base,
|
||||
status: response.status,
|
||||
status: response instanceof Response ? response.status : 101,
|
||||
durationMs: Date.now() - startedAt,
|
||||
...(response instanceof Response && {
|
||||
responseHeaders: headers(response.headers),
|
||||
}),
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
safeLog(logger, {
|
||||
event: "kuber.server.request.failed",
|
||||
...base,
|
||||
durationMs: Date.now() - startedAt,
|
||||
error: logValue(error),
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
if (response instanceof Response)
|
||||
logResponseBody(response, logger, {
|
||||
event: "kuber.server.response.body",
|
||||
...base,
|
||||
status: response.status,
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
safeLog(logger, {
|
||||
event: "kuber.server.request.failed",
|
||||
...base,
|
||||
durationMs: Date.now() - startedAt,
|
||||
error: logValue(error),
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
context.active = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function logKubernetesRequest<T>(
|
||||
|
||||
Reference in New Issue
Block a user