엘리스 AI트랙 4기/프로젝트

[TypeScript] morgan & winston으로 로그 남기기

남쪽마을밤송이 2022. 6. 28. 21:54

 [01 Log란?] 

  • 서버에서 발생하는 모든 행위와 이벤트 정보를 시간에 따라 남기는 기록을 의미한다.
  • 어떠한 정보를 남길지는 서버가 선택할 수 있으며 로그가 있어야 차후 버그나 해킹, 복구 등의 문제가 발생했을 때 신속하게 대처할 수 있다.

 

  • 나는 아직 실제 서비스를 운영해 본 적이 없지만 로그 관리와 관련한 여러 블로그를 읽으면서 로그가 없으면 정말 큰일난다는 반응을 많이 보았다...ㅎㅎ... (제일 많이 참고한 블로그)
  • 그래서 미리 미리 로그를 남겨보기 위해 Javascript(Typescript) 프로젝트에서 morgan과 winston으로 로그 남기는 법을 적용해보았다.

 

 [02 사용할 패키지] 

 (1) 필요한 패키지 

  • Javascript(Typescript)에서 로그를 남기는 데 두 가지 패키지가 필요한 이유는 다음과 같다.
    • morgan : request 요청에 대해 express가 기본적으로 출력해주는 output을 깔끔하게 정리해주는 모듈
    • winston : 로그를 기록하고 로그 파일 및 로그 레벨을 관리하는 모듈
    • winston-daily-rotate-file : 매일 날짜별로 로그 파일 생성 및 관리 모듈 (logrotate 기능)

 

 (2) 패키지 설치 

  •  morgan 은 TypeScript 호환용 패키지가 따로 있어 둘 다 설치해줘야 한다.
npm install morgan
npm install --save @types/morgan
  •  winston 과  winston-daily-rotate-file 은  각각만 설치해줘도 문제 없다.
npm install winston
npm install winston-daily-rotate-file

 

 [03 코드 적용] 

 (1) app.ts 

  • morgan은 다음과 같이 express( ) 객체를 선언한 곳에서 추가해준다.
import express, { Request, Response } from "express";
import cors from "cors";
import morgan from "morgan";
import { stream } from "./utils/winston";

import swaggerUi from "swagger-ui-express";
import YAML from "yamljs";
import path from "path";

import { UserRouter } from "./routes/userRouter";
.
.
.

const app: express.Application = express();
const swaggerSpec = YAML.load(path.join(__dirname, "./swagger.yaml"));

// swagger
app.use("/swagger", swaggerUi.serve, swaggerUi.setup(swaggerSpec));

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// morgan (request, response formatting)
app.use(morgan("combined", { stream }));

app.get("/", (req: Request, res: Response) => {
  res.send("hello typescript express!");
});

 (2) utils / winston.ts 

  • winston은 따로 파일을 만들어 info.log와 error.log로 나눠서 DailyRotateFile로 매일 로그가 생성되고 최대 30일간 저장되도록 설정했다.
import fs from "fs";
import moment from "moment";
import "moment-timezone";
import winston from "winston";
import "winston-daily-rotate-file";

const logDir = __dirname + "/../logs";

if (!fs.existsSync(logDir)) {
  fs.mkdirSync(logDir);
}

const infoTransport = new winston.transports.DailyRotateFile({
  filename: "info.log",
  dirname: logDir,
  level: "info",
  maxFiles: "30d", // 30일치 저장
});

const errorTransport = new winston.transports.DailyRotateFile({
  filename: "error.log",
  dirname: logDir,
  level: "error",
  maxFiles: "30d", // 30일치 저장
});

moment.tz.setDefault("Asia/Seoul"); // 로그 시간대 한국 기준으로 변경
const timeStamp = () => moment().format("YYYY-MM-DD HH:mm:ss");

const logger = winston.createLogger({
  transports: [infoTransport, errorTransport],
});

const stream = {
  write: (message: string) => {
    logger.info(`${timeStamp()} ${message}`);
  },
};

export { logger, stream };