背景
偶尔翻翻代码,看到项目中的日志打印都心有感慨,日志打印风格各异,也没什么好说的,毕竟没有领导专门约束过这些,只能凭个人了。
Java 编码中到底应该怎么打日志呢?一起回顾一下阿里巴巴Java开发者规范中的日志打印强制规约,不多,就 十条,落实到日常开发中需要开发者关注的就八条,掌握区区八条,对靠脑力劳动的开发者应该是不在话下的。
日志强制规约10条
1.应用中不可直接使用三方日志组件(Log4j、Logback)中的API,而应依赖使用统一日志框架(SLF4J、JCL---Jakarta Commons Logging)中的 API。【日常开发关注
】
2.普通日志保存天数与命名需要遵循规范。【项目初期需要关注】
3.应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:
bash
appName_logType_logName.log。
logType:日志类型,如 stats / monitor / access 等;
logName:日志描述。
原理:这种命名的好处是,通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。【项目初期需要关注】
4.避免重复打印日志,浪费磁盘空间,必须在日志配置中设置 additivity=false。【项目初期需要关注】
5.根据国家法律,网络运行状态、网络安全事件、个人敏感信息操作等相关记录,留存的日志不少于六个月,并且要多机备份。【运维关注】
6.在日志输出时,字符串变量之间的拼接使用占位符;有存在用 + 拼接的情况,应该杜绝。【日常开发关注
】
原理:字符串拼接使用 StringBuilder.append()
,有性能损耗;使用占位符仅是替换动作,可以有效提升性能。
正例:
bash
logger.debug("Processing trade with id : {} and symbol : {}", id, symbol);
反例:
bash
logger.info("文件路径为:"+classPath+name+",文件大小:"+size+",文件原名称:"+fileName);
7.对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。【日常开发关注
】
原理:虽然在 Logger 类的 debug(参数)
方法体内第一行代码 isDisabled(Level.DEBUG_INT)
为真时就直接 return, 但是参数可能会进行字符串拼接运算,为了减少不必要的计算,应该遵守第 6 条。【日常开发关注
】
8.生产环境禁止使用 System.out 或 System.err 输出或使用 e.printStackTrace() 打印异常堆栈。【日常开发关注
】
原理:首先,System.out 是单例,可能造成死锁「PS:本人亲自排过这个坑」。其次, 它打印到控制台了,一般生产环境Java 应用启动脚本会通过 >/dev/null 2>&1 &
命令忽略控制台日志的,日志信息被淹没了,对问题排查无任何帮助,徒耗资源而已。
9.异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。同时 logger.error
应该打印堆栈信息方便排错。【开发关注】
10.日志打印时禁止直接用JSON工具将对象转换成 String。【开发关注】
原理:如果对象里某些get方法被覆写,存在抛出异常的情况,则可能会因为打印日志而影响正常业务流程的执行。
启示录
思考:项目代码中很多地方都有在日志中打印 JSON 序列化对象的代码,日志中序列化有两个问题:
- 1)它耗时。
- 2)重写 get 方法异常的情况下序列化异常时会影响正常的业务流程。
怎么权衡呢?从我个人的经验来说,虽然直接打印 JSON 确实很方便,但不免有偷懒的嫌疑,总不能一个 JSON 中所有信息都重要吧。心中有这个规约意识,打重要信息,别总想着简单打 JSON,尤其是在开发测试阶段想打又不想影响生产环境而打印 DEBUG 日志时,更要谨慎第 6、7 条。