菜单

错误分类与处理策略

相关源文件

目的与范围

本文档介绍了如何在 Node.js 应用程序中对不同类型的错误进行分类,并为每种类型实施适当的处理策略。理解不同错误类别的区别对于构建能够优雅地处理故障的弹性应用程序至关重要。有关集中式错误处理架构的信息,请参阅集中式错误处理。有关使用 Promise 处理异步错误的信息,请参阅使用 Promise 处理异步错误

错误分类

运营错误与程序员错误

Node.js 错误处理中的基本区别在于运营错误和程序员错误。此分类决定了如何在运行时处理错误。

运营错误:

  • 正常运行期间发生的预期情况
  • 已知影响和根本原因
  • 示例:网络连接问题、无效用户输入、数据库超时
  • 可以在应用程序内优雅地处理
  • 应用程序保持一致状态

程序员错误:

  • 代码中的 bug
  • 影响不明确的意外情况
  • 示例:读取未定义值、内存泄漏、逻辑错误
  • 可能导致应用程序处于不一致状态
  • 通常最好通过崩溃并重新启动进程来处理

来源:sections/errorhandling/operationalvsprogrammererror.md3-5 sections/errorhandling/shutingtheprocess.md4-5 sections/errorhandling/operationalvsprogrammererror.basque.md3-5

标记错误为运营错误

要在代码中实施此区分,可以标记错误为运营错误(可信)或程序员错误(不可信)

对于集中式方法,使用其他属性扩展 Error 对象

来源:sections/errorhandling/operationalvsprogrammererror.md7-58 sections/errorhandling/useonlythebuiltinerror.md40-62

错误决策流程

来源:sections/errorhandling/shuttingtheprocess.md4-5 sections/errorhandling/operationalvsprogrammererror.md60-85

错误处理策略

集中式错误处理

所有错误都应被路由到一个中央处理程序,而不是在应用程序的不同部分单独处理

集中式错误处理程序提供以下好处

  • 跨不同来源(HTTP 请求、cron 作业等)的一致性错误处理
  • 实现日志记录、监控和错误分类的单一位置
  • 避免重复的错误处理代码

中央错误处理程序负责

  1. 记录错误
  2. 确定是运营错误还是程序员错误
  3. 决定是继续操作还是退出
  4. 发送适当的响应

来源:sections/errorhandling/centralizedhandling.md5 sections/errorhandling/centralizedhandling.md84-100

为程序员错误优雅退出

当检测到程序员错误时,应用程序可能处于不一致状态。在这种情况下,最安全的方法是

  1. 记录包含完整详细信息的错误
  2. 向监控系统发出警报
  3. 如果可能,优雅地完成现有请求
  4. 停止接受新请求
  5. 退出进程
  6. 使用进程管理器(PM2、Forever 等)以干净的状态重新启动

来源:sections/errorhandling/shuttingtheprocess.md7-32 sections/errorhandling/operationalvsprogrammererror.md77-85

处理未捕获的 Promise 拒绝

Promises 带来了一个特殊的挑战——在没有正确.catch()处理程序的 Promise 链中抛出的错误不会触发uncaughtException事件。要捕获这些错误

来源:sections/errorhandling/catchunhandledpromiserejection.md5-7 sections/errorhandling/catchunhandledpromiserejection.md22-42

快速失败:尽早验证参数

对于函数参数和输入,尽早验证以防止级联错误

此策略

  • 使 bug 更易于察觉
  • 防止细微问题蔓延
  • 通过从源头失败来简化调试

Joi 等工具可以简化复杂的验证

来源:sections/errorhandling/failfast.md3-25

实施指南

只使用内置的 Error 对象

始终使用 JavaScript 的内置 Error 对象(或其扩展),而不是字符串或自定义错误类型

不做
throw new Error('错误信息')throw '错误信息'
new AppError(type, msg, true)不继承自 Error 的自定义错误对象

使用内置 Error 的好处

  • 捕获堆栈跟踪
  • 与所有错误处理机制兼容
  • 在代码库中保持统一性
  • 与第三方库兼容

来源:sections/errorhandling/useonlythebuiltinerror.md3-5 sections/errorhandling/useonlythebuiltinerror.md9-29

使用成熟的日志记录器

将错误处理与成熟的日志解决方案相结合

日志记录最佳实践

  • 按不同级别记录(debug, info, error)
  • 将上下文信息包含为 JSON
  • 使用结构化日志记录格式
  • 使日志可被机器解析

来源:sections/errorhandling/usematurelogger.md3-12 sections/errorhandling/usematurelogger.md16-26

记录 API 错误

对于 REST API

  • 使用 OpenAPI 规范记录错误
  • 包含 HTTP 状态码及其含义
  • 记录错误响应结构

对于 GraphQL

  • 遵循 GraphQL 的错误规范
  • 在 schema 中记录错误类型
  • 在响应中包含详细的错误信息

来源:sections/errorhandling/documentingusingswagger.md3-7 sections/errorhandling/documentingusingswagger.md9-42

测试错误流程

与成功路径一起测试错误场景

测试不同类型的错误

  • 用于函数级错误处理的单元测试
  • 用于 API 错误响应的集成测试
  • 用于未捕获异常的测试
  • 验证错误日志记录和监控

来源:sections/errorhandling/testingerrorflows.md3-5 sections/errorhandling/testingerrorflows.md8-20

完善的错误处理系统

完整的错误处理系统结合了分类、集中处理和适当的响应策略

来源: sections/errorhandling/centralizedhandling.md5 sections/errorhandling/operationalvsprogrammererror.md3-30 sections/errorhandling/shuttingtheprocess.md4-32

监控和可见性

APM 产品

应用程序性能监控 (APM) 工具有助于检测错误和性能问题

APM 类别目的示例
网站/API 监控外部正常运行时间和性能检查Pingdom, Uptime Robot, New Relic
代码插桩内部性能和错误跟踪New Relic, App Dynamics
运维智能聚合指标和仪表板Datadog, Splunk, Zabbix

这些工具提供

  • 实时错误检测
  • 性能指标
  • 服务降级告警
  • 错误模式可视化
  • 基础设施监控

来源: sections/errorhandling/apmproducts.md3-6 sections/errorhandling/apmproducts.md12-20

错误处理方法总结

关于错误处理,有三种主要思想

  1. 崩溃并重启 - 允许应用程序在遇到程序员错误时崩溃,并依赖进程管理器来重启它
  2. 处理一切 - 尝试处理所有错误,永不崩溃
  3. 平衡的方法 - 优雅地处理运维错误,但在遇到程序员错误时崩溃并重启

Node.js 社区普遍偏爱平衡的方法,并遵循以下关键原则:

  • 区分运维错误和程序员错误
  • 使用集中的错误处理系统
  • 带上下文记录错误
  • 程序员错误时退出并重启
  • 对于预期的运维错误,继续运行

来源: sections/errorhandling/operationalvsprogrammererror.md77-85 sections/errorhandling/shuttingtheprocess.md86-93