mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 12:26:13 +00:00
Fix: add stack trace errors in logging for prod
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
],
|
],
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"unused-imports/no-unused-imports": "error",
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"@typescript-eslint/no-extra-semi": "off", // added to be able to push
|
||||||
"unused-imports/no-unused-vars": [
|
"unused-imports/no-unused-vars": [
|
||||||
"warn",
|
"warn",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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 } from "../utils/requestError";
|
import RequestError, { LogLevel, mapToWinstonLogLevel } from "../utils/requestError";
|
||||||
import { getNodeEnv } from "../config";
|
import { getNodeEnv } from "../config";
|
||||||
|
|
||||||
export const requestErrorHandler: ErrorRequestHandler = async (
|
export const requestErrorHandler: ErrorRequestHandler = async (
|
||||||
@@ -14,37 +14,31 @@ export const requestErrorHandler: ErrorRequestHandler = async (
|
|||||||
) => {
|
) => {
|
||||||
if (res.headersSent) return next();
|
if (res.headersSent) return next();
|
||||||
|
|
||||||
if (await getNodeEnv() !== "production") {
|
const logAndCaptureException = async (error: RequestError, logLevel: LogLevel) => {
|
||||||
/* eslint-disable no-console */
|
// log stack trace & error message to the console using Winston
|
||||||
console.error(error);
|
(await getLogger("backend-main")).log(mapToWinstonLogLevel(logLevel), `${error.stack}\n${error.message}`);
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
//* Set Sentry user identification if req.user is populated
|
||||||
if (error instanceof TokenExpiredError) {
|
if (req.user !== undefined && req.user !== null) {
|
||||||
error = UnauthorizedRequestError({ stack: error.stack, message: "Token expired" });
|
Sentry.setUser({ email: (req.user as any).email });
|
||||||
} else if (!(error instanceof RequestError)) {
|
}
|
||||||
error = InternalServerError({
|
|
||||||
context: { exception: error.message },
|
|
||||||
stack: error.stack,
|
|
||||||
});
|
|
||||||
(await getLogger("backend-main")).log(
|
|
||||||
(<RequestError>error).levelName.toLowerCase(),
|
|
||||||
(<RequestError>error).message
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Set Sentry user identification if req.user is populated
|
// eliminate false-positive errors being sent to Sentry
|
||||||
if (req.user !== undefined && req.user !== null) {
|
if ("level" in error && [LogLevel.ERROR, LogLevel.EMERGENCY, LogLevel.CRITICAL].includes(error.level)) {
|
||||||
Sentry.setUser({ email: (req.user as any).email });
|
Sentry.captureException(error);
|
||||||
}
|
}
|
||||||
//* Only sent error to Sentry if LogLevel is one of the following level 'ERROR', 'EMERGENCY' or 'CRITICAL'
|
};
|
||||||
//* with this we will eliminate false-positive errors like 'BadRequestError', 'UnauthorizedRequestError' and so on
|
|
||||||
if (
|
if (error instanceof RequestError || error instanceof TokenExpiredError) {
|
||||||
[LogLevel.ERROR, LogLevel.EMERGENCY, LogLevel.CRITICAL].includes(
|
if (error instanceof TokenExpiredError) {
|
||||||
(<RequestError>error).level
|
error = UnauthorizedRequestError({ stack: error.stack, message: "Token expired" }) as RequestError;
|
||||||
)
|
}
|
||||||
) {
|
|
||||||
Sentry.captureException(error);
|
logAndCaptureException((<RequestError>error), LogLevel.INFO);
|
||||||
|
} 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
|
res
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-extra-semi */
|
||||||
import { Request } from "express"
|
import { Request } from "express"
|
||||||
import { getVerboseErrorOutput } from "../config";
|
import { getVerboseErrorOutput } from "../config";
|
||||||
|
|
||||||
@@ -12,6 +13,27 @@ export enum LogLevel {
|
|||||||
EMERGENCY = 600,
|
EMERGENCY = 600,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mapToWinstonLogLevel = (customLogLevel: LogLevel): string => {
|
||||||
|
switch (customLogLevel) {
|
||||||
|
case LogLevel.DEBUG:
|
||||||
|
return "debug";
|
||||||
|
case LogLevel.INFO:
|
||||||
|
return "info";
|
||||||
|
case LogLevel.NOTICE:
|
||||||
|
return "notice";
|
||||||
|
case LogLevel.WARNING:
|
||||||
|
return "warn";
|
||||||
|
case LogLevel.ERROR:
|
||||||
|
return "error";
|
||||||
|
case LogLevel.CRITICAL:
|
||||||
|
return "crit";
|
||||||
|
case LogLevel.ALERT:
|
||||||
|
return "alert";
|
||||||
|
case LogLevel.EMERGENCY:
|
||||||
|
return "emerg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type RequestErrorContext = {
|
export type RequestErrorContext = {
|
||||||
logLevel?: LogLevel,
|
logLevel?: LogLevel,
|
||||||
statusCode: number,
|
statusCode: number,
|
||||||
@@ -87,7 +109,8 @@ export default class RequestError extends Error{
|
|||||||
}, this.context)
|
}, this.context)
|
||||||
|
|
||||||
//* Omit sensitive information from context that can leak internal workings of this program if user is not developer
|
//* Omit sensitive information from context that can leak internal workings of this program if user is not developer
|
||||||
if(!(await getVerboseErrorOutput())){
|
const verboseErrorOutput = await getVerboseErrorOutput();
|
||||||
|
if (verboseErrorOutput !== undefined) {
|
||||||
_context = this._omit(_context, [
|
_context = this._omit(_context, [
|
||||||
"stacktrace",
|
"stacktrace",
|
||||||
"exception",
|
"exception",
|
||||||
@@ -110,4 +133,4 @@ export default class RequestError extends Error{
|
|||||||
return formatObject
|
return formatObject
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user