Fix 500 error logs still showing generic pino-http message
The previous fix (8151331) set res.err but pino-http wasn't picking it
up (likely Express 5 response object behavior). Switch to a custom
__errorContext property on the response that customErrorMessage and
customProps read directly, bypassing pino-http's unreliable res.err
check. Remove duplicate manual logger.error calls from the error
handler since pino-http now gets the full context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
import type { Request, Response, NextFunction } from "express";
|
||||
import { ZodError } from "zod";
|
||||
import { logger } from "./logger.js";
|
||||
import { HttpError } from "../errors.js";
|
||||
|
||||
export interface ErrorContext {
|
||||
error: { message: string; stack?: string; name?: string; details?: unknown; raw?: unknown };
|
||||
method: string;
|
||||
url: string;
|
||||
reqBody?: unknown;
|
||||
reqParams?: unknown;
|
||||
reqQuery?: unknown;
|
||||
}
|
||||
|
||||
export function errorHandler(
|
||||
err: unknown,
|
||||
req: Request,
|
||||
@@ -11,22 +19,14 @@ export function errorHandler(
|
||||
) {
|
||||
if (err instanceof HttpError) {
|
||||
if (err.status >= 500) {
|
||||
(res as any).err = err;
|
||||
logger.error(
|
||||
{
|
||||
err: { message: err.message, stack: err.stack, name: err.name, details: err.details },
|
||||
method: req.method,
|
||||
url: req.originalUrl,
|
||||
reqBody: req.body,
|
||||
reqParams: req.params,
|
||||
reqQuery: req.query,
|
||||
},
|
||||
"HttpError %d: %s %s — %s",
|
||||
err.status,
|
||||
req.method,
|
||||
req.originalUrl,
|
||||
err.message,
|
||||
);
|
||||
(res as any).__errorContext = {
|
||||
error: { message: err.message, stack: err.stack, name: err.name, details: err.details },
|
||||
method: req.method,
|
||||
url: req.originalUrl,
|
||||
reqBody: req.body,
|
||||
reqParams: req.params,
|
||||
reqQuery: req.query,
|
||||
} satisfies ErrorContext;
|
||||
}
|
||||
res.status(err.status).json({
|
||||
error: err.message,
|
||||
@@ -40,28 +40,16 @@ export function errorHandler(
|
||||
return;
|
||||
}
|
||||
|
||||
const errObj = err instanceof Error
|
||||
? { message: err.message, stack: err.stack, name: err.name }
|
||||
: { raw: err };
|
||||
(res as any).__errorContext = {
|
||||
error: err instanceof Error
|
||||
? { message: err.message, stack: err.stack, name: err.name }
|
||||
: { message: String(err), raw: err },
|
||||
method: req.method,
|
||||
url: req.originalUrl,
|
||||
reqBody: req.body,
|
||||
reqParams: req.params,
|
||||
reqQuery: req.query,
|
||||
} satisfies ErrorContext;
|
||||
|
||||
// Attach the real error so pino-http uses it instead of its generic
|
||||
// "failed with status code 500" message in the response-complete log
|
||||
const realError = err instanceof Error ? err : Object.assign(new Error(String(err)), { raw: err });
|
||||
(res as any).err = realError;
|
||||
|
||||
logger.error(
|
||||
{
|
||||
err: errObj,
|
||||
method: req.method,
|
||||
url: req.originalUrl,
|
||||
reqBody: req.body,
|
||||
reqParams: req.params,
|
||||
reqQuery: req.query,
|
||||
},
|
||||
"Unhandled error: %s %s — %s",
|
||||
req.method,
|
||||
req.originalUrl,
|
||||
err instanceof Error ? err.message : String(err),
|
||||
);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
|
||||
@@ -53,11 +53,21 @@ export const httpLogger = pinoHttp({
|
||||
return `${req.method} ${req.url} ${res.statusCode}`;
|
||||
},
|
||||
customErrorMessage(req, res, err) {
|
||||
const errMsg = err?.message || (res as any).err?.message || "unknown error";
|
||||
const ctx = (res as any).__errorContext;
|
||||
const errMsg = ctx?.error?.message || err?.message || (res as any).err?.message || "unknown error";
|
||||
return `${req.method} ${req.url} ${res.statusCode} — ${errMsg}`;
|
||||
},
|
||||
customProps(req, res) {
|
||||
if (res.statusCode >= 400) {
|
||||
const ctx = (res as any).__errorContext;
|
||||
if (ctx) {
|
||||
return {
|
||||
err: ctx.error,
|
||||
reqBody: ctx.reqBody,
|
||||
reqParams: ctx.reqParams,
|
||||
reqQuery: ctx.reqQuery,
|
||||
};
|
||||
}
|
||||
const props: Record<string, unknown> = {};
|
||||
const { body, params, query } = req as any;
|
||||
if (body && typeof body === "object" && Object.keys(body).length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user