NodeJs 日志输出到以每天的日期命名的文件夹中
服务端的日志是非常重要的,可以帮助我们故障排查与问题定位。混乱的日志系统会导致我们花费大量时间去定位问题,查找日志。所以,把日志输出到以每天的日期命名的文件夹中,并把各种日志类型分类,可以大大提高我们的效率。
我们期望把输出成以下的结构:
- logs/
- 2024-04-05/
- error.log
- info.log
- 2024-04-06/
- error.log
- info.log
下面我们以winston为例子说明,先引入Winson和winston-daily-rotate-file
import * as winston from 'winston';
// 这是 Winston 的一个插件,用于实现日志文件的每日轮转,确保日志文件不会无限增长,而是根据日期进行切割和管理。
import 'winston-daily-rotate-file';
1. 定义好日志的类型以及输出的格式
const logTypes = {
INFO: 'info',
ERROR: 'error',
} as const;
type LogTypeUnion = (typeof logTypes)[keyof typeof logTypes];
interface ExtendedLogger extends winston.Logger {
track: (prefix?: string | number, message?: number | string) => void;
}
// 创建日志格式
// 假设type为info, 2024-04-06 12:22:29 [info]: this is log message!
const getBaseFormat = () =>
winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.printf(({ timestamp, message, type }) => {
return `${timestamp} [${type}]: ${message}`;
})
);
2. 创建日志文件的 transports
在 Winston 中,传输器(transports)是用于将日志消息从应用程序中的源传送到目的地的组件。传输器决定了日志消息的最终去向,比如控制台输出、文件存储、数据库存储等。
const logDirectory = 'logs';
// 创建控制台的 transport,用于控制台打印
const getBaseConsole = () =>
new winston.transports.Console({
format: getBaseFormat(),
});
const getBaseTransport = (type: LogTypeUnion) =>
new winston.transports.DailyRotateFile({
dirname: `${logDirectory}/%DATE%`,
filename: `${type}.log`,
// 如果想要测试的话,这边可以改成 YYYY-MM-DD-hh-mm-ss
datePattern: 'YYYY-MM-DD',
});
这样就创建了一个transport,当前是2024-04-06,作用是会把日志输出到logs/2024-04-06/{type}.log
下面,但是现在还不会每天自动更新,只有第一次会创建,因为DailyRotateFile本来是适用于每天更新文件名的,而不是文件夹名,所以我们下面还要改一下。
3. 创建logger
const getBaseLogger = (type: LogTypeUnion) => {
// 获取上面的transport
let baseTransport = getBaseTransport(type);
// 创建一个 Winston 日志记录器实例
const logger = winston.createLogger({
level: 'info',
format: getBaseFormat(),
transports: [baseTransport, getBaseConsole()],
}) as ExtendedLogger;
// 每天需要重新创建 transport,来实现每天新建文件夹,并写入日志
baseTransport.on('rotate', () => {
baseTransport = getBaseTransport(type);
});
// 添加 track 方法
logger.track = (
...arg: [number | string | undefined, number | string | undefined]
) => {
const length = arg.length;
const prefix = length > 1 ? arg[0] : '';
const message = length > 1 ? arg[1] : arg[0] || '';
const formattedMessage = prefix ? `|${prefix}| ${message}` : `${message}`;
logger.log({
level: 'info',
message: formattedMessage,
type,
});
};
return logger as ExtendedLogger;
};
4. 根据type批量生成日志示例
const loggers: Record<LoggerTypeUnion, ExtendedLogger> = Object.values(
logTypes
).reduce(
(acc, type) => ({ ...acc, [`${type}Logger`]: getBaseLogger(type) }),
{} as Record<LoggerTypeUnion, ExtendedLogger>
);
export const { infoLogger, errorLogger } = loggers;
5. 测试一下代码
baseTransport
中的datePattern
改成YYYY-MM-DD-hh-mm-ss
, 然后测试,发现每秒都会生成一个文件夹,测试成功后可以把datePattern
改回去,或者可以拓展成自己想要的时间,比如每分钟命名的文件夹等等。
import { infoLogger, errorLogger } from '.';
async function demo() {
while (true) {
infoLogger.track('Hello World ' + new Date().getSeconds());
errorLogger.track('Hello World ' + new Date().getSeconds());
console.log(new Date().getSeconds());
await new Promise(r => setTimeout(r, 1000));
}
}
demo();
完整代码可以查看我的github。
转载自:https://juejin.cn/post/7353963878541295656