fix: surface app errors in gateway logs
This commit is contained in:
@@ -128,6 +128,27 @@ function timingSafeEqual(a: string, b: string): boolean {
|
||||
return diff === 0;
|
||||
}
|
||||
|
||||
export function internalError(res: Response): string | null {
|
||||
const encoded = res.headers.get("x-wrnexus-internal-error");
|
||||
if (!encoded) return null;
|
||||
try {
|
||||
return decodeURIComponent(encoded);
|
||||
} catch {
|
||||
return "Malformed internal error diagnostic";
|
||||
}
|
||||
}
|
||||
|
||||
export function stripInternalError(res: Response): Response {
|
||||
if (!res.headers.has("x-wrnexus-internal-error")) return res;
|
||||
const headers = new Headers(res.headers);
|
||||
headers.delete("x-wrnexus-internal-error");
|
||||
return new Response(res.body, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
/** Preserve an intentional verifier redirect while keeping other failures opaque. */
|
||||
export function forwardAuthFailure(res: Response, verifierUrl: string): Response {
|
||||
const location = res.headers.get("location");
|
||||
@@ -210,8 +231,9 @@ async function checkAuth(
|
||||
if (!res.ok) {
|
||||
if (res.status >= 500) {
|
||||
const requestUrl = new URL(req.url);
|
||||
const diagnostic = internalError(res);
|
||||
console.error(
|
||||
`[wrnexus] forward-auth verifier error: ${req.method} ${requestUrl.pathname} via ${verifyUrl} returned ${res.status}`,
|
||||
`[wrnexus] forward-auth verifier error: ${req.method} ${requestUrl.pathname} via ${verifyUrl} returned ${res.status}${diagnostic ? ` — ${diagnostic}` : ""}`,
|
||||
);
|
||||
}
|
||||
return forwardAuthFailure(res, verifyUrl);
|
||||
@@ -506,6 +528,13 @@ export async function startGateway(opts: GatewayOptions): Promise<RunningGateway
|
||||
);
|
||||
res = new Response(`Gateway: app '${target.name}' is unavailable.`, { status: 502 });
|
||||
}
|
||||
const diagnostic = internalError(res);
|
||||
if (res.status >= 500 && diagnostic) {
|
||||
console.error(
|
||||
`[wrnexus] app response error: ${target.name} ${req.method} ${url.pathname} → ${res.status} — ${diagnostic}`,
|
||||
);
|
||||
}
|
||||
res = stripInternalError(res);
|
||||
if (sec.headers) res = applyEdgeHeaders(res);
|
||||
if (sec.accessLog)
|
||||
console.log(
|
||||
|
||||
@@ -791,10 +791,16 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
|
||||
const app = process.env.WRNEXUS_APP_NAME ?? "app";
|
||||
const detail =
|
||||
err instanceof Error ? (err.stack ?? `${err.name}: ${err.message}`) : String(err);
|
||||
const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
||||
console.error(
|
||||
`[wrnexus] unhandled request error (${app}) ${req.method} ${url.pathname}\n${detail}`,
|
||||
);
|
||||
return compressResponse(req, secure(renderError(err, mode)));
|
||||
const response = secure(renderError(err, mode));
|
||||
// Gateway-managed production apps bind to loopback. Carry a bounded,
|
||||
// encoded diagnostic to the parent gateway so centralized log collectors
|
||||
// can explain child failures; the gateway always strips this header.
|
||||
response.headers.set("x-wrnexus-internal-error", encodeURIComponent(message.slice(0, 500)));
|
||||
return compressResponse(req, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user