温馨提示×

Debian Node.js日志中的异常处理策略

小樊
39
2025-12-22 07:21:18
栏目: 编程语言

Debian Node.js 日志中的异常处理策略

一 分层捕获与全局兜底

  • 同步代码使用 try-catch 包裹可预见错误,避免进程崩溃与堆栈丢失。
  • 异步代码优先使用 Promise + .catch()async/await 的 try-catch;对事件流与回调,务必监听 error 事件
  • 设置全局兜底:监听 process.on(‘uncaughtException’)process.on(‘unhandledRejection’),在记录结构化日志后进行必要的资源清理,并安全退出(如 process.exit(1)),防止处于不一致状态继续运行。
  • Web 框架(如 Express)使用统一的错误处理中间件,集中捕获路由与中间件抛出的错误,并返回一致的错误响应。

二 日志采集与结构化输出

  • 避免使用仅 console 的方式输出生产日志,采用专业日志库(如 Winston、Pino、log4js)以获得日志级别、多目标输出与格式化能力。
  • 统一使用结构化日志(JSON),在日志中携带关键元数据:如 timestamp、level、message、pid、hostname、requestId、stack 等,便于检索、聚合与链路追踪。
  • 区分日志级别(如 debug/info/warn/error/fatal),生产环境合理配置级别,错误与告警单独落盘。
  • 示例(Winston,生产建议写入文件并区分 error/combined):
    • 参考实践:使用 Winston/Pino 等库进行分级、多目标与结构化输出;为错误单独配置 transport。

三 运行环境与系统侧保障

  • 权限与路径:确保日志目录存在且权限正确,例如创建 /var/log/myapp 并以应用用户运行;避免因权限不足导致写入失败。
  • 日志轮转:使用 logrotate 管理日志大小与保留周期,防止磁盘被撑满。示例配置:
    • /var/log/myapp/*.log { daily; missingok; rotate 7; compress; notifempty; create 0640 nodeuser nodegroup }
  • 进程管理:使用 PM2 等进程管理工具时,核对日志路径与权限配置,确保异常时日志能正常落盘与轮转。

四 监控告警与问题定位

  • 异常监控与告警:接入 Sentry、Fundebug 等 APM/异常监控服务,结合 日志级别 与错误堆栈实现主动告警与快速定位。
  • 请求链路与访问日志:在 Express 中使用 morgan 记录请求日志(如方法、路径、状态码、响应时间),配合异常中间件输出错误请求上下文。
  • 集中式日志:将 JSON 日志汇入 ELK(Elasticsearch/Logstash/Kibana) 或兼容方案,实现统一检索、可视化与告警。
  • 诊断报告:在 Node.js v14+ 启用 Diagnostic Report 生成包含 JS 与本机堆栈、堆统计等信息的诊断文件,辅助疑难问题定位。

五 最小落地示例

  • 说明:以下示例演示结构化日志、全局兜底与 Express 错误处理的最小组合,生产环境可按需扩展(如增加日志轮转、APM、集中式日志等)。

  • 代码示例

    • 安装依赖:npm i express morgan winston
    • 文件:app.js
      const express = require('express');
      const morgan = require('morgan');
      const winston = require('winston');
      
      // 结构化日志:error 单独落盘,combined 全量
      const logger = winston.createLogger({
        level: 'info',
        format: winston.format.combine(
          winston.format.timestamp(),
          winston.format.errors({ stack: true }),
          winston.format.json()
        ),
        transports: [
          new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
          new winston.transports.File({ filename: 'logs/combined.log' })
        ]
      });
      
      const app = express();
      app.use(morgan('combined')); // 访问日志
      
      // 业务路由
      app.get('/error', () => { throw new Error('boom'); });
      
      // 统一错误处理中间件
      app.use((err, req, res, next) => {
        logger.error({ err, req }, 'unhandled error');
        res.status(500).json({ error: 'Internal Server Error' });
      });
      
      // 全局兜底
      process.on('uncaughtException', (err) => {
        logger.error({ err }, 'uncaughtException, exiting');
        process.exit(1);
      });
      process.on('unhandledRejection', (reason) => {
        logger.error({ reason }, 'unhandledRejection');
      });
      
      app.listen(3000, () => logger.info('server listening', { port: 3000 }));
      
  • 运行与验证

    • 准备目录:mkdir -p logs
    • 启动:node app.js
    • 触发:curl http://localhost:3000/error
    • 验证:tail -f logs/*.log 应能看到包含堆栈的 error 日志与访问日志

0