日志等级
常用的日志级别由低到高分别为 debug > info > warn > error:
- debug:输出调试性质的信息,主要用于开发、测试阶段的输出。该级别的日志应该尽可能的详细,帮助开发人员进行问题分析。
- info:记录系统的关键信息,如出入参、业务流程中的核心操作等,方便日常运维以及定位问题时进行场景复现。
- warn:输出警告信息,内容一般是有规划的,如方法的入参不满足条件等,需要开发人员关注。
- error:用于输出一些不可预知的信息,如:网络通信异常、数据库连接异常等,对业务流程影响较大,需要运维配置日志监控。
军规解读
对于trace/debug/info级别的日志输出,必须进行日志级别的开关判断
日志输出的时候,只会输出级别大于等于系统设置的级别的日志,如果设置日志界别为info,那么debug日志就不会输出。但是,即使日志不输出,但日志语句还是会执行,会额外消耗cpu时间,因此对于性能消耗比较大的日志,比如日志语句中调用了计算类的方法,使用的时候需要进行日志级别的判断。
其他日志规范
- 在日志输出时,字符串变量之间的拼接使用占位符的方式
因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符仅是替换动作,可以有效提升性能。
- 禁止直接使用 System.out 或 System.err 输出日志或使用e.printStackTrace()打印异常堆栈
- System.out 或 System.err 的日志是直接输出的控制台的,如果不重定向到文件则没办法追溯。另外,标准输出文件只有在服务重启时才滚动,容易造成文件大小超过操作系统大小限制。
- System.out.println是一个同步方法,在高并发的情况下,大量执行println方法会严重影响性能
- 不能实现日志按等级输出
- 控制台日志不会自动添加类名、方法名等信息,不利用分析问题
- e.printStackTrace()打印出的堆栈日志跟业务代码日志是交错混合在一起的,通常排查异常日志不太方便。
- e.printStackTrace()语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,系统请求将被阻塞。
- 打印异常日志一定要输出全部错误信息
反例:
- 没有记录详细的堆栈异常信息,只记录错误基本描述信息,不利于排查问题。
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
log.error("execute failed", e.getMessage());
}
}
正例:
一般日志框架中的warn、error级别均有存在传递Throwable异常类型的API,可以直接将抛出的异常传入日志API中。
void error(String var1, Throwable var2);
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
log.error("execute failed", e);
}
}
- 不要在循环中打印INFO及以上级别日志
- 避免敏感信息输出
- 尽量用英语来描述日志错误信息
防止中文编码与终端不一致导致打印出现乱码的情况
- 禁止直接使用日志系统(Log4j、Logback)中的API
不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架 (SLF4J、JCL--Jakarta Commons Logging)中的API。
Slf4j是一个日志规范,用来标准化日志输出方式,并没有具体的日志输出实现。
log4j是一个日志框架,实现了具体日志的输出逻辑。
直接使用Log4j或者Logback中的API会导致系统代码实现强耦合日志系统,后续需要切换日志实现时会产生比较大的改造成本