From 00b5fe2cd13a3dea6f19dcaac940e5b4cd0fd28c Mon Sep 17 00:00:00 2001 From: marknguyen1302 Date: Wed, 19 Jun 2024 14:30:13 +0700 Subject: [PATCH] fix: prevent error loop, create crash report file if not exist --- cortex-js/src/command.ts | 9 ++++- .../middlewares/app.logger.middleware.ts | 2 + .../telemetry/telemetry.repository.ts | 3 ++ .../file-manager/file-manager.service.ts | 1 + .../usecases/telemetry/telemetry.usecases.ts | 39 +++++++++++++------ 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/cortex-js/src/command.ts b/cortex-js/src/command.ts index 2b79789c3..402ac5147 100644 --- a/cortex-js/src/command.ts +++ b/cortex-js/src/command.ts @@ -24,10 +24,17 @@ async function bootstrap() { process.exit(1); }, }); + telemetryUseCase = await app.resolve(TelemetryUsecases); contextService = await app.resolve(ContextService); + telemetryUseCase!.sendCrashReport(); - await contextService!.init(async () => CommandFactory.runApplication(app)); + + await contextService!.init(async () => { + contextService!.set('source', TelemetrySource.CLI); + return CommandFactory.runApplication(app); + }); + const notifier = updateNotifier({ pkg: packageJson, updateCheckInterval: 1000 * 60 * 60, // 1 hour diff --git a/cortex-js/src/infrastructure/middlewares/app.logger.middleware.ts b/cortex-js/src/infrastructure/middlewares/app.logger.middleware.ts index 09eeb62b2..bb8f9778e 100644 --- a/cortex-js/src/infrastructure/middlewares/app.logger.middleware.ts +++ b/cortex-js/src/infrastructure/middlewares/app.logger.middleware.ts @@ -1,3 +1,4 @@ +import { TelemetrySource } from '@/domain/telemetry/telemetry.interface'; import { ContextService } from '@/util/context.service'; import { Injectable, NestMiddleware, Logger } from '@nestjs/common'; @@ -32,6 +33,7 @@ export class AppLoggerMiddleware implements NestMiddleware { }); this.contextService.init(() => { this.contextService.set('endpoint', originalUrl ?? url); + this.contextService.set('source', TelemetrySource.CORTEX_SERVER); next(); }); } diff --git a/cortex-js/src/infrastructure/repositories/telemetry/telemetry.repository.ts b/cortex-js/src/infrastructure/repositories/telemetry/telemetry.repository.ts index 0f6c133f3..fe5be3be5 100644 --- a/cortex-js/src/infrastructure/repositories/telemetry/telemetry.repository.ts +++ b/cortex-js/src/infrastructure/repositories/telemetry/telemetry.repository.ts @@ -33,6 +33,9 @@ export class TelemetryRepositoryImpl implements TelemetryRepository { private async getTelemetryDirectory(): Promise { const dataFolderPath = await this.fileManagerService.getDataFolderPath(); + await this.fileManagerService.createFolderIfNotExistInDataFolder( + 'telemetry', + ); return join(dataFolderPath, 'telemetry'); } diff --git a/cortex-js/src/infrastructure/services/file-manager/file-manager.service.ts b/cortex-js/src/infrastructure/services/file-manager/file-manager.service.ts index 219f48a45..09faef48e 100644 --- a/cortex-js/src/infrastructure/services/file-manager/file-manager.service.ts +++ b/cortex-js/src/infrastructure/services/file-manager/file-manager.service.ts @@ -175,6 +175,7 @@ export class FileManagerService { stats.size === 0 ? data : `\n${data}`, { encoding: 'utf8', + flag: 'a+', }, ); } catch (err) { diff --git a/cortex-js/src/usecases/telemetry/telemetry.usecases.ts b/cortex-js/src/usecases/telemetry/telemetry.usecases.ts index f663dd970..e31a766fe 100644 --- a/cortex-js/src/usecases/telemetry/telemetry.usecases.ts +++ b/cortex-js/src/usecases/telemetry/telemetry.usecases.ts @@ -9,18 +9,15 @@ import { HttpException, Inject, Injectable, Scope } from '@nestjs/common'; @Injectable({ scope: Scope.TRANSIENT }) export class TelemetryUsecases { + private readonly crashReports: string[] = []; + private readonly maxSize = 100; + constructor( @Inject('TELEMETRY_REPOSITORY') private readonly telemetryRepository: TelemetryRepository, private readonly contextService: ContextService, ) { - process.on('uncaughtException', async (error: Error) => { - telemetryRepository.createCrashReport(this.buildCrashReport(error)); - }); - - process.on('unhandledRejection', async (error: Error) => { - telemetryRepository.createCrashReport(this.buildCrashReport(error)); - }); + this.catchException(); } async createCrashReport( @@ -28,10 +25,14 @@ export class TelemetryUsecases { source: TelemetrySource, ): Promise { try { - if (this.isCrashReportEnabled() === false) { - return; - } + if (!this.isCrashReportEnabled()) return; + const crashReport: CrashReportAttributes = this.buildCrashReport(error); + if (this.crashReports.includes(JSON.stringify(crashReport))) return; + if (this.crashReports.length >= this.maxSize) { + this.crashReports.shift(); + } + this.crashReports.push(JSON.stringify(crashReport)); await this.telemetryRepository.createCrashReport(crashReport, source); } catch (e) {} @@ -83,6 +84,22 @@ export class TelemetryUsecases { } private isCrashReportEnabled(): boolean { - return process.env.CORTEX_CRASH_REPORT === '1'; + return process.env.CORTEX_CRASH_REPORT !== '0'; + } + + private async catchException(): Promise { + process.on('uncaughtException', async (error: Error) => { + await this.createCrashReport( + error, + this.contextService.get('source') as TelemetrySource, + ); + }); + + process.on('unhandledRejection', async (error: Error) => { + await this.createCrashReport( + error, + this.contextService.get('source') as TelemetrySource, + ); + }); } }