diff --git a/lib/AppLoader.d.ts b/lib/AppLoader.d.ts index 6adca18..cab1c85 100644 --- a/lib/AppLoader.d.ts +++ b/lib/AppLoader.d.ts @@ -7,6 +7,7 @@ import { Namespace } from 'socket.io'; import DataSubscriber from './cluster/DataSubscriber'; import Synchronizer from './Synchronizer'; import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; +import { InternalErrorHandler } from './types'; export declare class AppLoader> extends GeneralAppLoader { protected dbStore: DbStore; private aspectDict; @@ -17,6 +18,16 @@ export declare class AppLoader): void; + /** + * 发布内部错误事件给注册的处理器 + */ + private publishInternalError; private requireSth; protected makeContext(cxtStr?: string, headers?: IncomingHttpHeaders): Promise; /** diff --git a/lib/AppLoader.js b/lib/AppLoader.js index c781cea..03c1161 100644 --- a/lib/AppLoader.js +++ b/lib/AppLoader.js @@ -28,6 +28,38 @@ class AppLoader extends types_1.AppLoader { nsSocket; watcherTimerId; scheduledJobs = {}; + internalErrorHandlers = new Array(); + /** + * 注册一个内部错误处理器 + * @param handler 内部错误处理器 + */ + registerInternalErrorHandler(handler) { + this.internalErrorHandlers.push(handler); + } + /** + * 发布内部错误事件给注册的处理器 + */ + async publishInternalError(type, message, err) { + const errorToPublish = (0, lodash_1.cloneDeep)(err); + let oakException; + if (errorToPublish instanceof types_1.OakException) { + oakException = errorToPublish; + } + await Promise.all(this.internalErrorHandlers.map((handler) => { + return new Promise(async (resolve) => { + try { + const ctx = await this.makeContext(); + handler(ctx, type, message, errorToPublish, oakException); + } + catch (e) { + console.error('执行internalErrorHandler时出错', e); + } + finally { + resolve(); + } + }); + })); + } requireSth(filePath) { return (0, requirePrj_1.default)(this.path, filePath, this.externalDependencies); } @@ -174,6 +206,7 @@ class AppLoader extends types_1.AppLoader { catch (err) { console.error(`执行aspect「${name}」出错`, err); await context.rollback(); + this.publishInternalError(`aspect`, `执行aspect「${name}」出错`, err); if (err instanceof types_1.OakException) { throw err; } @@ -358,6 +391,7 @@ class AppLoader extends types_1.AppLoader { else { await context.rollback(); } + // 不能在这里publish,因为这个方法可能是在timer中调用,也可能是在routine中调用 throw err; } } @@ -383,6 +417,7 @@ class AppLoader extends types_1.AppLoader { } catch (err) { console.error(`执行watcher【${watcher.name}】失败,耗时【${Date.now() - start}】,结果是:`, err); + await this.publishInternalError(`watcher`, `执行watcher【${watcher.name}】失败`, err); } }; const doWatchers = async () => { @@ -401,6 +436,7 @@ class AppLoader extends types_1.AppLoader { } catch (err) { console.error(`执行了checkpoint,发生错误:`, err); + await this.publishInternalError(`checkpoint`, `执行checkpoint发生错误`, err); } this.watcherTimerId = setTimeout(() => doWatchers(), 120000); }; @@ -425,6 +461,7 @@ class AppLoader extends types_1.AppLoader { } catch (err) { console.error(`定时器【${name}】执行失败,耗时${Date.now() - start}毫秒】,错误是`, err); + this.publishInternalError(`timer`, `定时器【${name}】执行失败`, err); } } else { @@ -444,6 +481,7 @@ class AppLoader extends types_1.AppLoader { else { await context.rollback(); } + this.publishInternalError(`timer`, `定时器【${name}】执行失败`, err); } } }); diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts new file mode 100644 index 0000000..7ab4e10 --- /dev/null +++ b/lib/types/index.d.ts @@ -0,0 +1,6 @@ +import { BaseEntityDict } from "oak-domain"; +import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; +import { EntityDict, OakException } from "oak-domain/lib/types"; +export type InternalErrorType = 'aspect' | 'trigger' | 'watcher' | 'timer' | 'checkpoint'; +export type InternalErrorHandler> = (ctx: Cxt, type: InternalErrorType, message: string, err: Error, oakException?: OakException) => Promise; +export type ExceptionPublisher = (type: string, message: string, err: any) => Promise; diff --git a/lib/types/index.js b/lib/types/index.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/types/index.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/src/AppLoader.ts b/src/AppLoader.ts index 61934f2..4402da8 100644 --- a/src/AppLoader.ts +++ b/src/AppLoader.ts @@ -22,6 +22,7 @@ import Synchronizer from './Synchronizer'; import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; import domainI18nData from 'oak-domain/lib/data/i18n'; import requireSth from './utils/requirePrj'; +import { InternalErrorHandler, InternalErrorType } from './types'; export class AppLoader> extends GeneralAppLoader { protected dbStore: DbStore; @@ -33,6 +34,40 @@ export class AppLoader = {}; + private internalErrorHandlers = new Array>(); + + /** + * 注册一个内部错误处理器 + * @param handler 内部错误处理器 + */ + public registerInternalErrorHandler(handler: InternalErrorHandler) { + this.internalErrorHandlers.push(handler); + } + + /** + * 发布内部错误事件给注册的处理器 + */ + private async publishInternalError(type: InternalErrorType, message: string, err: any) { + const errorToPublish = cloneDeep(err); + let oakException: OakException | undefined; + if (errorToPublish instanceof OakException) { + oakException = errorToPublish; + } + await Promise.all(this.internalErrorHandlers.map( + (handler) => { + return new Promise(async (resolve) => { + try { + const ctx = await this.makeContext(); + handler(ctx, type, message, errorToPublish, oakException); + } catch (e) { + console.error('执行internalErrorHandler时出错', e); + } finally { + resolve(); + } + }); + } + )); + } private requireSth(filePath: string): any { return requireSth(this.path, filePath, this.externalDependencies); @@ -76,7 +111,14 @@ export class AppLoader(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict); + this.dbStore = new DbStore( + storageSchema, + () => this.contextBuilder(this.dbStore), + dbConfig, + authDeduceRelationMap, + selectFreeEntities, + updateFreeDict, + ); if (nsSubscribe) { this.dataSubscriber = new DataSubscriber(nsSubscribe, nsServer); } @@ -223,6 +265,7 @@ export class AppLoader { @@ -476,6 +521,7 @@ export class AppLoader doWatchers(), 120000); @@ -504,6 +550,7 @@ export class AppLoader> = (ctx: Cxt, type: InternalErrorType, message:string, err: Error, oakException?: OakException, ) => Promise; +export type ExceptionPublisher = (type: string, message: string, err: any) => Promise; \ No newline at end of file