diff --git a/BE/package-lock.json b/BE/package-lock.json index 81d99c1..d4a4c3f 100644 --- a/BE/package-lock.json +++ b/BE/package-lock.json @@ -19,6 +19,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "crypto": "^1.0.1", + "morgan": "^1.10.0", "multer-s3": "^3.0.1", "mysql2": "^3.6.3", "nest-winston": "^1.9.4", @@ -4393,6 +4394,22 @@ } ] }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -8266,6 +8283,45 @@ "node": "*" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8511,6 +8567,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/BE/package.json b/BE/package.json index 04dcd4a..dadd90d 100644 --- a/BE/package.json +++ b/BE/package.json @@ -30,6 +30,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "crypto": "^1.0.1", + "morgan": "^1.10.0", "multer-s3": "^3.0.1", "mysql2": "^3.6.3", "nest-winston": "^1.9.4", diff --git a/BE/src/config/winston.config.ts b/BE/src/config/winston.config.ts index 0b69b7e..07e604a 100644 --- a/BE/src/config/winston.config.ts +++ b/BE/src/config/winston.config.ts @@ -5,7 +5,7 @@ import { } from 'nest-winston'; export const winstonOptions = new winston.transports.Console({ - level: process.env.NODE_ENV === 'prod' ? 'info' : 'silly', + level: process.env.NODE_ENV === 'prod' ? 'http' : 'silly', format: winston.format.combine( winston.format.timestamp(), winston.format.colorize({ all: true }), diff --git a/BE/src/main.ts b/BE/src/main.ts index 7c78f9f..38e8748 100644 --- a/BE/src/main.ts +++ b/BE/src/main.ts @@ -5,6 +5,7 @@ import { WINSTON_MODULE_NEST_PROVIDER, WinstonModule } from 'nest-winston'; import { dailyOption, winstonOptions } from './config/winston.config'; import * as winstonDaily from 'winston-daily-rotate-file'; import { ValidationPipe } from '@nestjs/common'; +import { HttpLoggerInterceptor } from './utils/httpLogger.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule, { @@ -16,7 +17,7 @@ async function bootstrap() { ], }), }); - + app.useGlobalInterceptors(new HttpLoggerInterceptor()); app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER)); app.useGlobalPipes( new ValidationPipe({ @@ -24,6 +25,7 @@ async function bootstrap() { }), ); setupSwagger(app); + await app.listen(3000); } bootstrap(); diff --git a/BE/src/post/post.controller.ts b/BE/src/post/post.controller.ts index 0b3cd1b..d115da1 100644 --- a/BE/src/post/post.controller.ts +++ b/BE/src/post/post.controller.ts @@ -19,6 +19,8 @@ import { FilesInterceptor } from '@nestjs/platform-express'; import { PostCreateDto } from './dto/postCreate.dto'; import { MultiPartBody } from '../utils/multiPartBody.decorator'; import { PostListDto } from './dto/postList.dto'; +import { http } from 'winston'; +import { HttpLoggerInterceptor } from 'src/utils/httpLogger.interceptor'; @Controller('posts') @ApiTags('posts') diff --git a/BE/src/utils/httpLogger.interceptor.ts b/BE/src/utils/httpLogger.interceptor.ts new file mode 100644 index 0000000..41540f1 --- /dev/null +++ b/BE/src/utils/httpLogger.interceptor.ts @@ -0,0 +1,43 @@ +import { + CallHandler, + ExecutionContext, + NestInterceptor, + Injectable, + Logger, + HttpException, + BadGatewayException, +} from '@nestjs/common'; +import { request } from 'http'; +import { Observable, throwError } from 'rxjs'; +import { map, tap, catchError } from 'rxjs/operators'; + +@Injectable() +export class HttpLoggerInterceptor implements NestInterceptor { + private readonly logger = new Logger('HTTP'); + intercept( + context: ExecutionContext, + next: CallHandler, + ): Observable | Promise> { + const request = context.switchToHttp().getRequest(); + + return next.handle().pipe( + tap((data) => { + const request = context.switchToHttp().getRequest(); + const response = context.switchToHttp().getResponse(); + this.logger.log( + `${request.url} ${request.method} ${request.ip}/${response.statusCode} ${response.statusMessage}`, + 'HTTP', + ); + }), + catchError((err: HttpException) => { + this.logger.log( + `${request.url} ${request.method} ${ + request.ip + }/${err.getStatus()} ${err.getResponse()}`, + 'HTTP ERROR', + ); + return throwError(err); + }), + ); + } +}