diff --git a/packages/bootstrap/src/bootstrap.ts b/packages/bootstrap/src/bootstrap.ts index e37eb21cda91..a86e9632218b 100644 --- a/packages/bootstrap/src/bootstrap.ts +++ b/packages/bootstrap/src/bootstrap.ts @@ -7,7 +7,7 @@ import { IMidwayContainer, } from '@midwayjs/core'; import { join } from 'path'; -import { createConsoleLogger, ILogger, IMidwayLogger } from '@midwayjs/logger'; +import { createConsoleLogger, ILogger } from '@midwayjs/logger'; export function isTypeScriptEnvironment() { const TS_MODE_PROCESS_FLAG: string = process.env.MIDWAY_TS_MODE; @@ -193,7 +193,7 @@ export class Bootstrap { if (!this.logger && !configuration.logger) { this.logger = createConsoleLogger('bootstrapConsole'); if (configuration.logger === false) { - (this.logger as IMidwayLogger)?.disableConsole(); + this.logger?.['disableConsole'](); } configuration.logger = this.logger; } else { @@ -248,6 +248,12 @@ export class Bootstrap { process.once('exit', this.onExit.bind(this)); + this.uncaughtExceptionHandler = this.uncaughtExceptionHandler.bind(this); + process.on('uncaughtException', this.uncaughtExceptionHandler); + + this.unhandledRejectionHandler = this.unhandledRejectionHandler.bind(this); + process.on('unhandledRejection', this.unhandledRejectionHandler); + await this.getStarter().init(); return this.getStarter() .run() @@ -262,6 +268,11 @@ export class Bootstrap { static async stop() { await this.getStarter().stop(); + process.removeListener('uncaughtException', this.uncaughtExceptionHandler); + process.removeListener( + 'unhandledRejection', + this.unhandledRejectionHandler + ); this.reset(); } @@ -293,4 +304,31 @@ export class Bootstrap { static onExit(code) { this.logger.info('[midway:bootstrap] exit with code:%s', code); } + + static uncaughtExceptionHandler(err) { + if (!(err instanceof Error)) { + err = new Error(String(err)); + } + if (err.name === 'Error') { + err.name = 'unhandledExceptionError'; + } + this.logger.error(err); + } + + static unhandledRejectionHandler(err) { + if (!(err instanceof Error)) { + const newError = new Error(String(err)); + // err maybe an object, try to copy the name, message and stack to the new error instance + if (err) { + if (err.name) newError.name = err.name; + if (err.message) newError.message = err.message; + if (err.stack) newError.stack = err.stack; + } + err = newError; + } + if (err.name === 'Error') { + err.name = 'unhandledRejectionError'; + } + this.logger.error(err); + } } diff --git a/packages/bootstrap/test/index.test.ts b/packages/bootstrap/test/index.test.ts index 0ed71731e41f..1a62c17004bf 100644 --- a/packages/bootstrap/test/index.test.ts +++ b/packages/bootstrap/test/index.test.ts @@ -96,6 +96,16 @@ class TestFrameworkUnit implements IMidwayFramework { + setTimeout(() => { + a(); + }, 100); + } +} + describe('/test/index.test.ts', () => { beforeEach(() => { @@ -160,4 +170,15 @@ describe('/test/index.test.ts', () => { await Bootstrap.stop(); global['MIDWAY_BOOTSTRAP_APP_SET'] = null; }); + + it.skip('should catch promise error when start', async () => { + // can't trigger in jest + const framework = new PromiseErrorFramework().configure({ + port: 7001, + }); + const spy = jest.spyOn(process, 'on'); + await Bootstrap.load(framework).run(); + await sleep(2000); + expect(spy).toHaveBeenCalled(); + }); });