actionhero
Version:
actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks
159 lines (147 loc) • 4.23 kB
text/typescript
import * as os from "os";
import { api, log, Initializer } from "../index";
export interface ExceptionHandlerAPI {
reporters: Array<any>;
report: Function;
loader?: Function;
action?: Function;
task?: Function;
}
/**
* Handlers for when things go wrong.
*/
export class Exceptions extends Initializer {
constructor() {
super();
this.name = "exceptions";
this.loadPriority = 130;
}
relevantDetails() {
return ["action", "remoteIP", "type", "params", "room"];
}
async initialize(config) {
api.exceptionHandlers = {
reporters: [],
report: (error, type, name, objects, severity) => {
if (!severity) {
severity = "error";
}
for (const i in api.exceptionHandlers.reporters) {
api.exceptionHandlers.reporters[i](
error,
type,
name,
objects,
severity
);
}
},
};
const consoleReporter = (error, type, name, objects, severity) => {
const extraMessages = [];
if (type === "loader") {
extraMessages.push("! Failed to load " + objects.fullFilePath);
} else if (type === "action") {
extraMessages.push("! uncaught error from action: " + name);
extraMessages.push("! connection details:");
const relevantDetails = this.relevantDetails();
for (const i in relevantDetails) {
const relevantDetail = relevantDetails[i];
if (
objects.connection[relevantDetail] !== null &&
objects.connection[relevantDetail] !== undefined &&
typeof objects.connection[relevantDetail] !== "function"
) {
extraMessages.push(
"! " +
relevantDetail +
": " +
JSON.stringify(objects.connection[relevantDetail])
);
}
}
} else if (type === "task") {
extraMessages.push(
"! error from task: " +
name +
" on queue " +
objects.queue +
" (worker #" +
objects.workerId +
")"
);
try {
extraMessages.push(
"! arguments: " + JSON.stringify(objects.task.args)
);
} catch (e) {}
} else {
extraMessages.push("! Error: " + error.message);
extraMessages.push("! Type: " + type);
extraMessages.push("! Name: " + name);
extraMessages.push("! Data: " + JSON.stringify(objects));
}
for (const m in extraMessages) {
log(extraMessages[m], severity);
}
let lines;
try {
lines = error.stack.split(os.EOL);
} catch (e) {
lines = new Error(error).stack.split(os.EOL);
}
for (const l in lines) {
const line = lines[l];
log("! " + line, severity);
}
log("*", severity);
};
api.exceptionHandlers.reporters.push(consoleReporter);
api.exceptionHandlers.loader = (fullFilePath, error) => {
const name = "loader:" + fullFilePath;
api.exceptionHandlers.report(
error,
"loader",
name,
{ fullFilePath: fullFilePath },
"alert"
);
};
api.exceptionHandlers.action = (error, data, next) => {
let simpleName;
try {
simpleName = data.action;
} catch (e) {
simpleName = error.message;
}
const name = "action:" + simpleName;
api.exceptionHandlers.report(
error,
"action",
name,
{ connection: data.connection },
"error"
);
data.connection.response = {}; // no partial responses
if (typeof next === "function") {
next();
}
};
api.exceptionHandlers.task = (error, queue, task, workerId) => {
let simpleName;
try {
simpleName = task.class;
} catch (e) {
simpleName = error.message;
}
const name = "task:" + simpleName;
api.exceptionHandlers.report(
error,
"task",
name,
{ task: task, queue: queue, workerId: workerId },
config.tasks.workerLogging.failure
);
};
}
}