import winston from 'winston'; import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // 确保日志目录存在 const logDir = path.join(__dirname, '../../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.printf(({ level, message, timestamp, stack }) => { if (stack) { return `${timestamp} [${level.toUpperCase()}]: ${message}\n${stack}`; } return `${timestamp} [${level.toUpperCase()}]: ${message}`; }) ); // 创建logger实例 const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: logFormat, transports: [ // 控制台输出 new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), logFormat ) }), // 错误日志文件 new winston.transports.File({ filename: path.join(logDir, 'error.log'), level: 'error', maxsize: 5242880, // 5MB maxFiles: 5 }), // 综合日志文件 new winston.transports.File({ filename: path.join(logDir, 'combined.log'), maxsize: 5242880, // 5MB maxFiles: 5 }) ], // 处理未捕获的异常 exceptionHandlers: [ new winston.transports.File({ filename: path.join(logDir, 'exceptions.log') }) ], // 处理未处理的Promise拒绝 rejectionHandlers: [ new winston.transports.File({ filename: path.join(logDir, 'rejections.log') }) ] }); // 在生产环境中不输出到控制台 if (process.env.NODE_ENV === 'production') { logger.remove(logger.transports[0]); // 移除控制台传输 } // 添加请求日志中间件 export const requestLogger = (req, res, next) => { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; const logData = { method: req.method, url: req.url, status: res.statusCode, duration: `${duration}ms`, ip: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent') }; if (res.statusCode >= 400) { logger.warn(`HTTP ${res.statusCode} - ${JSON.stringify(logData)}`); } else { logger.info(`HTTP ${res.statusCode} - ${JSON.stringify(logData)}`); } }); next(); }; export default logger;