Update to Knub 28, improve debugger-friendliness

Development npm scripts now also listen for debuggers:
- Port 9229 for the bot
- Port 9239 for the api

Via Knub 28, PluginErrors are no longer used in development, which
helps with call stacks in debuggers (see Knub changelog).

Unhandled promise rejections are now treated as exceptions via nodejs
flag --unhandled-rejections=strict, which allows catching them with
a debugger.

The internal "error-tolerant" error handler is now only used in
production; in development, all unhandled errors cause the bot to
crash and are easily catchable by debuggers.
This commit is contained in:
Dragory 2020-01-12 10:28:38 +02:00
parent 7de4be0b44
commit 59a927ba93
3 changed files with 31 additions and 30 deletions

View file

@ -2776,9 +2776,9 @@
}
},
"knub": {
"version": "27.0.0",
"resolved": "https://registry.npmjs.org/knub/-/knub-27.0.0.tgz",
"integrity": "sha512-iA332KoD2LN4R6c24HFGxQc8QH2GwG5Az97Rie5eqT5KFiWV/f5UJ8EfWhIJ/bOmMHubtJ4j+tEdQIBidC+WxA==",
"version": "28.0.0",
"resolved": "https://registry.npmjs.org/knub/-/knub-28.0.0.tgz",
"integrity": "sha512-dhLjlQP32AbkXoD0tUFrqa8/Emh/U5lvYXNCz+bIWiAlzYr1cQKRuh9qNjAL944sCWmtfhKXVMXRFS99P6M7qw==",
"requires": {
"escape-string-regexp": "^2.0.0",
"knub-command-manager": "^7.0.0",

View file

@ -6,10 +6,10 @@
"scripts": {
"watch": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node start-dev.js\"",
"build": "rimraf dist && tsc",
"start-bot-dev": "cross-env NODE_ENV=development node -r source-map-support/register -r ./register-tsconfig-paths.js dist/backend/src/index.js",
"start-bot-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js dist/backend/src/index.js",
"start-api-dev": "cross-env NODE_ENV=development node -r source-map-support/register -r ./register-tsconfig-paths.js dist/backend/src/api/index.js",
"start-api-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js dist/backend/src/api/index.js",
"start-bot-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --inspect=127.0.0.1:9229 dist/backend/src/index.js",
"start-bot-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js --unhandled-rejections=strict dist/backend/src/index.js",
"start-api-dev": "cross-env NODE_ENV=development node -r ./register-tsconfig-paths.js --unhandled-rejections=strict --inspect=127.0.0.1:9239 dist/backend/src/api/index.js",
"start-api-prod": "cross-env NODE_ENV=production node -r ./register-tsconfig-paths.js --unhandled-rejections=strict dist/backend/src/api/index.js",
"typeorm": "node -r ./register-tsconfig-paths.js ./node_modules/typeorm/cli.js",
"migrate-prod": "npm run typeorm -- migration:run",
"migrate-dev": "npm run build && npm run typeorm -- migration:run",
@ -32,7 +32,7 @@
"humanize-duration": "^3.15.0",
"io-ts": "^2.0.0",
"js-yaml": "^3.13.1",
"knub": "^27.0.0",
"knub": "^28.0.0",
"knub-command-manager": "^7.0.0",
"last-commit-log": "^2.1.0",
"lodash.chunk": "^4.2.0",

View file

@ -24,33 +24,34 @@ const RECENT_DISCORD_ERROR_EXIT_THRESHOLD = 5;
setInterval(() => (recentPluginErrors = Math.max(0, recentPluginErrors - 1)), 2500);
setInterval(() => (recentDiscordErrors = Math.max(0, recentDiscordErrors - 1)), 2500);
function errorHandler(err) {
// tslint:disable:no-console
console.error(err);
if (process.env.NODE_ENV === "production") {
const errorHandler = err => {
// 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`);
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);
}
} else if (err instanceof DiscordRESTError || err instanceof DiscordHTTPError) {
// 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);
}
} else if (err instanceof DiscordRESTError || err instanceof DiscordHTTPError) {
// 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
// tslint:enable:no-console
};
process.on("uncaughtException", errorHandler);
}
process.on("unhandledRejection", errorHandler);
process.on("uncaughtException", errorHandler);
// Verify required Node.js version
const REQUIRED_NODE_VERSION = "10.14.2";
const requiredParts = REQUIRED_NODE_VERSION.split(".").map(v => parseInt(v, 10));