From 64828c090683aa99a5b73981fbab291007a85505 Mon Sep 17 00:00:00 2001 From: mrkeksz Date: Sat, 21 Oct 2023 17:02:18 +0700 Subject: [PATCH] logging requests, responses and exceptions --- .eslintrc.js | 9 +++--- package.json | 2 +- src/app.module.ts | 20 +++++++----- src/filters/http-exception.filter.ts | 23 +++++++++----- src/interceptors/logging.interceptor.ts | 33 +++++++------------- src/middlewares/logger.middleware.ts | 19 ----------- src/middlewares/request-logger.middleware.ts | 12 +++++++ 7 files changed, 57 insertions(+), 61 deletions(-) delete mode 100644 src/middlewares/logger.middleware.ts create mode 100644 src/middlewares/request-logger.middleware.ts diff --git a/.eslintrc.js b/.eslintrc.js index 259de13..559046c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,10 +6,7 @@ module.exports = { sourceType: 'module', }, plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], root: true, env: { node: true, @@ -21,5 +18,7 @@ module.exports = { '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['warn'], + 'prettier/prettier': ['warn'], }, -}; +} diff --git a/package.json b/package.json index 3b3f036..bdddaf8 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", + "start:prod": "NODE_ENV=production node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "lint:check": "eslint \"{src,apps,libs,test}/**/*.ts\"", "test": "NODE_ENV=test jest", diff --git a/src/app.module.ts b/src/app.module.ts index a491a86..8b1e42f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,7 +3,7 @@ import {AppController} from './app.controller' import {AppService} from './app.service' import {DevtoolsModule} from '@nestjs/devtools-integration' import {UsersModule} from './users/users.module' -import {LoggerMiddleware} from './middlewares/logger.middleware' +import {RequestLoggerMiddleware} from './middlewares/request-logger.middleware' import {APP_FILTER, APP_INTERCEPTOR, APP_PIPE} from '@nestjs/core' import {HttpExceptionFilter} from './filters/http-exception.filter' import {LoggingInterceptor} from './interceptors/logging.interceptor' @@ -30,19 +30,25 @@ import {configModule, configProviders} from './config' whitelist: true, }), }, - { - provide: APP_INTERCEPTOR, - useClass: LoggingInterceptor, - }, { provide: APP_INTERCEPTOR, useClass: TimeoutInterceptor, }, + ...(process.env[NODE_ENV.name] === NODE_ENV.options.DEVELOPMENT + ? [ + { + provide: APP_INTERCEPTOR, + useClass: LoggingInterceptor, + }, + ] + : []), ...configProviders, ], }) export class AppModule implements NestModule { - configure(consumer: MiddlewareConsumer): any { - consumer.apply(LoggerMiddleware).forRoutes({path: '*', method: RequestMethod.ALL}) + configure(consumer: MiddlewareConsumer) { + if (process.env[NODE_ENV.name] === NODE_ENV.options.DEVELOPMENT) { + consumer.apply(RequestLoggerMiddleware).forRoutes({path: '*', method: RequestMethod.ALL}) + } } } diff --git a/src/filters/http-exception.filter.ts b/src/filters/http-exception.filter.ts index 82cccdb..cd0f9dc 100644 --- a/src/filters/http-exception.filter.ts +++ b/src/filters/http-exception.filter.ts @@ -1,9 +1,18 @@ -import {ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus} from '@nestjs/common' +import { + ExceptionFilter, + Catch, + ArgumentsHost, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common' import {Request, Response} from 'express' import {EnvironmentConfigService} from '../config/environment/environment.config.service' @Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(HttpExceptionFilter.name) + constructor(private readonly environmentConfigService: EnvironmentConfigService) {} catch(exception: HttpException, host: ArgumentsHost) { @@ -14,13 +23,11 @@ export class HttpExceptionFilter implements ExceptionFilter { response.status(status).json(exception.getResponse()) - if (status < HttpStatus.INTERNAL_SERVER_ERROR && !this.environmentConfigService.isTest) { - console.error( - `[Response] [${ - request.method - }] ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} - Path ${ - request.url - }, Status ${status}, Message: ${exception.cause || exception.message}`, + if (status < HttpStatus.INTERNAL_SERVER_ERROR && this.environmentConfigService.isDevelopment) { + const timeAgo = Date.now() - response.locals.startTime + this.logger.verbose( + `Response {${request.url}, ${request.method}, ${status}} \x1b[33m+${timeAgo}ms`, + `Body ${JSON.stringify(exception.getResponse())}`, ) } } diff --git a/src/interceptors/logging.interceptor.ts b/src/interceptors/logging.interceptor.ts index 1c4359f..28d6ebc 100644 --- a/src/interceptors/logging.interceptor.ts +++ b/src/interceptors/logging.interceptor.ts @@ -1,35 +1,26 @@ -import {Injectable, NestInterceptor, ExecutionContext, CallHandler} from '@nestjs/common' +import {Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger} from '@nestjs/common' import {Observable} from 'rxjs' import {tap} from 'rxjs/operators' import {Response} from 'express' -import {EnvironmentConfigService} from '../config/environment/environment.config.service' @Injectable() export class LoggingInterceptor implements NestInterceptor { - constructor(private readonly environmentConfigService: EnvironmentConfigService) {} + private readonly logger = new Logger(LoggingInterceptor.name) intercept(context: ExecutionContext, next: CallHandler): Observable { - if (this.environmentConfigService.isTest) return next.handle() - const ctx = context.switchToHttp() const response = ctx.getResponse() const request = ctx.getRequest() + response.locals.startTime = Date.now() - const now = Date.now() - return next - .handle() - .pipe( - tap(value => - console.log( - `[Response] [${ - request.method - }] ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} - Path ${ - request.url - }, Status ${response.statusCode}, Returned: ${JSON.stringify(value)} +${ - Date.now() - now - }ms`, - ), - ), - ) + return next.handle().pipe( + tap(value => { + const timeAgo = Date.now() - response.locals.startTime + this.logger.verbose( + `Response {${request.url}, ${request.method}, ${response.statusCode}} \x1b[33m+${timeAgo}ms`, + `Body ${JSON.stringify(value)}`, + ) + }), + ) } } diff --git a/src/middlewares/logger.middleware.ts b/src/middlewares/logger.middleware.ts deleted file mode 100644 index abf4a0f..0000000 --- a/src/middlewares/logger.middleware.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Injectable, NestMiddleware} from '@nestjs/common' -import {Request, Response, NextFunction} from 'express' -import {EnvironmentConfigService} from '../config/environment/environment.config.service' - -@Injectable() -export class LoggerMiddleware implements NestMiddleware { - constructor(private readonly environmentConfigService: EnvironmentConfigService) {} - - use(req: Request, res: Response, next: NextFunction) { - if (this.environmentConfigService.isTest) return next() - - console.info( - `[Request] [${ - req.method - }] ${new Date().toLocaleDateString()}, ${new Date().toLocaleTimeString()} - Path ${req.url}`, - ) - next() - } -} diff --git a/src/middlewares/request-logger.middleware.ts b/src/middlewares/request-logger.middleware.ts new file mode 100644 index 0000000..b9f330f --- /dev/null +++ b/src/middlewares/request-logger.middleware.ts @@ -0,0 +1,12 @@ +import {Injectable, Logger, NestMiddleware} from '@nestjs/common' +import {Request, Response, NextFunction} from 'express' + +@Injectable() +export class RequestLoggerMiddleware implements NestMiddleware { + private readonly logger = new Logger(RequestLoggerMiddleware.name) + + use(req: Request, res: Response, next: NextFunction) { + this.logger.verbose(`Request {${req.url}, ${req.method}}`) + next() + } +}