diff --git a/src/app.module.ts b/src/app.module.ts index 81d3e97..3a344dc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,5 +1,5 @@ import { SchedulerLockModule } from 'src/scheduler/scheduler-lock.module'; -import { Module } from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; @@ -26,6 +26,9 @@ import { HealthCheckModule } from './healthCheck/healthCheck.module'; import { v2NotificationsModule } from './notifications/v2/v2notifications.module'; import { Unsubscribe } from './notifications/v2/unsubscribe/unsubscribe.entity'; import { migrations } from './app.migrations'; +import { LoggerMiddleware } from './middleware'; +import { APP_FILTER } from '@nestjs/core'; +import { ErrorFilter } from './filters/error.filter'; @Module({ imports: [ @@ -73,8 +76,16 @@ import { migrations } from './app.migrations'; HealthCheckModule, ], controllers: [], - providers: [], + providers: [ + { + provide: APP_FILTER, + useClass: ErrorFilter, + }, + ], }) -export class AppModule { +export class AppModule implements NestModule { constructor(private connection: Connection) {} + configure(consumer: MiddlewareConsumer) { + consumer.apply(LoggerMiddleware).forRoutes('*'); + } } diff --git a/src/filters/error.filter.ts b/src/filters/error.filter.ts new file mode 100644 index 0000000..98e09dc --- /dev/null +++ b/src/filters/error.filter.ts @@ -0,0 +1,37 @@ +import { + Catch, + ArgumentsHost, + Logger, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import { BaseExceptionFilter } from '@nestjs/core'; + +@Catch(Error) +export class ErrorFilter extends BaseExceptionFilter { + catch(exception: Error, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const stack = exception.stack; + const message = exception.message; + const request = ctx.getRequest(); + const httpStatus = + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + Logger.error({ + message: 'Caught Error', + path: request.url, + error: message, + status: httpStatus, + stack: stack, + }); + + response.status(httpStatus).json({ + timestamp: new Date().toISOString(), + path: request.url, + errorMessage: message, + }); + } +} diff --git a/src/main.ts b/src/main.ts index 1ff9c27..cf59508 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,10 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; +import { ErrorFilter } from './filters/error.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.useGlobalFilters(new ErrorFilter()); await app.listen(process.env.PORT || 3001); } bootstrap(); diff --git a/src/middleware/logger.ts b/src/middleware/logger.ts index 607b07e..b35c2e7 100644 --- a/src/middleware/logger.ts +++ b/src/middleware/logger.ts @@ -10,12 +10,21 @@ export class LoggerMiddleware implements NestMiddleware { const { ip, method, originalUrl } = request; const userAgent = request.get('user-agent') || ''; + this.logger.log({ + message: 'Incoming request', + method, + originalUrl, + userAgent, + ip, + }); + response.on('finish', () => { const { statusCode } = response; const contentLength = response.get('content-length'); const endAt = process.hrtime.bigint(); const responseTime = `${(endAt - startAt) / BigInt(1000)}ms`; this.logger.log({ + message: 'Outgoing response', method, originalUrl, statusCode,