티스토리 뷰

예전에 winston을 이용해서 log를 남기는 법에 대해 글을 작성한 적이 있다.

참고: https://xively.tistory.com/21

 

[node.js express] winston으로 node console log 관리하기. winston 사용 설정 자세한 설명!!

오늘은 express에서 winston으로 console창에 logger 남기는 법, log기록 파일로 저장하는 법에 대해 말해볼 것이다 정말...열심히 찾아가보면서 만들었더니 혼자 쓰기 아까워서 글로 남김ㅋㅋㅋ글쓰는데

xively.tistory.com

이 글 남기고 winston middleware 설정하는 법에 대해서도 글을 남기려고 했는데 너무 바빠져서 이제야 글을 올린다...

 

내 개인적인 생각으론 winston은 백엔드 개발하면서 기능별로 log를 남길 때 좋은 것 같고, winston은 프론트-백엔드와 통신하는 log를 기록하기에 적합하다고 생각한다. (내가 그런식으로 커스터마이징을 해서 그런 것일 수도 있음)

 

모르간 설정을 4월쯤에 해놔서 기억이 가물가물해져서 큰일이지만... 코드보면서 최대한 기억을 짜내볼 예정


우선 morgan 모듈을 다운받아 준다. npm이나 yarn을 통해 설치해주면 됨

그러고나서 morgan 미들웨어를 만들어 줄 것이다.

 

1. 나는 백엔드 소스가 있는 폴더의 config 폴더 내에 morganMiddleware.js 파일을 만들어줬다.

프로젝트 root>servers(백엔드)>config>morganMiddleware.js

const morgan = require("morgan");
const Logger = require('../config/logger');

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

// Override the stream method by telling
// Morgan to use our custom logger instead of the console.log.
const stream = {
    // Use the http severity
    write: message => { Logger.http(message) }
};

const skip = () => {
    const env = process.env.NODE_ENV || "development";
    return env !== "development";
};

// https://jeonghwan-kim.github.io/morgan-helper/
morgan.token("status", function (req, res) {
    let color ;

    if (res.statusCode < 300) color = "\x1B[32m"    //green
    else if (res.statusCode < 400) color = "\x1B[36m" //cyan
    else if (res.statusCode < 500) color = "\x1B[33m"   //yellow
    else if (res.statusCode < 600) color = "\x1B[31m"   //red
    else color = "\033[0m" /*글자색 초기화*/

    return color + res.statusCode + "\033[35m" /*보라색*/;
});

// https://jeonghwan-kim.github.io/morgan-helper/
morgan.token("request", function (req, res) {
    return "Request_" + JSON.stringify(req.body);
});
morgan.token("makeLine", function () {
    let line = "-----------------------------------------------*(੭*ˊᵕˋ)੭* 응답 결과 ╰(*'v'*)╯-----------------------------------------------"
    let blank = "                                   ";
    return line + "\n" + blank;
});

// Build the morgan middleware
// morgan 함수의 인자(format)로는 short, dev, common, combined 가 올 수 있다. (정보의 노출 수준)
// 보통 배포시에는 combined 혹은 common 에 필요한 정보들을 추가하여 사용하는 것을 추천 || 추후 배포 시 사용 -> 주소,IP_ :remote-addr :remote-user |
const morganMiddleware = morgan(
    ":makeLine 요청_:method | url_':url' | :request | Status_:status | 응답시간_:response-time ms (:res[content-length]줄)",
    { stream, skip }
);

module.exports =  morganMiddleware;

내가 해당 코드를 짜면서 가장 많이 도움된 사이트의 주소를 주석에 달아놨다. 이해가 잘 안되는 분들은 사이트로 들어가서 보셔도 됨

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

요기 사이트를 토대로 middleware 파일을 설계했다고 봐도 무관할 듯!

반응형

2. 서버의 app.js(백엔드의 인덱스파일)에 morgan middleware 설정

코드 설명 들어가기 전에 여기 설정하는 것 먼저 보고 가쟈... 설명이 필요없는 분들도 있을테니!

참고로 나는 server.js라고 이름을 정해놨다.

root>servers>server.js

const express = require('express');
const app = express();
const port =process.env.PORT || 3001;
/**********로거 출력용 logger, morgan**********/
global.logger || (global.logger = require('./config/logger'));  // → 전역에서 사용
const morganMiddleware = require('./config/morganMiddleware');
app.use(morganMiddleware);  // 콘솔창에 통신결과 나오게 해주는 것
/**********로그인 세션 관리**********/
const session = require('express-session');
const passport = require('passport');
const passportConfig = require('./passport');
/**********라우트 목록**********/
const apiRoute = require('./routes/apiRoute');
// 생략... 대충 api router설정해주는 부분임!

// 반드시 session이후에 passport.initialize()와 passport.session()이 위치해야 합니다.
app.use(session({
    secret: 'hafProject',   //세션 암호화
    resave: false,  //세션을 항상 저장할지 여부를 정하는 값. (false 권장)
    saveUninitialized: true ,   //초기화되지 않은채 스토어에 저장되는 세션
    // cookie: { secure: false, maxAge: 60000  },
}));

app.use('/uploads', express.static('uploads')); //uploads 폴더로 이동

app.use(passport.initialize());
app.use(passport.session());
passportConfig(passport);

app.use(express.json()); // json으로 받아들인 정보를 분석함
// 아래 옵션이 false면 노드의 querystring 모듈을 사용하여 쿼리스트링을 해석하고, true면 qs 모듈을 사용하여 쿼리스트링을 해석한다
app.use(express.urlencoded({ extended: true }));

// 생략... 대충 api router설정해주는 부분임!

app.listen(port, () => {
    logger.debug(`SERVER ON ... Express is running on http:localhost:${port}`);
});

거의 풀 소스를 올렸는데, 필요한 것은 상단부에 위치한 요 코드이다.

맨위에 global 어쩌고는 winston의 log찍어주는 함수를 전역에서 사용하기 위함이고

지금 필요한 것은 2번째!

config폴더에 만들어 놓은 모르간 미들웨어를 import해주고, app.use(요기 미들웨어파일 변수) 이렇게 해준다.

결과는 쨘~~~ 친구는 이모티콘이 똥같다고 했지만 나만 귀여우면 됨

winston의 log와 morgan의 api 통신 커스터마이징을 합치니까 개발하기 정말 편할 것 같다.

참고로 밑에 304는 이미지 불러오는 거라서 그럼..! 아마...^^

 

암튼 결과는 이정도로 보고 코드 설명 ㄱㄱ


1. import

맨위에 코드는 morgan 모듈 불러오는거 알거고, 밑에 logger는 winston의 log출력 함수를 연결해놓은 것이다.

config>logger 파일 확인하기 >>> (https://xively.tistory.com/21)

지금 생각해보니 전역변수 선언해놨으니까 그거 썼어도 됐지 않을까 싶긴한데... 일단 진행

2. stream 값 설정 (write 함수 설정)

실질적으로 통신할 때 출력할 메시지를 오버라이딩하는 것 같은데...

번역기 돌리면 대충 " 스트림 메서드를 재정의합니다. Morgan은 console.log 대신 사용자 정의 로거를 사용합니다." 이런 뜻이니까 내가 이해한게 맞을 듯..ㅎㅎ

morgan 라이브러리를 까보면 write 함수는 stream을 받아서 buffer에 넣어주는 것 같은데... 얘가 최종적으로 쓰이는게 morgan() 함수인 것을 보면 출력할 메시지를 넣어주는 것 같다. 

(비전공자이다 보니까 요런 스트림이나 버퍼 이런 것에 대한 이해가 부족하다는 걸 계속 느끼고있다... 정처기는 땄는데, 얘로는 역시 모자란 느낌... 또 여유로워지면 공부를 시작해야겠다 다짐하게됨..ㅋㅋㅋ)

 

아무튼 나는 logger함수의 http레벨로 메시지를 출력할 예정이다.

3. morgan 미사용할 환경 설정

요건 어떤 상태일 때 morgan을 실행하지 않고 skip할 것인지 설정하는 곳

4-1. HTTP 통신 결과 확인 토큰 생성 및 색상 변경

이건 morgan 함수에서 사용할 변수를 생성해주는 mogan.token() 함수이다.

첫번째 인자에는 사용할 변수(토큰) 명, 두번째 인자에는 받아올 인자를 넣어주면 되는데, 나는 서버 응답 결과를 색깔 별로 다르게 표현하고 싶어서 이렇게 설정하였다.

 

morgan에서 글자 색깔을 변경하려면 정규식을 사용해야하는 듯 하다. (winston은 영문으로 표현할 수 있었는데 요건 좀 아쉽긴함ㅋㅋ)

암튼 통신결과가 300 미만이면 초록색, 400미만이면 옥색 등... 설정해뒀다.

그리고 마지막에 보라색을 더해주는 이유가 쟤를 안해주면 console의 status 마지막 글자들이 설정 색으로 변경되기 때문에 winston에서 설정한 http 로그 설정값인 보라색으로 변경해주는 것이다.

암튼 이런식으로 통신 결과에 따라 색상을 변경하여 육안으로 확인하기 쉽게 변경할 수 있게 되었다.

4-2. Request로 받아오는 값 확인할 토큰 생성

요게 나름 핵심 아닐까싶다. 프론트 단에서 body에 담아서 보내준 값을 보여줄 수 있게 해주는 것.

반대로 벡엔드 단에서 프론트 단으로 넘겨주는 값도 보여주고 싶었는데, 보안 상으로 못보게 해놓은 것 같다...

4-3. console에 보기 좋게 정렬해주는 토큰 생성 (생략해도 무관)

이건 굳이 안해도 되긴한데,, 깔끔하게 보고 싶어서 구분선을 만들어 줬다. 이모티콘은 그냥 내가 넣고싶어서 넣은거라

---- 이렇게 짝대기만 넣어줘도 됨ㅋㅋㅋㅋ

5. morgan 함수 설정

마지막 morgan함수에 사용할 커스터마이징한 format과 option을 넣어준다.

format에는 그냥 문자열을 사용하면 되는데, 신기한게 위에서 생성한 토큰변수는 앞에 : 콜론을 찍고 토큰 명을 작성해주면 된다.

 

:method 는 GET인지 POST인지 PUT인지 표현해주는 내장 변수이다. 이 외에도 내가 만들지 않은 변수는 다 내장되어 있는 것임

:url 은 요청 된 api 주소 / :response-time는 응답 시간 / :res[content-length] 는 응답 길이

이 외에도 다양한 내장 변수가 있다. 내장변수 확인하려면 요기 아래 주소에서!!

https://www.npmjs.com/package/morgan

 

morgan

HTTP request logger middleware for node.js

www.npmjs.com

Tokens 되어있는 곳 쪽에 보면 있다. 귀찮을까봐 아래 접은 글에 첨부했음(영어 주의)

더보기

:date[format]

The current date and time in UTC. The available formats are:

  • clf for the common log format ("10/Oct/2000:13:55:36 +0000")
  • iso for the common ISO 8601 date time format (2000-10-10T13:55:36.000Z)
  • web for the common RFC 1123 date time format (Tue, 10 Oct 2000 13:55:36 GMT)

If no format is given, then the default is web.

:http-version

The HTTP version of the request.

:method

The HTTP method of the request.

:referrer

The Referrer header of the request. This will use the standard mis-spelled Referer header if exists, otherwise Referrer.

:remote-addr

The remote address of the request. This will use req.ip, otherwise the standard req.connection.remoteAddress value (socket address).

:remote-user

The user authenticated as part of Basic auth for the request.

:req[header]

The given header of the request. If the header is not present, the value will be displayed as "-" in the log.

:res[header]

The given header of the response. If the header is not present, the value will be displayed as "-" in the log.

:response-time[digits]

The time between the request coming into morgan and when the response headers are written, in milliseconds.

The digits argument is a number that specifies the number of digits to include on the number, defaulting to 3, which provides microsecond precision.

:status

The status code of the response.

If the request/response cycle completes before a response was sent to the client (for example, the TCP socket closed prematurely by a client aborting the request), then the status will be empty (displayed as "-" in the log).

:total-time[digits]

The time between the request coming into morgan and when the response has finished being written out to the connection, in milliseconds.

The digits argument is a number that specifies the number of digits to include on the number, defaulting to 3, which provides microsecond precision.

:url

The URL of the request. This will use req.originalUrl if exists, otherwise req.url.

:user-agent

The contents of the User-Agent header of the request.

마지막으로 방금 설정한 morgan함수를 담은 변수를 export 해준다음 서버의 app.js에서 설정해주면 됨


물론 이렇게까지 morgan 모듈을 커스터마이징 할 필요 없이 내장된 커스터마이징 값을 사용해도 된다.

단순하게 서버의 app.js에 아래 코드만 추가해줘도 끝이다. (다른 파일 생성 없어도 됨)

const morgan = require('morgan');
 
app.use(morgan('dev'));

다만, 내가 원하는 인자를 예쁘게 볼 수 없다는 점?

 

그리고 기본으로 제공하는 morgan format은 6가지이다.

- combined 

- common

- default (말그대로 기본 값이라 아무 것도 안 넣으면 얘가 실행되는 걸로 알고 있음)

- short

- tiny

- dev

 

보통 배포시에는 combined 혹은 common에 필요한 정보들을 추가하여 사용하는 것을 추천한다고 한다.

 

아래 코드는 morgan 라이브러리에 설정된 포맷 별 출력되는 값 들이다. 굳이 해석하지말고 참고 정도로만 보면 될 듯

/**
 * Apache combined log format.
 */

morgan.format('combined', ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"')

/**
 * Apache common log format.
 */

morgan.format('common', ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]')

/**
 * Default format.
 */

morgan.format('default', ':remote-addr - :remote-user [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"')
deprecate.property(morgan, 'default', 'default format: use combined format')

/**
 * Short format.
 */

morgan.format('short', ':remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms')

/**
 * Tiny format.
 */

morgan.format('tiny', ':method :url :status :res[content-length] - :response-time ms')

/**
 * dev (colored)
 */

morgan.format('dev', function developmentFormatLine (tokens, req, res) {
  // get the status code if response written
  var status = headersSent(res)
    ? res.statusCode
    : undefined

  // get status color
  var color = status >= 500 ? 31 // red
    : status >= 400 ? 33 // yellow
      : status >= 300 ? 36 // cyan
        : status >= 200 ? 32 // green
          : 0 // no color

  // get colored function
  var fn = developmentFormatLine[color]

  if (!fn) {
    // compile
    fn = developmentFormatLine[color] = compile('\x1b[0m:method :url \x1b[' +
      color + 'm:status\x1b[0m :response-time ms - :res[content-length]\x1b[0m')
  }

  return fn(tokens, req, res)
})

암튼 요거 설정하는데도 꽤나 오래 걸렸고(node 처음 배우던 시절이라 지금보다 더 초보일 때라서 왕 오래걸림..ㅠㅠ)

winston 설정이랑 같이 한다고 한 2일 정도 걸렸던 것 같은데, 해놓고 나니까 개발하면서 너무 편하고 눈이 즐거웠다 ㅋㅋㅋ 서버 부하는 많이 걸릴지언정 나는 개인 프로젝트에 사용한거니까 괜찮지 않을까...

SQL 결과 찍히는 것도 따로 함수를 만들어놔서 이쁘게 출력되는 중이다 ㅋㅋㅋ 암튼 이렇게 눈으로 보면서 개발할 수 있으니 좀 더 편하게 디버깅도 할 수 있었다.

 

morgan 커스터마이징하는 글은 없어서 (내가 못찾은 것일지도...) 이렇게라도 찌그려놓으면 누군가에게 도움되겠지라는 생각으로 또 한시간동안 글을 썼다... 많이 봐주세용.·´¯`(>▂<)´¯`·. 

 

암튼 morgan 커스터마이징 및 포맷 변경해서 미들웨어 설정하는 글 마무리!!

이상한 코드 있으면 댓글부탁드립니다.

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함