V-logue

[Node.js] JavaScript에서 Morgan과 Winston을 사용해 ExpressJS를 위한 더 나은 logs system 본문

발자취/Node.js

[Node.js] JavaScript에서 Morgan과 Winston을 사용해 ExpressJS를 위한 더 나은 logs system

보그 2022. 6. 8. 20:42

https://levelup.gitconnected.com/better-logs-for-expressjs-using-winston-and-morgan-with-typescript-1c31c1ab9342

 

Better logs for ExpressJS using Winston and Morgan with Typescript

A step-by-step guide on how to configure an ExpressJS application with Winston and Morgan loggers using Typescript

levelup.gitconnected.com

참고한 글 원본

A great log system is one of the easiest ways to check your application behavior and it’s our first weapon to look into bugs
대충, 잘 만들어진 로그 시스템은 당신의 application 동작을 확인하는 가장 쉬운 방법 중 하나이며, 
버그를 확인하는데 있어서 첫번째 무기가될 것이라는 어쩌고 저쩌고...

이 글에선 로그시스템을 활용하는 것을 내가 이해한대로 작성해볼 예정이다.

원글에서는, 대부분의 application이 포괄적인 로그 시스템을 가지고 있지 않거나, 심지어는 대부분

간단한 console.log를 활용해 사용한다는 점을 지적하고 있다.

 

가장 먼저,

First of all, we need an ExpressJS application. You can clone this repository.

git-hub에서 파일을 다운받아 준다.

git clone https://github.com/vassalloandrea/medium-morgan-winston-example.git

그다음, 서버를 실행하기 위해 다음의 코드를 터미널에 입력해준다.

cd medium-morgan-winston-example
npm install
npm run dev

그리고 winston을 설치해준다.

npm install winston

그리고 src폴더에 logger.js(logger.ts , 타입스크립트 파일인데 

나는 그냥 편의상 js로 만들었다. )만들고 아래의 코드를 입력해준다.

참고로, 원문에서는 타입스크립트를 기반으로 하기 때문에 js로 실행시키려는 나와는

코드적으로 어느정도 차이가 있기 때문에 참고하면 감사할 것 같다.

const winston = require("winston);

const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  debug: 4,
}

const level = () => {
  const env = process.env.NODE_ENV || 'development'
  const isDevelopment = env === 'development'
  return isDevelopment ? 'debug' : 'warn'
}

const colors = {
  error: 'red',
  warn: 'yellow',
  info: 'green',
  http: 'magenta',
  debug: 'white',
}

winston.addColors(colors)

const format = winston.format.combine(
  winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
  winston.format.colorize({ all: true }),
  winston.format.printf(
    (info) => `${info.timestamp} ${info.level}: ${info.message}`,
  ),
)

const transports = [
  new winston.transports.Console(),
  new winston.transports.File({
    filename: 'logs/error.log',
    level: 'error',
  }),
  new winston.transports.File({ filename: 'logs/all.log' }),
]

const Logger = winston.createLogger({
  level: level(),
  levels,
  format,
  transports,
})

module.exports = Logger

여기서 주의할 점은, 나는 js파일로 만들었기 때문에 logger를 내보내려면, module.exports = Logger로 해야 한다는 점이었다. 실행이 안되다가 저렇게 바꾸니까 실행이 됐다.

 

그리고 node index.js로 실행을 시키고, Thunder Client로 get으로 받으면, 아래와 같이 터미널에 나온다.

보시다시피, logger는 logs들을 다른 컬러를 통해 보여주게 된다. 하지만, 이것보다 더 중요한 다른 점은

파일을 실행시키고 난 후 생성되는 logs디렉토리의 all.log와 error.log에도 기록된다는 점이다.

 

다음은, 내가 원글에 기록된 주석을 해석한 내용이다.

const winston = require("winston");

// Define your severity levels, 각 수준은 심각도를 나타낸다.
// With them, 로그 파일을 만들 수 있다.
// 실행준인 env를 기준으로 수준을 보거나 숨길 수 있다.
const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  debug: 4,
}

// 이 메서드는 다음을 기준으로 심각도 수준을 정의한다.
// the current NODE_ENV: 모든로그 수준 표시
// 서버가 개발모드에서 실행된 경우와 그렇지 않은 경우
// 운영환경에서 실행된 경우 warn(경고)와 debug(오류)메세지를 표시한다.
const level = () => {
  const env = process.env.NODE_ENV || 'development'
  const isDevelopment = env === 'development'
  return isDevelopment ? 'debug' : 'warn'
}

// 각 수준에 대해서 다른 색상으로 정의한다.
// 로그 메세지를 더 잘보이고 구분되게 표시하게된다.
// 메시지에 초점을 맞추거나, 메세지를 무시하는 기능을 추가할 수 있다.
const colors = {
  error: 'red',
  warn: 'yellow',
  info: 'green',
  http: 'magenta',
  debug: 'white',
}

// winston에게 위에서 정의한 colors를 연결하고 싶다고 정의한다.
winston.addColors(colors)

// 로그 형식을 사용자 정의에 따라 어떻게 보여줄지 선택한다.
const format = winston.format.combine(
  // 기본 형식을 사용하여 메세지에 타임스탬프 추가,
  winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
  // winston에게 색을 입혀야 한다고 지정
  winston.format.colorize({ all: true }),
  // 타임스탬프, 그리고 심각도 수준, 메세지를 어떻게 표시할 것인지 Define
  winston.format.printf(
    (info) => `${info.timestamp} ${info.level}: ${info.message}`,
  ),
)

// logger가 메세지를 출력하는데 사용해야하는 전송양식을 Define
// 다음의 example에선 3가지 양식을 Define 한다.
const transports = [
  // 메세지를 출력하기 위해 console을 사용하는 것을 허락한다.
  new winston.transports.Console(),
  // error.log파일에 모든 수준의 메시지를 출력할 수 있게 한다.
  new winston.transports.File({
    filename: 'logs/error.log',
    level: 'error',
  }),
  // all.log파일에 모든 오류 메세지를 출력할 수 있게 한다.
  // 또한 error.log(오류로그) 내부에도 인쇄된다.
  new winston.transports.File({ filename: 'logs/all.log' }),
]

// exports할 Logger 인스턴스를 생성한다.
// 메세지를 기록하는데 사용된다.
const Logger = winston.createLogger({
  level: level(),
  levels,
  format,
  transports,
})

//   export default Logger, ts파일에서 사용할 버전
module.exports = Logger // js에서는 이렇게 해야하는 것 같다.

이해가 잘 안간다면, 위 주석을 본다면 이해가 아마 잘 될것이다. 나는 그랬다.

 

현재 여기까지 실행한 사람이라면, 아마 기능의 복잡성?(잘 모르겠다)에 따라 로그를 기록하는

application을 실행할 수 있을 것이다.

With Winston, you can also change the log severity at the runtime using an ENV variable.
윈스턴과 함께라면, ENV변수를 사용하여 런타임에서 로그의 심각도를 바꿀수도 있다는 것 같다.

이부분은 잘 이해가 안되는..
누구 잘 아시는 분이 있다면... 알려주시기 바랍니다..

ExpressJS가 requests들을 처리하도록 만들어졌기 때문에, requests된 모든 요청 정보(every requests information)들을 

자동으로(automatically) 추가해주는 요청 로거(requests logger)를 추가해야 한다.

이런 목표는, Winston configuration과 쉽게 통합될 수 있는 라이브러리를 사용함으로써 달성되야 한다.

바로 이런점에서 쉽게 통합될 수 있는 Middleware가 있는데, 그게 바로 morgan이다.

 

morgan은 요청된 log를 customize 하는데 필요한 NodeJs의 미들웨어다.

npm install morgan @types/morgan

npm으로 morgan을 설치했다면,

 

원글에서 제시한 간단하게 구성된 morgan 미들웨어를 사용해보도록 하자,

restAPI원칙에 따라 알아서 파일을 만들어 다음의 코드를 복/붙하면,

const morgan = require("morgan");

// Override the stream method by telling
// Morgan to use our custom logger instead of the console.log.
const stream = {
    // 원글에선 const stream: StreamOptions = { } 이런식인데
    // StreamOptions는 ts에서만 사용가능하다.
    // Use the http severity
    write: (message) => Logger.http(message),
  };
  
  // 모든 Morgan http log를 건너 뛴다.
  // 응용 프로그램이 개발모드에서 실행되고 있지 않다.
  // This method는 여기서 진짜로 필요한 것은 아니다.
  // 우리는 이미 logger에게 출력해야 한다고 했기 때문이다.
  // 경고 및 에러메세지만 production에서 볼 수 있다.
  const skip = () => {
    const env = process.env.NODE_ENV || "development";
    return env !== "development";
  };
  
  // 모건 미들웨어 구축
  const morganMiddleware = morgan(
    // 메세지 형식 문자열을 Define한다(기본 문자열로),
    // 메세지 형식은 토큰으로 만들어지며,
    // 각 토큰은 모건 라이브러리 내에 Define되있다.
    // 이것을 사용하는 당신은 사용자 지정 토큰을 만들어 요청에서 원하는 내용을 표시할 수 있다.
    ":method :url :status :res[content-length] - :response-time ms",
    // Options: 이 케이스의 경우, Stream과 skip logic을 덮어썼다.
    // 위의 방법을 참조하면,
    { stream, skip }
  );
  
//   export default morganMiddleware;
module.exports = morganMiddleware;

만들어진 morganMiddleware를 express서버인 index.js에 추가한다.

const morganMiddleware  = require('./config/morganMiddleware') // 만든 미들웨어 로드

//

app.use(morganMiddleware); // 미들웨어 추가

 그리고 node index.js로 실행을 시키면 다음과 같이 나오게 된다.

여기서 중요한 점은 마지막 http: GET /logger 200 11 - 6.681ms 이부분이다.

먼저 http: 는 Severity level

GET은 Request Method,

/logger는 end point,

200은 Response status,

200다음의 11은 content-length로 자세한 설명은

https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Length 을 참고하자!

그리고 마지막 6.681ms는 response time 즉, 응답시간을 말한다.

 

이렇게 응답된 로그를 통해 어떤 로그가 들어왔는지 알 수 있게 된다.

 

 

 

 

추가적으로 더 알게된다면, 수정할 예정

 

 

 

Comments