import * as winston from 'winston'; import DailyRotateFile from 'winston-daily-rotate-file'; import * as path from 'path'; import * as fs from 'fs'; const logDir = path.join(process.cwd(), 'logs'); // 确保日志目录存在 if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } // 日志格式 const logFormat = winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.splat(), winston.format.printf(({ timestamp, level, message, context, stack }) => { const timestampStr = typeof timestamp === 'string' ? timestamp : String(timestamp); const levelStr = typeof level === 'string' ? level : String(level); const messageStr = typeof message === 'string' ? message : String(message); const contextStr = context ? typeof context === 'string' ? context : JSON.stringify(context) : ''; let stackStr = ''; if (stack) { if (typeof stack === 'string') { stackStr = stack; } else if (typeof stack === 'object' && stack !== null) { stackStr = JSON.stringify(stack); } else { stackStr = String(stack); } } let log = `${timestampStr} [${levelStr}]`; if (contextStr) { log += ` [${contextStr}]`; } log += ` ${messageStr}`; if (stackStr) { log += `\n${stackStr}`; } return log; }), ); // 控制台传输 const consoleTransport = new winston.transports.Console({ format: winston.format.combine(winston.format.colorize(), logFormat), }); // 应用日志传输(按天轮转) const appLogTransport = new DailyRotateFile({ dirname: logDir, filename: 'application-%DATE%.log', datePattern: 'YYYY-MM-DD', maxSize: '20m', maxFiles: '30d', format: logFormat, }); // 错误日志传输(按天轮转) const errorLogTransport = new DailyRotateFile({ dirname: logDir, filename: 'error-%DATE%.log', datePattern: 'YYYY-MM-DD', level: 'error', maxSize: '20m', maxFiles: '30d', format: logFormat, }); // 创建 winston logger 实例 export const winstonLogger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: logFormat, transports: [consoleTransport, appLogTransport, errorLogTransport], exitOnError: false, });