Winston日志按月份自动切换目录问题求助
解决Winston日志按月自动切换目录的问题
当前问题
我的日志目录结构如下:
- logs
- 2023-07
- info-2023-07-20.log
- info-2023-07-21.log
- 2023-07
logs文件夹位于项目根目录,使用Winston结合winston-daily-rotate-file实现了日志按天自动切换,但无法自动按月生成新目录——必须重启服务器才能创建当月的日志目录,否则日志会一直追加到旧月份的目录中。
当前的logger.js配置代码如下:
const fs = require('fs') const winston = require('winston') const { format, transports } = winston const { colorize, combine, timestamp, printf, splat, errors } = format require('winston-daily-rotate-file') if (!fs.existsSync('logs/')) { fs.mkdirSync('logs/') } const todayDate = new Date() const currentMonth = ('0' + (todayDate.getMonth() + 1)).slice(-2) const logDir = `logs/${todayDate.getFullYear()}-${currentMonth}` // Directory path for logs. // month + 1 is because first month starts with 0 if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir) } const logFormat = combine( timestamp({ format: 'HH:mm:ss' }), splat(), errors({ stack: true }), printf(({ timestamp, level, message, stack }) => { return `${timestamp},${global.isBackgroundServer ? ` [BG]:` : ''}${ process.env.NODE_ENV === 'development' ? ` ${level}:` : '' } ${typeof message === 'object' ? JSON.stringify(message) : message}${stack ? `, Stack: ${stack}` : ''}` }), ) const defaultLoggerOptions = { format: logFormat, } const infoTransport = new transports.DailyRotateFile({ ...defaultLoggerOptions, level: 'info', filename: logDir + '/Info-%DATE%.log', }) const errorTransport = new transports.DailyRotateFile({ ...defaultLoggerOptions, level: 'error', filename: logDir + '/Error-%DATE%.log', }) const warningTransport = new transports.DailyRotateFile({ ...defaultLoggerOptions, level: 'debug', filename: logDir + '/Warning-%DATE%.log', }) const consoleTransport = new transports.Console({ format: combine(colorize(), logFormat), }) const errorLogger = winston.createLogger({ transports: [consoleTransport, errorTransport], }) const successLogger = winston.createLogger({ transports: [consoleTransport, infoTransport], }) const warningLogger = winston.createLogger({ transports: [consoleTransport, warningTransport], }) const transportConsoleToWinston = () => { const getArgumentsStr = (arguments) => { return Array.prototype.slice.call(arguments).join(' ') } const wrappedSuccessLogger = (...args) => successLogger.log.apply(successLogger, ['info', getArgumentsStr(args)]) const wrappedWarningLogger = (...args) => warningLogger.log.apply(warningLogger, ['warn', getArgumentsStr(args)]) const wrappedDebugLogger = (...args) => warningLogger.log.apply(warningLogger, ['debug', getArgumentsStr(args)]) console.log = wrappedSuccessLogger console.info = wrappedSuccessLogger console.warn = wrappedWarningLogger console.debug = wrappedDebugLogger console.error = (...args) => errorLogger.error.call(errorLogger, ...args) } module.exports = { errorLogger, successLogger, warningLogger, transportConsoleToWinston }
问题根源
当前代码在启动时就计算好了固定的logDir,之后所有日志都会写入这个目录,即使月份变更也不会重新计算目录路径,导致必须重启服务才能更新目录。
解决方案
通过让winston-daily-rotate-file的dirname属性使用动态函数,每次生成新日志文件时自动计算当前月份的目录并创建(如果不存在),实现无需重启服务即可自动切换月份目录。
修改后的完整代码
const fs = require('fs') const winston = require('winston') const { format, transports } = winston const { colorize, combine, timestamp, printf, splat, errors } = format require('winston-daily-rotate-file') // 确保根日志目录存在 if (!fs.existsSync('logs/')) { fs.mkdirSync('logs/') } const logFormat = combine( timestamp({ format: 'HH:mm:ss' }), splat(), errors({ stack: true }), printf(({ timestamp, level, message, stack }) => { return `${timestamp},${global.isBackgroundServer ? ` [BG]:` : ''}${ process.env.NODE_ENV === 'development' ? ` ${level}:` : '' } ${typeof message === 'object' ? JSON.stringify(message) : message}${stack ? `, Stack: ${stack}` : ''}` }), ) const defaultLoggerOptions = { format: logFormat, datePattern: 'YYYY-MM-DD', // 明确指定日期格式 } // 生成动态月份目录的工具函数 const getCurrentMonthDir = () => { const today = new Date() const year = today.getFullYear() const month = ('0' + (today.getMonth() + 1)).slice(-2) const dir = `logs/${year}-${month}` // 递归创建目录(确保父目录存在) if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }) } return dir } const infoTransport = new transports.DailyRotateFile({ ...defaultLoggerOptions, level: 'info', filename: 'Info-%DATE%.log', dirname: getCurrentMonthDir, // 动态获取目录 }) const errorTransport = new transports.DailyRotateFile({ ...defaultLoggerOptions, level: 'error', filename: 'Error-%DATE%.log', dirname: getCurrentMonthDir, }) const warningTransport = new transports.DailyRotateFile({ ...defaultLoggerOptions, level: 'debug', filename: 'Warning-%DATE%.log', dirname: getCurrentMonthDir, }) const consoleTransport = new transports.Console({ format: combine(colorize(), logFormat), }) const errorLogger = winston.createLogger({ transports: [consoleTransport, errorTransport], }) const successLogger = winston.createLogger({ transports: [consoleTransport, infoTransport], }) const warningLogger = winston.createLogger({ transports: [consoleTransport, warningTransport], }) const transportConsoleToWinston = () => { const getArgumentsStr = (arguments) => { return Array.prototype.slice.call(arguments).join(' ') } const wrappedSuccessLogger = (...args) => successLogger.log.apply(successLogger, ['info', getArgumentsStr(args)]) const wrappedWarningLogger = (...args) => warningLogger.log.apply(warningLogger, ['warn', getArgumentsStr(args)]) const wrappedDebugLogger = (...args) => warningLogger.log.apply(warningLogger, ['debug', getArgumentsStr(args)]) console.log = wrappedSuccessLogger console.info = wrappedSuccessLogger console.warn = wrappedWarningLogger console.debug = wrappedDebugLogger console.error = (...args) => errorLogger.error.call(errorLogger, ...args) } module.exports = { errorLogger, successLogger, warningLogger, transportConsoleToWinston }
关键修改点
- 移除了启动时固定计算
logDir和创建月份目录的代码,改为通过getCurrentMonthDir函数动态生成目录 - 为每个
DailyRotateFile配置添加dirname: getCurrentMonthDir,每次生成新日志文件时都会调用该函数获取当前月份的目录 - 使用
fs.mkdirSync(dir, { recursive: true })确保目录不存在时自动创建,无需担心父目录缺失 - 显式指定
datePattern: 'YYYY-MM-DD',让日志文件名的日期格式更清晰
这样配置后,当月份变更时,Winston会自动创建新的月份目录并将日志写入其中,无需重启服务器。
内容的提问来源于stack exchange,提问作者Juhil Somaiya
相关产品推荐
相关产品推荐

