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 type { Request, Response, NextFunction } from "express";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
import { logger } from "./logger.js";
|
|
||||||
import { HttpError } from "../errors.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(
|
export function errorHandler(
|
||||||
err: unknown,
|
err: unknown,
|
||||||
req: Request,
|
req: Request,
|
||||||
@@ -11,22 +19,14 @@ export function errorHandler(
|
|||||||
) {
|
) {
|
||||||
if (err instanceof HttpError) {
|
if (err instanceof HttpError) {
|
||||||
if (err.status >= 500) {
|
if (err.status >= 500) {
|
||||||
(res as any).err = err;
|
(res as any).__errorContext = {
|
||||||
logger.error(
|
error: { message: err.message, stack: err.stack, name: err.name, details: err.details },
|
||||||
{
|
method: req.method,
|
||||||
err: { message: err.message, stack: err.stack, name: err.name, details: err.details },
|
url: req.originalUrl,
|
||||||
method: req.method,
|
reqBody: req.body,
|
||||||
url: req.originalUrl,
|
reqParams: req.params,
|
||||||
reqBody: req.body,
|
reqQuery: req.query,
|
||||||
reqParams: req.params,
|
} satisfies ErrorContext;
|
||||||
reqQuery: req.query,
|
|
||||||
},
|
|
||||||
"HttpError %d: %s %s — %s",
|
|
||||||
err.status,
|
|
||||||
req.method,
|
|
||||||
req.originalUrl,
|
|
||||||
err.message,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
res.status(err.status).json({
|
res.status(err.status).json({
|
||||||
error: err.message,
|
error: err.message,
|
||||||
@@ -40,28 +40,16 @@ export function errorHandler(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const errObj = err instanceof Error
|
(res as any).__errorContext = {
|
||||||
? { message: err.message, stack: err.stack, name: err.name }
|
error: err instanceof Error
|
||||||
: { raw: err };
|
? { 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" });
|
res.status(500).json({ error: "Internal server error" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,11 +53,21 @@ export const httpLogger = pinoHttp({
|
|||||||
return `${req.method} ${req.url} ${res.statusCode}`;
|
return `${req.method} ${req.url} ${res.statusCode}`;
|
||||||
},
|
},
|
||||||
customErrorMessage(req, res, err) {
|
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}`;
|
return `${req.method} ${req.url} ${res.statusCode} — ${errMsg}`;
|
||||||
},
|
},
|
||||||
customProps(req, res) {
|
customProps(req, res) {
|
||||||
if (res.statusCode >= 400) {
|
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 props: Record<string, unknown> = {};
|
||||||
const { body, params, query } = req as any;
|
const { body, params, query } = req as any;
|
||||||
if (body && typeof body === "object" && Object.keys(body).length > 0) {
|
if (body && typeof body === "object" && Object.keys(body).length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user