日志开发指南

发布时间 2023-05-09 11:31:44作者: 伊丽莎白菜

日志规约

  1. 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架(如SLF4J)中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理 方式统一。

  2. 【强制】日志文件至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。对于当天日志,以 “应用名.log”来保存,保存在/{统一目录}/logs/{应用名}目录下,过往日志文件名带有yyyy-MM-dd格式日期。

  3. 【强制】在日志输出时,字符串变量之间的拼接使用占位符的方式。 说明:因为 String 字符串的拼接会使用 StringBuilder 的 append() 方式,有一定的性能损耗。使用占位符仅是替换动 作,可以有效提升性能。
    正例

    logger.debug("Processing trade with id : {} and symbol : {}", id, symbol);
    
  4. 【强制】生产环境禁止使用 System.out 或 System.err 输出或使用 e.printStackTrace() 打印异常堆栈。

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

    注意:一定要携带最后一个参数e(java.lang.Throwable),禁止仅打印 e.getMessage()

    正例

    logger.error("inputParams: {}", 各类参数或者对象 toString(), e);
    
  6. 【推荐】谨慎地记录日志。生产环境禁止输出 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]
    

参考文献