feat: 在AppLoader中新增异常处理的注册,以支持异常信息上报
This commit is contained in:
parent
4c1e55982c
commit
d19cdec336
|
|
@ -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<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends GeneralAppLoader<ED, Cxt> {
|
||||
protected dbStore: DbStore<ED, Cxt>;
|
||||
private aspectDict;
|
||||
|
|
@ -17,6 +18,16 @@ export declare class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt exten
|
|||
private nsSocket?;
|
||||
private watcherTimerId?;
|
||||
private scheduledJobs;
|
||||
private internalErrorHandlers;
|
||||
/**
|
||||
* 注册一个内部错误处理器
|
||||
* @param handler 内部错误处理器
|
||||
*/
|
||||
registerInternalErrorHandler(handler: InternalErrorHandler<ED, Cxt>): void;
|
||||
/**
|
||||
* 发布内部错误事件给注册的处理器
|
||||
*/
|
||||
private publishInternalError;
|
||||
private requireSth;
|
||||
protected makeContext(cxtStr?: string, headers?: IncomingHttpHeaders): Promise<Cxt>;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> = (ctx: Cxt, type: InternalErrorType, message: string, err: Error, oakException?: OakException<ED>) => Promise<void>;
|
||||
export type ExceptionPublisher = (type: string, message: string, err: any) => Promise<void>;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -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<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends GeneralAppLoader<ED, Cxt> {
|
||||
protected dbStore: DbStore<ED, Cxt>;
|
||||
|
|
@ -33,6 +34,40 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
private nsSocket?: Namespace;
|
||||
private watcherTimerId?: NodeJS.Timeout;
|
||||
private scheduledJobs: Record<string, Job> = {};
|
||||
private internalErrorHandlers = new Array<InternalErrorHandler<ED, Cxt>>();
|
||||
|
||||
/**
|
||||
* 注册一个内部错误处理器
|
||||
* @param handler 内部错误处理器
|
||||
*/
|
||||
public registerInternalErrorHandler(handler: InternalErrorHandler<ED, Cxt>) {
|
||||
this.internalErrorHandlers.push(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布内部错误事件给注册的处理器
|
||||
*/
|
||||
private async publishInternalError(type: InternalErrorType, message: string, err: any) {
|
||||
const errorToPublish = cloneDeep(err);
|
||||
let oakException: OakException<ED> | undefined;
|
||||
if (errorToPublish instanceof OakException) {
|
||||
oakException = errorToPublish;
|
||||
}
|
||||
await Promise.all(this.internalErrorHandlers.map(
|
||||
(handler) => {
|
||||
return new Promise<void>(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<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
this.externalDependencies = depGraph.ascOrder;
|
||||
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = this.requireSth('lib/configuration/relation')!;
|
||||
this.aspectDict = Object.assign({}, generalAspectDict, this.requireSth('lib/aspects/index')!);
|
||||
this.dbStore = new DbStore<ED, Cxt>(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
||||
this.dbStore = new DbStore<ED, Cxt>(
|
||||
storageSchema,
|
||||
() => this.contextBuilder(this.dbStore),
|
||||
dbConfig,
|
||||
authDeduceRelationMap,
|
||||
selectFreeEntities,
|
||||
updateFreeDict,
|
||||
);
|
||||
if (nsSubscribe) {
|
||||
this.dataSubscriber = new DataSubscriber(nsSubscribe, nsServer);
|
||||
}
|
||||
|
|
@ -223,6 +265,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
catch (err) {
|
||||
console.error(`执行aspect「${name}」出错`, err);
|
||||
await context.rollback();
|
||||
this.publishInternalError(`aspect`, `执行aspect「${name}」出错`, err)
|
||||
if (err instanceof OakException) {
|
||||
throw err;
|
||||
}
|
||||
|
|
@ -427,6 +470,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
else {
|
||||
await context.rollback();
|
||||
}
|
||||
// 不能在这里publish,因为这个方法可能是在timer中调用,也可能是在routine中调用
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
@ -457,6 +501,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
}
|
||||
catch (err) {
|
||||
console.error(`执行watcher【${watcher.name}】失败,耗时【${Date.now() - start}】,结果是:`, err);
|
||||
await this.publishInternalError(`watcher`, `执行watcher【${watcher.name}】失败`, err);
|
||||
}
|
||||
};
|
||||
const doWatchers = async () => {
|
||||
|
|
@ -476,6 +521,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
}
|
||||
catch (err) {
|
||||
console.error(`执行了checkpoint,发生错误:`, err);
|
||||
await this.publishInternalError(`checkpoint`, `执行checkpoint发生错误`, err);
|
||||
}
|
||||
|
||||
this.watcherTimerId = setTimeout(() => doWatchers(), 120000);
|
||||
|
|
@ -504,6 +550,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
}
|
||||
catch (err) {
|
||||
console.error(`定时器【${name}】执行失败,耗时${Date.now() - start}毫秒】,错误是`, err);
|
||||
this.publishInternalError(`timer`, `定时器【${name}】执行失败`, err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -523,6 +570,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
|||
else {
|
||||
await context.rollback();
|
||||
}
|
||||
this.publishInternalError(`timer`, `定时器【${name}】执行失败`, err);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
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<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> = (ctx: Cxt, type: InternalErrorType, message:string, err: Error, oakException?: OakException<ED>, ) => Promise<void>;
|
||||
export type ExceptionPublisher = (type: string, message: string, err: any) => Promise<void>;
|
||||
Loading…
Reference in New Issue