@hoangnam.io/qa-tools
Version:
Logging, Error handling, Notifying for Express codebase
90 lines (81 loc) • 3.12 kB
JavaScript
function getErrorHandlerMiddleware(notifier, extractCallerFunc = (req) => req.caller, appName) {
process.on("unhandledRejection", async (error) => {
try {
console.error(error);
if (!isIgnoredError({}, error)) {
await notifier.send(`appName: ${appName}, unhandledRejection: ${error}`);
}
} finally {
process.exit(-1);
}
});
process.on("uncaughtException", async (error) => {
try {
console.error(error);
if (!isIgnoredError({}, error)) {
await notifier.send(`appName: ${appName}, uncaughtException: ${error}`);
}
} finally {
process.exit(-1);
}
});
function isIgnoredError(req, err) {
try {
if (!req || !err) return false;
if (err.toString()?.includes("BSONError")) return true;
if (err.toString()?.includes("LoggingServiceV2")) return true;
if (err.name === "MongoServerError" && err.message.includes("E11000")) return true;
if (err.name === "ZodError") return true;
if (err.name === "SyntaxError") return true;
if (req.extendedLog?.clientDisconnected) return true;
return false;
} catch (error) {
return false;
}
}
// error: name, message, [details]
// send notify error: context, error
return function errorHandlerMiddleware(err, req, res, next) {
// context
const context = {
appName,
method: req.method,
url: req.originalUrl,
caller: extractCallerFunc(req),
extendedLog: req.extendedLog,
};
// axios error
if (err.response) {
const { status, statusText } = err.response;
const { baseURL, method, url, data } = err.response.config;
let response = err.response.data;
if (response instanceof Buffer) response = response.toString();
const error = {
name: "axios error",
message: `failed to call api: ${method} ${baseURL}${url}`,
details: { status, response },
};
if (!isIgnoredError(req, err)) notifier.send(JSON.stringify({ context, error }, null, 2));
return res.status(status).json(error);
}
// mongodb error
if (err.name === "MongoServerError") {
const error = { name: err.name, message: err.message };
if (!isIgnoredError(req, err)) notifier.send(JSON.stringify({ context, error }, null, 2));
return res.status(err.statusCode || 500).json(error);
}
// zod
if (err.name === "ZodError") {
const issues = err.issues.map((item) => ({ field: item.path.join("."), message: item.message }));
const message = JSON.stringify(issues);
const error = { name: "Invalid input data", message };
if (!isIgnoredError(req, err)) notifier.send(JSON.stringify({ context, error }, null, 2));
return res.status(err.statusCode || 500).json(error);
}
// BaseError or other Error
const error = { ...err, name: err.name, message: err.message };
if (!isIgnoredError(req, err)) notifier.send(JSON.stringify({ context, error }, null, 2));
return res.status(err.statusCode || 500).json(error);
};
}
module.exports = { getErrorHandlerMiddleware };