Node.js错误追踪方法及服务异常不宕机实现方案咨询
Hey there! 刚部署的Node.js应用突然因为包报错宕机确实头疼,我来给你梳理下具体的解决步骤,分错误追踪和防宕机两部分来说:
一、全面追踪错误的方法
1. 捕获全局未处理的异常与Promise拒绝
Node.js默认会因为未捕获的uncaughtException或unhandledRejection直接崩溃,你可以先给进程绑定监听事件,把错误信息完整记录下来:
// 在应用入口文件顶部添加 const fs = require('fs'); const logError = (err, type) => { const errorLog = `[${new Date().toISOString()}] ${type}: ${err.stack}\n`; fs.appendFileSync('./error.log', errorLog, 'utf8'); console.error(err); }; process.on('uncaughtException', (err) => { logError(err, 'Uncaught Exception'); // 注意:uncaughtException后进程状态不稳定,建议后续交由进程管理器自动重启 }); process.on('unhandledRejection', (reason, promise) => { logError(reason, 'Unhandled Rejection'); console.log('关联Promise:', promise); });
2. 使用专业日志库增强追踪能力
原生日志功能有限,推荐用winston或pino这类库来做分级日志(info/warn/error),还能自动按日期分割日志,方便后期回溯排查:
比如winston的基础配置:
const winston = require('winston'); const logger = winston.createLogger({ level: 'error', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }) ] }); // 开发环境输出到控制台方便调试 if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.simple() })); } // 之后用logger.error(err)统一记录错误
3. 定位具体包的错误
从错误栈信息里找到报错的包路径(比如node_modules/xxx/package/file.js),可以:
- 查看该包的版本,尝试更新到最新稳定版(执行
npm update <package-name>) - 去该包的代码仓库issues里搜类似错误,看有没有官方解决方案或社区补丁
- 临时替换为功能相似的其他包测试,确认是否是该包本身的问题
4. 借助进程管理器查看日志
如果用PM2管理进程,直接执行pm2 logs就能查看实时的应用日志,pm2 show <你的应用名>可以看到崩溃次数、重启记录等详细信息,PM2会自动保存崩溃日志到本地目录,方便后期分析。
二、实现服务器遇错不宕机的方案
1. 用进程管理器自动重启
最省心的方式是用PM2这类进程管理器,它会在进程崩溃时自动重启,还能监控CPU、内存资源使用:
# 全局安装PM2 npm install pm2 -g # 启动应用并命名 pm2 start app.js --name my-node-app # 设置开机自启 pm2 startup pm2 save
2. 框架级全局错误处理(Express/Koa为例)
如果你用Express,一定要加全局错误中间件,捕获路由里的所有错误,避免单个请求的错误导致整个服务器崩溃:
// Express 全局错误中间件(要放在所有路由定义之后) app.use((err, req, res, next) => { logger.error(err.stack); res.status(500).json({ message: '服务器内部错误' }); });
Koa的话可以用try/catch包裹中间件逻辑,或者使用专门的错误处理中间件来捕获异常。
3. 规范Promise与async/await的错误处理
所有Promise必须加.catch(),用async/await时一定要包在try/catch里,避免未处理的Promise拒绝导致进程崩溃:
// 错误示例:未处理Promise拒绝 // fetchData().then(data => console.log(data)); // 正确做法 fetchData() .then(data => console.log(data)) .catch(err => logger.error('Fetch data error:', err)); // async/await的正确写法 async function handleRequest() { try { const data = await fetchData(); console.log(data); } catch (err) { logger.error('Handle request error:', err); } }
4. 隔离易出错的第三方包
如果某个第三方包频繁崩溃,可以把调用它的逻辑放在子进程里,子进程崩溃不会影响主进程,还能单独重启子进程:
const { fork } = require('child_process'); // 子进程文件:child.js // process.on('message', async (task) => { // try { // // 调用易出错的包逻辑 // const result = await riskyPackageFunction(task); // process.send({ success: true, result }); // } catch (err) { // process.send({ success: false, error: err.stack }); // } // }); const child = fork('./child.js'); child.on('message', (response) => { if (response.success) { console.log('子进程返回结果:', response.result); } else { logger.error('子进程执行出错:', response.error); // 重启子进程 child.kill(); // 重新创建子进程 } });
这样一套组合拳下来,既能完整追踪所有错误,又能保证服务器不会因为单个错误直接宕机啦。
内容的提问来源于stack exchange,提问作者Mr.Smith

