如何用Winston Logger记录Node.js错误?未捕获ReferenceError排查
这个问题我之前也碰到过——Winston默认并不会自动帮你捕获Node.js的全局未处理错误,你的自定义log方法只是主动调用时才会生效,那些跑出代码之外的运行时错误(比如你遇到的ReferenceError: totalValue is not defined)根本没机会触发你的log函数,所以才会出现应用冻结却没日志的情况。
要解决这个问题,你需要手动监听Node.js的两个全局事件,把这些未捕获的错误路由到Winston的日志系统里:
1. 先确认你的Winston Logger配置正确
首先确保你已经创建了一个配置好输出目标(比如文件、控制台)的Winston实例,比如:
const winston = require('winston'); // 创建Winston logger实例 const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ // 输出错误到文件 new winston.transports.File({ filename: 'errors.log', level: 'error' }), // 同时输出到控制台方便调试 new winston.transports.Console({ format: winston.format.simple() }) ] }); // 你的自定义log方法(假设是挂载到全局) global.log = (message) => { logger.info(message); };
2. 监听uncaughtException捕获同步未处理错误
Node.js在遇到未被try/catch捕获的同步错误时,会触发uncaughtException事件,如果不监听这个事件,应用会直接崩溃且不会留下任何日志(除非你用了内置的默认处理,但默认处理也不会写入你的Winston日志)。
添加以下代码:
process.on('uncaughtException', (err) => { // 用Winston记录错误详情 logger.error('【未捕获同步异常】', { error: err.message, stack: err.stack, timestamp: new Date().toISOString() }); // 注意:发生uncaughtException后Node.js的状态可能不稳定,建议退出进程 // 如果是生产环境,可以用PM2等进程管理器自动重启应用 process.exit(1); });
3. 监听unhandledRejection捕获Promise未处理拒绝
如果你的代码里有Promise被拒绝但没有用.catch()或者try/await/catch处理,Node.js会触发unhandledRejection事件,这个事件同样不会被Winston自动捕获,需要手动处理:
process.on('unhandledRejection', (reason, promise) => { logger.error('【未处理Promise拒绝】', { reason: reason instanceof Error ? reason.message : reason, stack: reason instanceof Error ? reason.stack : 'No stack trace available', promise: promise.toString(), timestamp: new Date().toISOString() }); // 同样,建议退出进程以避免不稳定状态 process.exit(1); });
为什么要退出进程?
Node.js官方文档明确指出,在uncaughtException触发后,应用的内部状态可能已经损坏,继续运行可能导致内存泄漏、数据损坏等不可预测的问题。所以最佳实践是记录错误后立即退出,然后用进程管理器(比如PM2)自动重启应用。
额外提示
如果你的应用是基于Express、Koa等Web框架,除了全局事件监听,还要记得在框架层面处理路由错误(比如Express的error-handling middleware),确保请求处理过程中的错误也能被Winston捕获。
这样配置后,下次再出现类似ReferenceError的全局未处理错误时,Winston就会把错误详情写入日志文件,你也能及时发现问题所在,而不是面对一个静默冻结的应用。
内容的提问来源于stack exchange,提问作者kramer65

