# 概念

  • 异常(Exception) 如果是程序员预先规划好的异常(Exception),那么显示异常号的时候基本上都会跟着说明的,因为这种异常往往是用户误操作造成的,所以显示说明有意义。
  • 错误(Error) 但是程序员无法预先规划的错误(Error),那么出错表示的是程序员写错了,此时为了帮助 Debug,故障恢复要做的事情要尽可能少,显示最基本的信息(代号)并且直接退出是最有效的做法,而且对于程序员来说,错误号就是最具体的说明。

# 错误码

来源 (opens new window)

# 按照系统架构

一个系统的消息代码(错误码)一般可以分为这么几大类:

  1. 成功
  2. 未知异常: 作为兜底的错误码
  3. 客户端异常:请求方式错误、请求参数错误、没有权限、没有登录、上传文件过大、重复提交抢锁失败、并发过大触发限流等等
  4. 依赖的第三方业务系统异常:比如调用第三方系统超时;第三方系统抛出异常;第三方业务系统限流等等
  5. 基础中间件异常: 如 mysql、redis、MQ 这些基础中间件出现连接超时、连接池满、访问失败等等
  6. 数据问题:数据不一致、记录不存在、主键冲突、字段不能为空等等 建议参考 HTTP 协议,错误码本身使用数字,每一类错误码前缀保持一致,如:
0:成功
-1:未知异常
4xxxxx:客户端异常
5xxxxx:第三方业务系统异常
6xxxxx:基础中间件异常
7xxxxx:数据问题

# 按照业务逻辑

具体到业务实现上可以分级,1-99 用来定义公共的错误,如:参数校验错误、数据库失去连接、请求超时未响应等,后续各系统按照分段申请保留错误码段,如 1000-1999 用于用户体系,2000-2999 用于商品体系,依次递增。

# 规定

  1. 错误码用 4 位数字表示,大类之间使用步长 100 进行分类 错误码以不断追加的方式进行兼容。错误等级由日志和错误码本身的释义(msg)来决定。
  2. 编号不与公司业务架构,更不与组织架构挂钩,一切与平台先到先申请的原则进行。生效之后,编号即被永久固定。
  3. 错误码使用者避免随意定义新的错误码。 说明:尽可能在原有错误码附表中找到语义相同或者相近的错误码在代码中使用即可。
    堆栈(stack_trace)   ---后端自己看
    错误信息(error_message)  ---后端给前端的参考
    错误码(error_code)--- 前端给后端反馈
    提示信息(user_tip) --- 用户指引
    
  4. 错误码之外的业务独特信息由 error_message 来承载,而不是让错误码本身涵盖过 多具体业务属性。

# 最终定义

# 错误码

  • 大类
正常:0000
未知错误:9999
用户输入错误:1000 开头
第三方依赖错误:2000 开头
爬虫服务错误:3000 开头
客户端错误:4000 开头
服务端错误:5000 开头
中间件(数据库)错误:6000 开头
数据冲突错误:7000 开头
...
  • 派生类 继承自大类,如果不指定 http 状态码,则默认返回继承的类的 http 状态码。注意:服务端错误码不一定都是 5xx,该系统错误是在项目的维度来区分的,和 http 中的服务端错误不一定相同。

# 总结

对于后端编写层级的异常,我们调用excepts.py中的异常类(FmException)抛出异常;只有需要通过 API 告知前端或者用户的信息,我们才调用errors.py中的(StatusCodeError)抛出错误。从而保证异常类抽象层级保持一致。再直接点来说:errors.py应该只在views.py中被导入,而excepts.py可以在utils或者其他子模块(如爬虫模块等)中被导入。

# 推荐阅读