항상 console.log에 익숙해져 있었다. 사실 혼자서 무언가를 만들다 보면 (대부분 작은 규모이다 보니) 콘솔에서만 찍어보고 직접 남겨본 적이 없었다. 하지만 대형 프로젝트에 참여하게 되면서 logging에 대해 배우게 되었다. 큰 프로젝트이다 보니 왜 로그를 남기는지 조금은 알 것 같았다. JS에서 가장 많이 쓰이는 logging 모듈인 winston에 대해서 글을 작성해보았다.
Winston 설치하기
현재(20년 06월) 기준으로 winston의 가장 최신 버전은 3.2.1이다. 버전 3이 나오면서 버전 2와 다른 점이 많으니, 처음 시작하는 사람들이라면 3 이상 버전을 설치하는 걸 추천한다. (2.@ 버전도 사용해봤는데 공식 문서가 있음에도 불구하고 잘 안 되는 것들이 몇 가지 있었다.)
현재 나는 typescript로 개발을 하고있기때문에 타입까지 설치해주었다. 만약 js로 개발 중인데 이 글을 읽고 있다면 @types/winston은 설치하지 않아도 된다.
// for typescript
npm install winston @types/winston
// for javascript
npm install winston
Logger 생성하기
새로운 파일을 하나 생성해보자. 해당 파일에서는 어떤 식으로 로그를 남길지에 대한 logger를 정의하는 곳이라고 생각하면 된다.
Console에 로그 남기기
몇 가지 옵션을 통해 내가 원하는 대로 로그를 남길 수 있다.
level | 어느 로그 레벨까지 기록할 것인가? |
format | 로그의 형태는 어떻게 할 것인지 정의할 수 있다. |
// logger.ts
import { createLogger, transports, format } from 'winston';
interface TransformableInfo {
level: string;
message: string;
[key: string]: any;
}
const logger = createLogger({
transports: [
new transports.Console({
level: 'debug',
format: format.combine(
format.label({ label: '[my-server]' }),
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
format.colorize(),
format.printf((info: TransformableInfo) => `${info.timestamp} - ${info.level}: ${info.label} ${info.message}`),
)
})
]
});
- label: winston에서는 여러 로그를 생성할 수 있다. 이때 어떤 logger에서 생성된 로그인지 확인하기 위해 label을 사용할 수 있다.
- timestamp: 로그 발생 시점을 알려준다. 이 또한 원하는 대로 형식을 지정할 수 있다.
- colorize: winston에서는 로그 레벨별로 색깔이 다르게 지정되어있다. (또한, 이 색상들은 사용자가 직접 커스텀할 수 있다. ) 로그 메시지의 형태에 맞게 레벨을 설정하고 한눈에 보기 쉽게 색깔도 표시해주면 로그를 좀 더 유용하게 쓸 수 있다.
- printf: 내가 원하는 형태로 출력할 수 있다.
logger.info('Ready to start SERVER');
logger.debug(`let's debug something here!`);
위에서 생성한 logger를 통해 직접 로그를 찍어보면 아래와 같은 결과를 보여준다. 형식, 색상, 라벨, 타임스탬프 모두 내가 정의한 대로다.
File에 로그 남기기
File에서 가장 중요한 점은 filename이다.
filename | 로그 파일이 저장될 경로 |
// logger.ts
import * as winston from 'winston';
const logger = winston.createLogger({
transports: [
...,
new transports.Console({
level: 'debug',
format: format.combine(
format.label({ label: '[my-server]' }),
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
format.colorize(),
format.printf((info: TransformableInfo) => `${info.timestamp} - ${info.level}: ${info.label} ${info.message}`),
)
})
]
export default logger;
- filename: process.cwd()는 현재 프로세스가 생성되는 위치를 알려준다. 경로를 정확하게 적어줘야 로그 파일이 생성된다. Mac에서는 폴더가 존재하지 않을 경우, 직접 폴더를 생성하고 로그 파일을 생성해줬는데 Windows에서는 오류를 뱉었던 것 같다. Windows를 2.x 버전에서 실행해서 나타나는 문제인지.. 는 모르겠다. 그래서 직접 폴더를 생성했던 기억이 난다.)
Logger 레벨, 색상 커스텀하기
winston 공식 문서를 보면 내 마음대로 레벨과 색상을 커스텀할 수 있다고 소개하고 있다.
기본값으로 지정되어있는 레벨과 색상은 다음과 같다.
const levels = {
error: 0, // 'red'
warn: 1, // 'yellow'
info: 2, // 'green'
http: 3, // 'green'
verbose: 4, // 'cyan'
debug: 5, // 'blue'
silly: 6 // 'magenta'
};
레벨은 내가 원하는 대로 바꿀 수 있으며, 색상은 다음과 같다.
black, red, green, yellow, blue, magenta, cyan, white, gray, grey
직접 만들어보자. winston을 typescript에서 사용하는 사람들은 README에 적힌 대로 하다 보면 오류가 날 것이다.
// logger.ts
import winston, { Logger, createLogger, transports, format, config } from 'winston';
// 직접 정의한 로그 레벨
const customLevels: config.AbstractConfigSetLevels = {
customedError: 0,
customedWarn: 1,
customedInfo: 2,
customedDebug: 3,
customedSilly: 4
}
// 레벨별 색상 * 주어지지않은 색상을 넣을 경우 오류 발생
const customColors: config.AbstractConfigSetColors = {
customedError: 'red',
customedWarn: 'yellow',
customedInfo: 'cyan',
customedDebug: 'magenta',
customedSilly: 'gray'
}
// 색상을 추가하고싶다면 winston에게 이를 알려야한다. (README 참고)
winston.addColors(customColors);
interface CustomLevels extends winston.Logger {
customedError: winston.LeveledLogMethod;
customedWarn: winston.LeveledLogMethod;
customedInfo: winston.LeveledLogMethod;
customedDebug: winston.LeveledLogMethod;
customedSilly: winston.LeveledLogMethod;
}
export const customLogger: CustomLevels = <CustomLevels>createLogger({
levels: customLevels,
format: format.combine(
format.label({ label: '[customed-server]' }),
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
format.colorize(), // 색상을 보고싶다면 꼭 추가!
format.printf((info: TransformableInfo) => `${info.timestamp} - ${info.level}: ${info.label} ${info.message}`),
),
transports: [
new transports.Console({ level: 'customedSilly' })
]
});
이와 같이 정의한 후, 아까 위에서 생성한 logger와 지금 생성한 customLogger가 제대로 작동하는지 확인해보자.
// app.ts (서버가 시작되는 곳)
logger.info('Ready to start SERVER');
logger.debug(`let's debug something here!`);
customLogger.customedError('level called customedError');
customLogger.customedWarn('level called customedWarn');
customLogger.customedInfo('level called customedInfo');
customLogger.customedDebug('level called customedDebug');
customLogger.customedSilly('level called customedSilly');
마치며... 👩🏻💻
이 글에서는 winston의 아주 기본적인 사항들만 다루고 있다. 이 외에도 로그 파일 여러 개 생성하기, 로그 쿼리 하기, 메타 데이터 추가하기, 에러 핸들링 등 다양한 기능들이 있다. 아래에 참고할만한 자료들을 첨부해두었으니 직접 공식 문서를 보면서 만들어나가면 좋을 것 같다. 시간이 된다면 좀 더 심화적인 내용들에 대해서도 글을 작성해봐야겠다.
한 두 개가 아닌 여러 개의 서로 다른 마이크로 서비스로 구축되어있는 환경에서 작업을 하다 보니 에러가 발생했을 때, 가장 최초로 발생한 지점을 찾는 게 쉽지 않다. 이때 필요한 게 각 서비스마다 구축된 로깅 및 모니터링 시스템이었다. 처음 로깅에 대해 접했을 때, 모든 지점에서 다 로그를 찍었었다. 하지만 모든 로그가 반드시 필요한 정보 일리는 없으니.. 점차 배워나가면서 터득해봐야겠다.
로그 시스템에 익숙해져 보자!
참고 자료
'Programming > 일반' 카테고리의 다른 글
[Node.js][Log] morgan으로 http request 로그를 남겨보자 (0) | 2020.06.29 |
---|---|
[Go] mac에서 go 시작하기 (0) | 2020.04.05 |