Handle eris errors in common error handler
This commit is contained in:
parent
3538d6c66a
commit
1a3d6d2fd9
2 changed files with 78 additions and 49 deletions
14
backend/src/ErisError.ts
Normal file
14
backend/src/ErisError.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export class ErisError extends Error {
|
||||||
|
code: number | string | undefined;
|
||||||
|
shardId: number;
|
||||||
|
|
||||||
|
constructor(message: string, code: number | string | undefined, shardId: number) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
this.shardId = shardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return `[ERIS] [CODE ${this.code || "?"}] [SHARD ${this.shardId}] ${this.message}`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import { LogType } from "./data/LogType";
|
||||||
import { ZeppelinPlugin } from "./plugins/ZeppelinPlugin";
|
import { ZeppelinPlugin } from "./plugins/ZeppelinPlugin";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { PluginLoadError } from "knub/dist/plugins/PluginLoadError";
|
import { PluginLoadError } from "knub/dist/plugins/PluginLoadError";
|
||||||
|
import { ErisError } from "./ErisError";
|
||||||
|
|
||||||
const fsp = fs.promises;
|
const fsp = fs.promises;
|
||||||
|
|
||||||
|
@ -51,61 +52,75 @@ const RECENT_DISCORD_ERROR_EXIT_THRESHOLD = 5;
|
||||||
setInterval(() => (recentPluginErrors = Math.max(0, recentPluginErrors - 1)), 2000);
|
setInterval(() => (recentPluginErrors = Math.max(0, recentPluginErrors - 1)), 2000);
|
||||||
setInterval(() => (recentDiscordErrors = Math.max(0, recentDiscordErrors - 1)), 2000);
|
setInterval(() => (recentDiscordErrors = Math.max(0, recentDiscordErrors - 1)), 2000);
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "production") {
|
// Eris handles these internally, so we don't need to panic if we get one of them
|
||||||
const errorHandler = err => {
|
const SAFE_TO_IGNORE_ERIS_ERROR_CODES = [
|
||||||
const guildName = err.guild?.name || "Global";
|
1001, // "CloudFlare WebSocket proxy restarting"
|
||||||
const guildId = err.guild?.id || "0";
|
1006, // "Connection reset by peer"
|
||||||
|
"ECONNRESET", // Pretty much the same as above
|
||||||
|
];
|
||||||
|
|
||||||
if (err instanceof RecoverablePluginError) {
|
function errorHandler(err) {
|
||||||
// Recoverable plugin errors can be, well, recovered from.
|
const guildName = err.guild?.name || "Global";
|
||||||
// Log it in the console as a warning and post a warning to the guild's log.
|
const guildId = err.guild?.id || "0";
|
||||||
|
|
||||||
// tslint:disable:no-console
|
if (err instanceof RecoverablePluginError) {
|
||||||
console.warn(`${guildName}: [${err.code}] ${err.message}`);
|
// Recoverable plugin errors can be, well, recovered from.
|
||||||
|
// Log it in the console as a warning and post a warning to the guild's log.
|
||||||
if (err.guild) {
|
|
||||||
const logs = new GuildLogs(err.guild.id);
|
|
||||||
logs.log(LogType.BOT_ALERT, { body: `\`[${err.code}]\` ${err.message}` });
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err instanceof PluginLoadError) {
|
|
||||||
// tslint:disable:no-console
|
|
||||||
console.warn(`${guildName} (${guildId}): Failed to load plugin '${err.pluginName}': ${err.message}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err instanceof DiscordHTTPError && err.code === 500) {
|
|
||||||
// Don't need stack traces on HTTP 500 errors
|
|
||||||
console.error(err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable:no-console
|
// tslint:disable:no-console
|
||||||
console.error(err);
|
console.warn(`${guildName}: [${err.code}] ${err.message}`);
|
||||||
|
|
||||||
if (err instanceof PluginError) {
|
if (err.guild) {
|
||||||
// Tolerate a few recent plugin errors before crashing
|
const logs = new GuildLogs(err.guild.id);
|
||||||
if (++recentPluginErrors >= RECENT_PLUGIN_ERROR_EXIT_THRESHOLD) {
|
logs.log(LogType.BOT_ALERT, { body: `\`[${err.code}]\` ${err.message}` });
|
||||||
console.error(`Exiting after ${RECENT_PLUGIN_ERROR_EXIT_THRESHOLD} plugin errors`);
|
}
|
||||||
process.exit(1);
|
|
||||||
}
|
return;
|
||||||
} else if (isDiscordRESTError(err) || isDiscordHTTPError(err)) {
|
}
|
||||||
// Discord API errors, usually safe to just log instead of crash
|
|
||||||
// We still bail if we get a ton of them in a short amount of time
|
if (err instanceof PluginLoadError) {
|
||||||
if (++recentDiscordErrors >= RECENT_DISCORD_ERROR_EXIT_THRESHOLD) {
|
// tslint:disable:no-console
|
||||||
console.error(`Exiting after ${RECENT_DISCORD_ERROR_EXIT_THRESHOLD} API errors`);
|
console.warn(`${guildName} (${guildId}): Failed to load plugin '${err.pluginName}': ${err.message}`);
|
||||||
process.exit(1);
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// On other errors, crash immediately
|
if (err instanceof ErisError) {
|
||||||
|
if (err.code && SAFE_TO_IGNORE_ERIS_ERROR_CODES.includes(err.code)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof DiscordHTTPError && err.code === 500) {
|
||||||
|
// Don't need stack traces on HTTP 500 errors
|
||||||
|
// These also shouldn't count towards RECENT_DISCORD_ERROR_EXIT_THRESHOLD because they don't indicate an error in our code
|
||||||
|
console.error(err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable:no-console
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
if (err instanceof PluginError) {
|
||||||
|
// Tolerate a few recent plugin errors before crashing
|
||||||
|
if (++recentPluginErrors >= RECENT_PLUGIN_ERROR_EXIT_THRESHOLD) {
|
||||||
|
console.error(`Exiting after ${RECENT_PLUGIN_ERROR_EXIT_THRESHOLD} plugin errors`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
// tslint:enable:no-console
|
} else if (isDiscordRESTError(err) || isDiscordHTTPError(err)) {
|
||||||
};
|
// Discord API errors, usually safe to just log instead of crash
|
||||||
|
// We still bail if we get a ton of them in a short amount of time
|
||||||
|
if (++recentDiscordErrors >= RECENT_DISCORD_ERROR_EXIT_THRESHOLD) {
|
||||||
|
console.error(`Exiting after ${RECENT_DISCORD_ERROR_EXIT_THRESHOLD} API errors`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// On other errors, crash immediately
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// tslint:enable:no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
process.on("uncaughtException", errorHandler);
|
process.on("uncaughtException", errorHandler);
|
||||||
process.on("unhandledRejection", errorHandler);
|
process.on("unhandledRejection", errorHandler);
|
||||||
}
|
}
|
||||||
|
@ -154,8 +169,8 @@ connect().then(async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on("error", err => {
|
client.on("error", (err, shardId) => {
|
||||||
logger.error(`[ERIS] ${String(err)}`);
|
errorHandler(new ErisError(err.message, (err as any).code, shardId));
|
||||||
});
|
});
|
||||||
|
|
||||||
const allowedGuilds = new AllowedGuilds();
|
const allowedGuilds = new AllowedGuilds();
|
||||||
|
|
Loading…
Add table
Reference in a new issue