-
nestjs-pino 로깅 처리NodeJS 2023. 5. 30. 13:58728x90
nestjs-pino 로깅 처리
상황 정리
위와 같은 WEB 환경에서
Some logging
의 경우Request(요청)
정보가 없기 때문에 동시 다발적인 이벤트의 로그들을 추적하기가 불가능 합니다.이를 nestjs-pino 이용해서 각 로그별로 동일한 요청의 경우 연결 처리 할 수 있도록 작성합니다
nestjs-pino에서도 적혀 있지만, pino-http모듈을
nestjs
에 녹인 프로젝트입니다.필요 모듈 정보
- nestjs-pino : nestjs와 연동 처리된 모듈
- pino-http : pino 로그에 request, response 정보를 bind 처리한 모듈
- file-stream-rotator : 파일 스트림을 기반으로 파일의 생명주기를 관리하는 모듈
- pino-pretty : pino 로그의 결과를 이쁘게 정렬하여 표기하는 모듈
기본 모듈 설치 및 설정
nestjs-pino 는 기본적으로 로그 파일 저장시 파일을 나눠주거나, 관리를 지원하지 않습니다.
이를 file-stream-rotator 모듈을 이용해서 로그 파일에 대한 생명주기 및 적재 관리를 합니다.
다음 명령어로 기본적인 모듈을 설치합니다.
$ npm i nestjs-pino pino-http file-stream-rotator
nestjs 설정하기
먼저, nestjs의
main.ts
에서 Logging을 설정 합니다.import { Logger } from "nestjs-pino"; const app = await NestFactory.create(AppModule, { bufferLogs: true }); app.useLogger(app.get(Logger));
이후
app.module.ts
에 아래와 같이 설정 합니다.import { LoggerModule } from "nestjs-pino"; @Module({ imports: [LoggerModule.forRoot()], }) class AppModule {}
위의 예제는 기본 설정입니다.
위와 같이 설정 후 아래와 같이 로깅을 하면,
// NestJS standard built-in logger. // Logs will be produced by pino internally import { Logger } from '@nestjs/common'; export class MyService { private readonly logger = new Logger(MyService.name); foo() { // All logger methods have args format the same as pino, but pino methods // `trace` and `info` are mapped to `verbose` and `log` to satisfy // `LoggerService` interface of NestJS: this.logger.verbose({ foo: 'bar' }, 'baz %s', 'qux'); this.logger.debug('foo %s %o', 'bar', { baz: 'qux' }); this.logger.log('foo'); } }
아래와 같이 표출 됩니다.
// Logs by injected Logger and PinoLogger in Services/Controllers. Every log // has it's request data and unique `req.id` (by default id is unique per // process, but you can set function to generate it from request context and // for example pass here incoming `X-Request-ID` header or generate UUID) {"level":10,"time":1629823792023,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"context":"MyService","foo":"bar","msg":"baz qux"} {"level":20,"time":1629823792023,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"context":"MyService","msg":"foo bar {\"baz\":\"qux\"}"} {"level":30,"time":1629823792023,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"context":"MyService","msg":"foo"}
로깅 결과 이쁘게 표시 하기
pino-pretty는 stdout 리디렉션을 사용하기 때문에 경우에 따라 셸 제한으로 인해 명령이 오류와 함께 종료될 수 있습니다. 따라서 운영에서는 사용하지 말고, 개발시에만 사용하세요.
로깅을 이쁘게 표기하기 위해서는 pino-pretty모듈을 설치하면 됩니다.
$ npm install pino-pretty
설정은 아래와 같이 할 수 있습니다.
import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ LoggerModule.forRoot({ pinoHttp: [ { name: 'add some name to every JSON line', level: process.env.NODE_ENV !== 'production' ? 'debug' : 'info', // install 'pino-pretty' package in order to use the following option transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined, useLevelLabels: true, // and all the others... }, ], }) ], ... }) class MyModule {}
처리를 하면 다음과 같이 정렬된 로그를 확인 가능합니다.
[17:05:49.172] INFO (20249): GET / 200 168 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 ::1 { "level": 10, "time": 1629823792023, "pid": 15067, "hostname": "my-host", "req": { "id": 1, "method": "GET", "url": "/", "query": { }, "params": { "0": "" }, "headers": { "host": "localhost:3000", "user-agent": "curl/7.64.1", "accept": "*/*" }, "remoteAddress": "::1", "remotePort": 63822 }, "context": "MyService", "foo": "bar", "msg": "baz qux" }
로그 설정 파일 관리
다음은
pinoLogging.ts
로 로깅 설정에 대한 정보입니다.import pino from 'pino'; import * as FileStreamRotator from 'file-stream-rotator'; import { v4 } from 'uuid'; type Level = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; export default () => { const LOG_LEVEL = process.env.LOGGING_DEBUG ? 'debug' : 'info'; // 로그 파일 관리 스트림 생성 const rotatingLogStream = FileStreamRotator.getStream({ filename: `${process.env.LOGGING_PATH}/${LOG_LEVEL}/${LOG_LEVEL}-%DATE%`, // 파일 위치 & 이름 frequency: '1h', // 주기 설정 date_format: 'YYYY-MM-DD-HH', // 데이터 포멧 설정 size: process.env.LOGGING_MAXSIZE, // 최대 파일 크기 설정 max_logs: process.env.LOGGING_MAXFILES, // 파일 로깅 audit_file: `${process.env.LOGGING_PATH}/audit.json`, // 정보 파일 extension: '.log', // 로그 확장자 create_symlink: true, // 링크 파일 여부 symlink_name: 'tail-current.log', //링크 파일 명 }); return { logginConfig: { pinoHttp: { genReqId: function (req, res) { // req - id 를 uuid로 생성 const uuid = v4(); res.header('X-Request-Id', uuid); return uuid; }, transport: LOG_LEVEL === 'debug' // debug일 경우 pretty 처리 ? { target: 'pino-pretty' } : undefined, level: LOG_LEVEL, // 여기에도 있고, stream 상세에도 있어야 정상 동작 한다 stream: pino.multistream([ // multistream으로 여러군데 동시 출력 { stream: rotatingLogStream, level: LOG_LEVEL as Level, }, { stream: process.stdout, // 콘솔에 출력 level: process.env.LOGGING_CONSOLE_LEVEL as Level, }, ]), formatters: { // 로그 표시시 포멧 level(level) { return { level }; }, }, redact: { // 로그 표기시 제외 처리 remove: true, paths: [ 'email', 'password', 'req.query', 'req.params', 'req.query', 'res.headers', 'req.headers.host', 'req.headers.connection', 'req.headers.accept', 'req.headers.origin', 'req.headers.referer', 'req.headers["content-type"]', 'req.headers["sec-ch-ua"]', 'req.headers["sec-ch-ua-mobile"]', 'req.headers["user-agent"]', 'req.headers["sec-ch-ua-platform"]', 'req.headers["sec-fetch-site"]', 'req.headers["sec-fetch-mode"]', 'req.headers["sec-fetch-dest"]', 'req.headers["accept-encoding"]', 'req.headers["accept-language"]', 'req.headers["if-none-match"]', ], }, timestamp: pino.stdTimeFunctions.isoTime, }, }, }; };
전체 예제 코드 바로 가기
전체 예제 코드를 보면, nestjs-pino 외에도 nestjs에서 사용되는 여러 모듈의 사용법을 정리해 두었습니다.
참고자료
728x90'NodeJS' 카테고리의 다른 글
정규식을 이용해서 package-lock.json의 주소를 변경 처리 (0) 2023.06.02 VSCode에서 Jest Debug 설정 (0) 2023.05.31 Node.js 메모리 옵션 (0) 2023.05.29 Nodejs 동작 훑어보기 (0) 2023.05.27 SLACK 봇 알림 처리 (0) 2023.05.26