winston에 대해 알아보자.
winston 모듈
winston은 로그를 효율적으로 관리할 수 있게 도와주는 모듈로 서버를 운영할 때 console.log
와 console.error
를 대체하기 위한 모듈입니다.
쯕, Log를 파일에 저장해서 관리하기 위해 winston을 사용합니다.
winston-datily-rotate-file 모듈
winston-datily-rotate-file은 날짜별로 로그 파일을 생성하고 자동 삭제하는 등 파일 관리 모듈입니다.
서버가 종료되는 순간 쌓여있던 로그를 삭제하지않고 파일에 저장해서 관리합니다.
winston.format
format은 로그의 형식을 지정할 수 있습니다.
json
, label
, timestamp
, printf
, simple
, combine
등의 다양한 형식으로 지정이 가능합니다.
combine
은 여러 형식을 혼합해서 사용할 때 사용합니다.
- timestamp : 날짜 형식 지정
- label : 어플리케이션 이름 지정
로그 경로 & 출력 형식 설정
winston.format
에서 필요한 프로퍼티들을 꺼내옵니다.
로그 파일을 저장할 경로를 설정해줍니다.
logs폴더를 만들어 설정해주겠습니다.
logFormat
변수에 로그 출력 포맷을 설정해줍니다.
timestamp [label] level: message
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import winston from "winston";
import winstonDaily from "winston-daily-rotate-file";
let dirname = import.meta.dirname;
const { combine, timestamp, label, colorize, printf } = winston.format;
// 로그 파일 저장 경로
const logDir = `${dirname}/logs`;
// 로그 출력 포맷 정의 함수
const logFormat = printf(({ level, message, label, timestamp }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
// 날짜 [라벨] 레벨 메세지
});
winston 로거 생성 (createLogger)
로그 기록(Logging)을 하는 메서드를 생성합니다.
winston.createLogger
메서드를 사용하며 format에 로그의 형식을 설정합니다.
보통 JSON 형식이나 combine을 통해 설정합니다.
1
2
3
4
5
6
7
8
9
const logger = winston.createLogger({
// 로그 출력 형식 정의
format: combine(
timestamp({ format: "YYYY-MM-DD HH:MM:SS || " }),
label({ label: "Project - 01" }), // 어플리케이션 이름
logFormat // log 출력 포맷
// format: combine() 에서 정의한 timestamp와 label 형식값이 logFormat에 들어가서 정의되게 된다. level이나 message는 콘솔에서 자동 정의
),
});
winston 로그 저장 방식 설정 (transports)
transports는 로그 저장 방식을 설정합니다.
winston-daily-rotate-file 모듈
에서 불러온 winstonDaily 생성자 함수를 사용해 어떤 레벨의 로그를 어떤 형식으로 저장할 지 등의 상세 설정이 가능합니다.
로그 레벨
error : 0, warn : 1, info : 2, http : 3, verbose : 4, debug : 5, silly : 6
숫자가 낮을 수록 위험도가 높습니다.
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
const logger = winston.createLogger({
//* 로그 출력 형식 정의
format: combine(),
// ...
transports: [
// 레벨을 설정하면 해당 레벨보다 위험도가 높은 로그가 모두 출력됩니다.
// info 레벨 로그
new winstonDaily({
level: "info", // info 레벨
datePattern: "YYYY-MM-DD", // 파일 날짜 형식
dirname: logDir, // 파일 경로
filename: `%DATE%.log`, // 파일 이름
maxFiles: 14, // 최근 14일치 저장
zippedArchive: true, // gzip으로 압축할지 여부
}),
// error 레벨 로그
new winstonDaily({
level: "error",
datePattern: "YYYY-MM-DD",
dirname: logDir + "/error", // /logs/error 하위에 저장
filename: `%DATE%.error.log`, // 에러 로그는 2022-12-28.error.log 형식으로 저장
maxFiles: 14,
zippedArchive: true,
}),
],
});
winstonDaily 로깅 설정
frequency : 회전 빈도를 나타내는 문자열 (default : null)
유효한 값은 ‘#m’ 또는 ‘#h’(예 : ‘5m’ 또는 ‘3h’)datePattern : 회전에 사용할 moment.js 날짜 형식 을 나타내는 문자열 (default : ‘YYYY-MM-DD’)
zippedArchive : 아카이브된 로그 파일을 gzip으로 압축할지 여부 (default : ‘거짓’)
filename : 로그에 사용할 파일 이름 (default : ‘winston.log.%DATE%’)
이 파일 이름은 파일 이름의 %DATE%를 포함할 수 있습니다.dirname : 로그 파일을 저장할 디렉터리 이름 (기본: ‘.’)
stream : 사용자 지정 스트림에 직접 쓰고 회전 기능을 우회 (default : null)
maxSize : 회전할 파일의 최대 크기 (default : null)
maxFiles : 보관할 최대 로그 수 (default : null)
auditFile : 감사 파일의 이름을 나타내는 문자열 (default : ‘..json’)
옵션 개체의 해시를 계산하여 생성된 기본 파일 이름을 재정의하는 데 사용할 수 있습니다.utc : 파일 이름의 날짜에 UTC 시간 (default : false)
extension : 파일 이름에 추가할 파일 확장자 (default : ‘’)
createSymlink : 현재 활성 로그 파일에 대한 tailable symlink (default : false)
symlinkName : tailable symlink의 이름 (default : ‘current.log’)
options : 파일 스트림에 전달되어야 하는 추가 옵션 (default : { flags: ‘a’ })
winston 예외 로그 처리 (exceptionHandlers)
추가로 코드 에러 뿐만 아니라 try catch 에러도 잡기 위해 밑에 exceptionHandlers
를 설정해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const logger = winston.createLogger({
//_ 로그 출력 형식 정의
format: combine(),
// ...
//_ 실제 로그를 어떻게 기록을 한 것인가 정의
transports: [
new winstonDaily({
// ...
}),
],
//\* uncaughtException 발생시 파일 설정
exceptionHandlers: [
new winstonDaily({
level: "error",
datePattern: "YYYY-MM-DD",
dirname: logDir + "/exception",
filename: `%DATE%.exception.log`,
maxFiles: 14,
zippedArchive: true,
}),
],
});
morgan과 winston 결합해서 사용하기
http 요청과 응답 로그를 남겨주는 morgan을 winston으로 관리해 해당 로그를 파일로 관리해보겠습니다.
먼저 morgan.js
파일에 사용할 morgan 커스텀을 정의합니다.
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
// morgan.js
import morgan from "morgan";
import logger from "../config/winston.js";
// 로그 작성을 위한 옵션.
const format =
process.env.NODE_ENV === "production"
? "combined"
: "[:date] :method :url :status :response-time ms";
// 로깅 스킵 여부
const skip = (_, res) => {
if (process.env.NODE_ENV === "production") {
return res.statusCode < 400; // 코드가 400 이상이면 로그 기록
}
return false;
};
// 로그 작성을 위한 Output stream옵션.
const stream = {
write: (message) => {
// logger.info(message);
logger.info(
message.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
""
)
);
},
};
const options = {
skip,
stream,
};
// 적용될 moran 미들웨어 형태
const morganMiddleware = morgan(format, options);
export default morganMiddleware;
이제 서버 파일인 app.js
에 morgan.js
를 불러와 미들웨어로 적용해줍니다.
1
2
3
4
5
6
//app.js
import morganMiddleware from "./src/middleware/morgan.js";
// ...
app.use(morganMiddleware);