polish error handling

This commit is contained in:
Maidul Islam
2023-10-12 15:06:37 +01:00
parent f119c921d0
commit b1ec59eb67
+21 -19
View File
@@ -3,8 +3,8 @@ import { ErrorRequestHandler } from "express";
import { TokenExpiredError } from "jsonwebtoken"; import { TokenExpiredError } from "jsonwebtoken";
import { InternalServerError, UnauthorizedRequestError } from "../utils/errors"; import { InternalServerError, UnauthorizedRequestError } from "../utils/errors";
import { getLogger } from "../utils/logger"; import { getLogger } from "../utils/logger";
import RequestError, { LogLevel, mapToWinstonLogLevel } from "../utils/requestError"; import RequestError from "../utils/requestError";
import { getNodeEnv } from "../config"; import { ForbiddenError } from "@casl/ability";
export const requestErrorHandler: ErrorRequestHandler = async ( export const requestErrorHandler: ErrorRequestHandler = async (
error: RequestError | Error, error: RequestError | Error,
@@ -14,35 +14,37 @@ export const requestErrorHandler: ErrorRequestHandler = async (
) => { ) => {
if (res.headersSent) return next(); if (res.headersSent) return next();
const logAndCaptureException = async (error: RequestError, logLevel: LogLevel) => { const logAndCaptureException = async (error: RequestError) => {
// log stack trace & error message to the console using Winston (await getLogger("backend-main")).log(
(await getLogger("backend-main")).log(mapToWinstonLogLevel(logLevel), `${error.stack}\n${error.message}`); (<RequestError>error).levelName.toLowerCase(),
`${error.stack}\n${error.message}`
);
//* Set Sentry user identification if req.user is populated //* Set Sentry user identification if req.user is populated
if (req.user !== undefined && req.user !== null) { if (req.user !== undefined && req.user !== null) {
Sentry.setUser({ email: (req.user as any).email }); Sentry.setUser({ email: (req.user as any).email });
} }
// eliminate false-positive errors being sent to Sentry Sentry.captureException(error);
if ("level" in error && [LogLevel.ERROR, LogLevel.EMERGENCY, LogLevel.CRITICAL].includes(error.level)) {
Sentry.captureException(error);
}
}; };
if (error instanceof RequestError || error instanceof TokenExpiredError) { if (error instanceof RequestError) {
if (error instanceof TokenExpiredError) { if (error instanceof TokenExpiredError) {
error = UnauthorizedRequestError({ stack: error.stack, message: "Token expired" }) as RequestError; error = UnauthorizedRequestError({ stack: error.stack, message: "Token expired" });
}
await logAndCaptureException((<RequestError>error));
} else {
if (error instanceof ForbiddenError) {
error = UnauthorizedRequestError({ context: { exception: error.message }, stack: error.stack })
} else {
error = InternalServerError({ context: { exception: error.message }, stack: error.stack });
} }
logAndCaptureException((<RequestError>error), LogLevel.INFO); await logAndCaptureException((<RequestError>error));
} else {
// For unexpected errors, throw a 500 error & ensure these are sent to Sentry in prod
error = InternalServerError({ context: { exception: error.message }, stack: error.stack }) as RequestError;
logAndCaptureException((<RequestError>error), (await getNodeEnv() === "production") ? LogLevel.ERROR : LogLevel.DEBUG);
} }
res delete (<any>error).stacktrace // remove stack trace from being sent to client
.status((<RequestError>error).statusCode) res.status((<RequestError>error).statusCode).json(error);
.json((<RequestError>error).format(req));
next(); next();
}; };