diff --git a/BackendAcademy/src/app.module.ts b/BackendAcademy/src/app.module.ts index 647e5e4d3..3e2e3fa47 100644 --- a/BackendAcademy/src/app.module.ts +++ b/BackendAcademy/src/app.module.ts @@ -17,6 +17,7 @@ import { SocialModule } from './social/social.module'; import { OnboardingModule } from './onboarding/onboarding.module'; import { LessonModule } from './lessons/lesson.module'; import { TaskModule } from './tasks/task.module'; +import { LoggingModule } from './logging/logging.module'; import { ProgressModule } from './courses/progress/progress.module'; import { AppConfigModule } from './config/config.module'; import { ContractsModule } from './contracts/contracts.module'; @@ -50,6 +51,7 @@ import { SessionsModule } from './sessions/sessions.module'; OnboardingModule, LessonModule, TaskModule, + LoggingModule, PathfindingModule, MonitoringModule, ProgressModule, diff --git a/BackendAcademy/src/logging/error-tracking.service.spec.ts b/BackendAcademy/src/logging/error-tracking.service.spec.ts new file mode 100644 index 000000000..fbe4e258e --- /dev/null +++ b/BackendAcademy/src/logging/error-tracking.service.spec.ts @@ -0,0 +1,26 @@ +import { ErrorTrackingService, ErrorTrackingReport } from './error-tracking.service'; + +describe('ErrorTrackingService', () => { + let service: ErrorTrackingService; + + beforeEach(() => { + service = new ErrorTrackingService(); + }); + + it('captures an exception as a structured placeholder report', () => { + const report = service.captureException(new Error('boom'), 'users'); + + expect(report).toEqual( + expect.objectContaining({ + source: 'placeholder', + context: 'users', + message: 'boom', + }), + ); + expect(report.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/); + }); + + it('initializes process handlers without throwing', () => { + expect(() => service.onModuleInit()).not.toThrow(); + }); +}); diff --git a/BackendAcademy/src/logging/error-tracking.service.ts b/BackendAcademy/src/logging/error-tracking.service.ts new file mode 100644 index 000000000..a1692445f --- /dev/null +++ b/BackendAcademy/src/logging/error-tracking.service.ts @@ -0,0 +1,43 @@ +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; + +export interface ErrorTrackingReport { + source: 'placeholder'; + context?: string; + message: string; + stack?: string; + timestamp: string; +} + +@Injectable() +export class ErrorTrackingService implements OnModuleInit { + private readonly logger = new Logger(ErrorTrackingService.name); + + onModuleInit(): void { + process.on('uncaughtException', (error: Error) => { + this.captureException(error, 'process'); + }); + + process.on('unhandledRejection', (reason: unknown) => { + this.captureException(reason, 'process'); + }); + + this.logger.log('Error tracking placeholder integration initialized'); + } + + captureException(error: Error | unknown, context?: string): ErrorTrackingReport { + const message = error instanceof Error ? error.message : String(error); + const stack = error instanceof Error ? error.stack : undefined; + + const report: ErrorTrackingReport = { + source: 'placeholder', + context, + message, + stack, + timestamp: new Date().toISOString(), + }; + + this.logger.warn(`Captured error report for ${context ?? 'unknown'}: ${message}`); + + return report; + } +} diff --git a/BackendAcademy/src/logging/logging.module.ts b/BackendAcademy/src/logging/logging.module.ts new file mode 100644 index 000000000..9de958e7e --- /dev/null +++ b/BackendAcademy/src/logging/logging.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { ErrorTrackingService } from './error-tracking.service'; + +@Module({ + providers: [ErrorTrackingService], + exports: [ErrorTrackingService], +}) +export class LoggingModule {}