专栏名称: 架构师之路
架构师之路,坚持撰写接地气的架构文章
目录
相关文章推荐
架构师之路  ·  用单库自增键来生成业务id,后期要怎么分裤? ·  6 天前  
架构师之路  ·  对不起,你那不叫努力,叫重复劳动... ·  1 周前  
架构师之路  ·  恭喜PostgreSQL,超越MySQL成为 ... ·  1 周前  
51好读  ›  专栏  ›  架构师之路

阿里巴巴,日志与异常强制规范26条,Java篇(收藏不亏)

架构师之路  · 公众号  · 架构  · 2024-08-19 19:01

正文

规范内容取自“阿里巴巴异常日志强制规范”(Java版),发布时间为2022.2.3,开源。

内容较多,3000字,建议收藏。


规范的原理,比规范本身,更加重要。


第一部分,异常处理强制规范【9条】


1. 可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理。

正例:

if (obj != null) {...}


反例:

try { obj.method(); } 
catch (NullPointerException e) {…}


2. 不要用异常捕获模块来做流程控制,条件控制。
画外音:异常捕获的初衷是解决程序运行中的各种意外情况,而不是处理业务异常分支。


3. 只 catch 非稳定代码。

稳定代码指的是无论如何不会出错的代码。

正例:
用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或用户输入密码过于简单... 这些是稳定代码,应该在程序上作出判断,并提示给用户,而不是放到 try catch 中。

4. 捕获异常的目的是为了处理它,不能捕获了却不处理而抛弃之。

如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,并将其转化为用户可以理解的内容。


5. 事务场景中,抛出异常被 catch 后,如果需要回滚,可能要考虑业务层面的补偿事务。

6. finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch
画外音:如果JDK7,可以使用 try-with-resources 方式。

7. 不要在 finally 块中使用 return

画外音:try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存在 return 语句,则会在此直接返回,无情丢弃掉 try 块中的返回点。

反例:


8. 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。

9. 在调用RPC、二方包、或动态生成类的相关方法时,捕捉异常使用 Throwable 类进行拦截。

画外音:二方包,是指公司内部的依赖库。

通过反射机制来调用方法,如果找不到方法,抛出 NoSuchMethodException

反例:

非核心服务引入了高版本的 spring ,导致运行到某段核心逻辑时,抛出 NoSuchMethodError 错误, catch 用的类却是 Exception ,堆栈向上抛,影响到上层业务。这是一个非核心功能点影响到核心应用的典型反例。


第二部分,错误码强制规范【7条】


1. 错误码的制定原则是:快速溯源、沟通标准化。
画外音:错误码必须能够方便的比对与查找(方便grep,方便代码查找),让人能够快速知晓错误来源,快速判断是谁的问题,并对错误原因快速达成一致认知。

2. 错误码不体现版本号和错误等级信息。

画外音:
错误码只追加,不修改,不使用版本号区分;
错误等级,由错误码本身的业务含义决定,由其他字段体现。


3. 没有错误,但不得不填充错误码时,错误码填写五个零:00000。


4. 错误码为字符串类型,共5位,分成两个部分:错误产生来源+四位数字编号。


其中,错误产生来源分为A/B/C。


A表示错误来源于用户,比如参数错误,用户安装版本过低等:


B表示错误来源于当前系统,往往是业务逻辑出错,例如:


C表示错误来源于非自身系统,例如:


其次,四位数字编号从0001到9999,大类之间的步长间距预留100。


5. 错误码不与公司业务架构挂钩,不与组织架构挂钩,需要在统一平台审批,审批后生效,先到先得。


6. 禁止随意定义新的错误码。

画外音:尽可能在原有错误码表中找语义相同或者相近的错误码使用。

7. 错误码不能直接透传给用户。
画外音,作为工程师,应该区分好:
堆栈(stack_trace)
错误信息(error_message)
错误码(error_code)
用户提示(user_tip
它们有关联,但不要混用。

第三部分,日志强制规范【10条】


1. 应用中不可直接使用三方日志组件(Log4j、Logback)中的API,而应依赖使用统一日志框架(SLF4J、JCL—Jakarta Commons Logging)中的 API。
画外音:使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

2. 普通日志保存天数与命名需要遵循规范。
(1)日志文件至少保存15天;
画外音:有些异常具备以“周”为频次发生的特点。
(2)当天日志,以“应用名.log”来保存;

(3)保存目录为:/{统一目录}/{应用名}/logs/;

(4)过往日志格式为:{logname}.log.{保存日期}

(5)日期格式为:yyyy-MM-dd


mppserver 应用为例:
日志保存为: /home/admin/mppserver/logs/mppserver.log
历史日志为: mppserver.log.2024-08-18


3. 根据国家法律,网络运行状态、网络安全事件、个人敏感信息操作等相关记录,留存的日志不少于六个月,并且要多机备份。


4. 应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:appName_logType_logName.log


logType:日志类型,如 stats / monitor / access 等;

logName:日志描述。
画外音:这种命名的好处是,通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。


例如,mppserver应用中单独监控时区转换异常:
mppserver_monitor_timeZoneConvert.log

5. 在日志输出时,字符串变量之间的拼接使用占位符。


字符串拼接使用 StringBuilder.append() ,有性能损耗。使用占位符仅是替换动作,可以有效提升性能。

正例:
logger.debug("Processing trade with id : {} and symbol : {}", id, symbol);


6. 对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。


虽然在debug(参数) 的方法体内第一行代码 isDisabled(Level.DEBUG_INT)  为真时就直接 return, 但是参数可能会进行字符串拼接运算。

正例:


7. 避免重复打印日志,浪费磁盘空间,必须在日志配置中设置additivity=false


8. 生产环境禁止使用 System.outSystem.err 输出或使用 e.printStackTrace() 打印异常堆栈。
画外音:标准日志输出与标准错误输出文件每次 Jboss 重启时才滚动,如果大量输出,容易造成文件大小超过操作系统大小限制。

9. 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。


正例:

logger.error("inputParams: {} and errorMessage: {}", 各类参数或者对象toString(), e.getMessage(), e);


10. 日志打印时禁止直接用JSON工具将对象转换成String。

画外音:如果对象里某些get方法被覆写,存在抛出异常的情况,则可能会因为打印日志而影响正常业务流程的执行。

规范背后的原理,比规范本身,更有价值。


讨论:

你觉得日志与异常需要被规范吗?

你们公司有相关规范吗?执行情况如何?


相关文章

阿里巴巴MySQL规范,五千字版(收藏)


希望大家有收获,