Node.js Morgan, Winston 사용해보기
2020-04-12 18:12:27

winston을 조금만 설정 하면 express 내 로그를 편하게 남길 수 있다.

로그 작성

app, http 로그를 처리하기 위한 모듈로 logger라는 파일을 생성 한다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const { createLogger, format, transports } = require('winston');
const { combine, label, printf } = format;
const path = require('path');
const mt = require('moment-timezone');

const date = mt().tz('Asia/Seoul'); // NOTE: 날짜는 한국 시간으로 하고 싶다.
const myFormat = printf(info => `${info.timestamp} [${info.level}]: ${info.label} - ${info.message}`); // NOTE: 로그 형식 설정
const koreaTime = format((info) => { // NOTE: 한국 시간으로 하기 위해.. 설정을 안 할 시 에는 UTC 0이 default다.
info.timestamp = date.format();
return info;
});

const logType = { // 걍 만들어본 서비스 enum
1: 'join',
2: 'login',
3: 'spend_item',
4: 'system',
};

const appLogger = (type) => { // NOTE: application log를 남기기 위함.
const init = createLogger({
format: combine(
label({ label: logType[type] }), // NOTE: 어떤 서비스인지 알기 위함
koreaTime(),
myFormat,
),
transports: [
new transports.File({ filename: path.join(__dirname, 'logs', 'app-error.log'), level: 'error' }), // NOTE: 에러는 별도로 보기 위함
new transports.File({ filename: path.join(__dirname, `logs`, date.format('YYYY-MM-DD'), 'app.log') }), // NOTE: 모든 로그 (에러 포함)
],
});
if (process.env.NODE_ENV !== 'production') { // NOTE: 실제 서비스 환경이 아닐 시에는 출력을 해야 바로 바로 보기 편함.
init.add(new transports.Console());
}
return init;
};

const httpLogger = createLogger({ // NOTE: http status 로그를 남기기 위함.
format: combine(
label({ label: 'http' }),
koreaTime(),
myFormat,
),
transports: [
new transports.File({ filename: path.join(__dirname, 'logs', date.format('YYYY-MM-DD'), 'http.log') }),
],
});

const httpLogStream = {
write: (message) => { // NOTE: morgan에서 쓰기 위해 이 형태로 fix 되야함.
httpLogger.log({
level: 'info',
message: message,
});
},
};

exports.appLogger = appLogger;
exports.httpLogStream = httpLogStream;

express 테스트

예제 express를 만든다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

const express = require('express');
const morgan = require("morgan");
const logger = require('./logger');
const app = express();
const PORT = process.env.PORT || 3000;

const morganFormat = process.env.NODE_ENV !== "production" ? "dev" : "combined"; // NOTE: morgan 출력 형태

app.use(morgan(morganFormat, { stream: logger.httpLogStream })); // NOTE: http request 로그 남기기

app.get('/', (req, res) => {
res.send('hihi');
});

app.get('/set-item', (req, res) => {
const { id, name, item_id } = req.query;
if (typeof id === 'undefined' || typeof name === 'undefined' || typeof item_id === 'undefined') {
logger.appLogger(3).log({
level: 'warn',
message: '파라미터 누락',
});
return res.status(200).json({ statusCode: 403, message: '파라미터 누락' });
}
if (typeof item_id !== 'string' || !item_id.includes('item_')) {
logger.appLogger(3).log({
level: 'error',
message: '잘못된 아이템 타입',
});
return res.status(200).json({ statusCode: 500, message: '잘못된 아이템 타입' });
}
logger.appLogger(3).log({
level: 'info',
message: `user_id: ${id}, name: ${name}, 획득 아이템: ${item_id}`,
});
return res.status(200).json({ statusCode: 200, message: '처리 성공' });
});

app.use((err, req, res, next) => {
logger.appLogger(4).log({
level: 'error',
message: err.stack,
});
res.status(500).json({ statusCode: 500, message: err.message });
});

app.listen(PORT, () => console.log(`app listening on port ${PORT}`));

http request의 상태를 알기 위해선 morgan 라이브러리를 이용 한다.

앱의 로그는 간단하다. 로깅 레벨을 적어주고 출력할 메세지를 작성하면 된다.

로깅의 레벨은 error, warn, info, debug, verbose, silly가 잇는데 상황에 맞게 쓰자.

결과

생성 폴더 리스트

실행 후 테스트를 해 보면 위와 같은 폴더가 생긴다 아래는 파일의 내용.

app.log

1
2
3
2020-04-12T17:47:41+09:00 [warn]: spend_item - 파라미터 누락
2020-04-12T17:47:41+09:00 [error]: spend_item - 잘못된 아이템 타입
2020-04-12T17:47:41+09:00 [info]: spend_item - user_id: abc, name: 123, 획득 아이템: item_123

http.log

1
2
3
4
5
6
7
2020-04-12T17:47:41+09:00 [info]: http - GET /eqwe 404 5.140 ms - 143

2020-04-12T17:47:41+09:00 [info]: http - GET /set-item?id=abc&name=123 200 541.433 ms - 50

2020-04-12T17:47:41+09:00 [info]: http - GET /set-item?id=abc&name=123&item_id=123 200 4.376 ms - 57

2020-04-12T17:47:41+09:00 [info]: http - GET /set-item?id=abc&name=123&item_id=item_123 304 2.525 ms - -

app-error.log

1
2020-04-12T17:47:41+09:00 [error]: spend_item - 잘못된 아이템 타입