서비스를 개발할때는 로그를 남기는것이 필수다.
로그를 남기지 않으면 운영중인 서비스에서 문제가 생겼을때, 원인을 찾고 해결하기가 매우 어려워진다.
나는 프론트엔드 개발자이지만, 아무도 로그를 고려하지 않는다...
그래서 내가 직접한다.. 그 과정에서 알게된 것들을 기록해두었다.
winston, winston-daily-rotate-file 패키지 설치
npm install winston winston-daily-rotate-file
winston.js
파일을 생성한다. (이름은 개발자 마음대로..^^)winston.createLogger()
에 로그 설정들을 작성해서 넘겨준다.format
: 타임스탬프나 로그 포맷을 결정할 수 있다.transports
: logging targets를 설정할 수 있다.const winston = require('winston');
const winstonDaily = require('winston-daily-rotate-file');
const { combine, timestamp, printf, colorize } = winston.format;
const logDir = 'logs'; // 'logs' 디렉토리
// 로거 생성
const logger = winston.createLogger({
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}), // 로그 파일에 시간이 이런 형태로 기록됨
printf(info => { // 데이터를 어떤 형태로 받을지 직접 커스텀 할 수 있음
return `${info.timestamp} ${info.level}: ${info.message}`
}),
),
transports: [
// 로그 파일에 찍힐 내용을 콘솔에도 찍어줌
new winston.transports.Console({
format: winston.format.combine(
winston.format.splat(),
winston.format.colorize(),
// winston.format.simple() // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷
)
}),
new winstonDaily({
level: 'info', // info 레벨 로그를 저장할 파일 설정
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
filename: `%DATE%.log`, // %DATE% = 위에서 설정한 datePattern 이 들어감
dirname: logDir,
maxFiles: 30, // 30일치 로그 파일 저장
}),
new winstonDaily({
level: 'error', // error 레벨 로그를 저장할 파일 설정
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
filename: `%DATE%.error.log`,
dirname: logDir + '/error', // error.log 파일은 /logs/error 하위에 저장
maxFiles: 30,
}),
],
});
module.exports = { logger };
환경변수에 따라 분기처리를 해주고,
logger.add(new Winston.transports.Console
... 로 새로운 로거를 추가해주면 된다.
if (process.env.NODE_ENV !== 'PROD') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(), // 색깔 넣어서 출력
winston.format.simple(), // `${info.level}: ${info.message} JSON.stringify({ ...rest })` 포맷으로 출력됨
)
}));
}
format 옵션의 값으로 winston.format.simple()
을 넘겨주면, 로그가 ${info.level}: ${info.message} JSON.stringify({ ...rest })
포맷으로 출력 되기 때문에 따로 값을 설정하지 않는 게 좋겠다...!!
log( ) 또는 각 레벨 함수로 로그를 남길 수 있다.
해당 함수에는 message를 인자로 넘겨줄 수 있는데, 이 인자는 info객체의 message로 들어간다. createLogger 참고,,
logger.info(message) 또는 logger.log("info", message)
logger.warn(message) 또는 logger.warn("info", message)
logger.error(message) 또는 logger.error("info", message)
...
이 함수를 로그를 남기고자 하는 액션이 작성된 곳에 추가해준다.
아래 예시는 로그인이 성공하는 경우에 로그를 남긴다.
logger.info()
에 인자로 객체를 넘겨주어, 유저아이디/타입(로그인)/아이피주소 를 같이 넘겨주었다.const commonModel = require("../../models/Common/commonModel");
const { logger } = require("../../winston");
const requestIP = require('request-ip');
const commonController = {
login : async (req, res) => {
const response = await commonModel.login(req.body);
if (response.ErrorCode === "OK") {
const userIP = requestIP.getClientIp(req);
logger.info( {id:req.body.id, ip:userIP, type:"로그인"} );
}
return res.json(response);
},
}
module.exports = commonController;
그리고 YYYY-MM-DD hh:mm:ss info [로그인] 사용자id (IP주소) 이런 형태로 로그를 남기고 싶어서 아래와 같이 작성 했다.
...
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
printf(info => {
return `${info.timestamp} ${info.level}: [${info.message.type}] ${info.message.id} (${info.message.ip})`
}),
),
...
내가 만든 서비스에도 많은 유저가 생겼으면 좋겠다,, 😂