日志开发指南
日志规约
-
【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架(如SLF4J)中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理 方式统一。
-
【强制】日志文件至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。对于当天日志,以 “应用名.log”来保存,保存在/{统一目录}/logs/{应用名}目录下,过往日志文件名带有yyyy-MM-dd格式日期。
-
【强制】在日志输出时,字符串变量之间的拼接使用占位符的方式。 说明:因为 String 字符串的拼接会使用 StringBuilder 的 append() 方式,有一定的性能损耗。使用占位符仅是替换动 作,可以有效提升性能。
正例:logger.debug("Processing trade with id : {} and symbol : {}", id, symbol);
-
【强制】生产环境禁止使用 System.out 或 System.err 输出或使用 e.printStackTrace() 打印异常堆栈。
-
【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。
注意:一定要携带最后一个参数e(java.lang.Throwable),禁止仅打印 e.getMessage()
正例:
logger.error("inputParams: {}", 各类参数或者对象 toString(), e);
-
【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时 删除这些观察日志。
日志格式
统一的日志格式不仅对用户友好,也有利于日志收集等运维平台做进一步处理。因此,日志格式必须在系统内达成共识。
一条完整的日志由系统自动捕获的公共信息(由日志模板定义),和开发者手动记录的日志信息拼接组成,即一条日志=公共信息+日志体
。
格式定义
以下日志格式配置适用logback,对应xml中的pattern配置:
|%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%replace(%X{tid}){'TID:',''}|%replace(%X{sw_ctx}){'(([\\w\\[\\-\\.:@/]*,){3})|(,-?\\d+\\])',''}|${APP_NAME}|%t|%C|%M|%L|%m%n
log4j2与logback的replace函数有细微差别,如有需要可查阅log4j2文档。
分段释义:
|日期时间|日志级别|链路ID|链路跨度ID|应用名词|线程名称|类名|方法名|行号|日志体
其中traceId与spanId是链路追踪参数,需要基于链路追踪框架生成,可以结合链路追踪系统(本例模板格式为skywalking
)使用;appName由应用系统定义。
日志样例
-
INFO(DEBUG,WARN等)日志
|2023-01-30 14:15:26.220|INFO |trace0|span1|iot.spaceFence|http-nio-8080-exec-1|com.iot.spaceFence.controller.IotAlarmFenceController|findOne|31|正常日志:0e178fbb5907676c38959eb7bf1c5f2b
-
ERROR日志
|2023-01-30 14:15:26.221|ERROR|trace1|span1|iot.spaceFence|http-nio-8080-exec-1|com.iot.spaceFence.controller.IotAlarmFenceController|findOne|35|异常日志: 0e178fbb5907676c38959eb7bf1c5f2b java.lang.RuntimeException: 测试异常 at com.iot.spaceFence.controller.IotAlarmFenceController.findOne(IotAlarmFenceController.java:33) [classes/:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_351] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_351] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_351] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_351]