0.1 写在前面

日志的作用 什么是好的日志 日志规范方向
问题追踪 日志信息要素全,简单易懂,快速查找 定义输出哪些日志和具体输出的信息要素
状态监控 格式统一,便于自动化采集 统一输出的格式,合理的落盘策略
业务埋点 日志信息要素全,位置要对 规范输出日志的场景

0.2 日志的要素

  • when
    • 日志时间
  • where
    • 产生的行数 // 酌情
    • 记录器名称
    • 业务标识
    • 线程名称
  • who
    • 会话标识
  • what
    • 日志级别
    • 日志内容
    • 异常信息
    • 异常堆栈
  • how
    • 关键参数

一、日志开发规范

  1. 【强制】 应用中不可直接使用日志系统中的 API,而应依赖使用日志框架中的API。

    目的: 使用门面模式的日志框架有利于维护和各个类的日志处理方式统一。 正例:使用slf4j api

     @Slf4j
     public class Test 
    

    反例:

     import org.apache.logging.log4j.LogManager;
     import org.apache.logging.log4j.Logger;
     private static final Logger logger = LogManager.getLogger("HelloWorld");
    
  2. 【强制】 在日志输出时,字符串变量之间的拼接使用占位符的方式

    目的: 因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符仅是确定需要打印时才有的替换动作,可以有效提升性能 正例:使用占位符的方式

     log.info("后台-根据电池类型编号获取电池类型信息:batteryTypeId:{}",batteryTypeId);
    

    反例:

     log.info("根据城市编号获取城市信息,cityId:"+cityId);
    
  3. 【强制】禁止直接使用 System.out 或 System.err 输出日志或使用e.printStackTrace()打印异常堆栈

    目的: 日志输出统一使用日志框架配置,统一格式

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

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

  5. 【强制】 避免打印日志时出现空指针,造成日志打印影响了主业务

  6. 【强制】 日志信息中避免出现换行 避免自动采集程序出现问题

  7. 【推荐】 打印日志时仅打印出业务相关属性值或者调用其对象的 toString()方法

日志打印时不建议直接用 JSON 工具将对象转换成 String,不过具体问题具体分析,并发访问量不高的情况下可以尝试使用

  1. 【推荐】 一个项目尽量使用统一的语言(英文/中文)来描述日志信息,考虑英文水平差异性,建议选中文

  2. 【推荐】 记录日志时请先进行思考:这些日志真的有人看吗,看到这条日志你能做什么,能不能给问题排查带来好处,如果没有,建议不记录。

二、 日志场景规范

日志不是记录的越多越详细越好,太过多的日志,会减少日志的存储天数!

必须记录的一些场景

  1. 编程语言提示异常
  2. 业务流程预期不符
  3. 系统核心角色,组件关键动作
  4. 系统启动初始化时加载的自定义配置信息
  5. 调用第三方服务的出参以及入参 // 跨信任边界的调用

    酌情考虑的

  6. 后台定时任务的执行情况
  7. 特殊的条件分支
  8. 接口请求数据的入参以及出参
  9. 执行关键方法的入参以及出参
  10. 执行关键方法的中间状态记录

三、日志级别规范

debug

此级别不允许在生产环境输出

  1. 调试性质的内容,该级别日志主要用于在开发、测试阶段输出

info

  1. 系统启动初始化时加载的自定义配置信息
  2. 调用第三方服务的出参以及入参
  3. 后台定时任务的执行情况

这条日志会被频繁打印或者大部分时间对于纠错起不到作用,就应当考虑下调为DEBUG级别。

warn

  1. 一些不影响主流程的异常,有补偿机制的模块中的异常,可以使用此级别来记录 如: 调用发短信服务,如果失败了有重试机制或者有其他通知方式,出现异常可以使用此级别
  2. 特殊的条件分支 如: 一些参数本不应该为空但是判断条件为空而执行的分支等,建议使用WARN级别日志输出

error

一般来说,WARN级别不会短信报警,ERROR级别则会短信报警甚至电话报警,ERROR级别的日志意味着系统中发生了非常严重的问题,必须有人马上处理,比如数据库不可用,系统的关键业务流程走不下去等等。 ERROR日志目标:给处理者直接准确的信息,error信息能够明确告知发生了什么问题,哪些功能受影响,如何解决或者找什么人解决等。如果只是if逻辑之类的,不要走error级别

模版:

log.error(“[接口名或操作名] [Some Error Msg] happens. [Probably Because]. [Probably need to do] [params] .”);
log.error(“[接口名或操作名] [Some Error Msg] happens. [Probably Because]. [please contact xx@xxx] [params] .”);

四、日志落盘规范

  1. 对日志输出进行分类,便于开发人员查看,也便于通过日志对系统进行及时监控
  2. 目前日志会在ES中存储1周。
  3. 统一日志输出格式,如果有特殊配置则需要评审
    11:44:44.827 WARN [93ef3E0120160803114444] [main] [ClassPathXmlApplicationContext] Exception encountered during context initialization - cancelling refresh attempt
    
  4. 强制输出:
    • 时间
    • 执行线程
    • 日志级别
    • 执行类名
    • 执行类行号
    • 输出内容
  5. 推荐输出
    • traceId,轨迹Id,用于串联一次完整的请求执行路径
    • userId,用户Id,用于作业系统标识某位业务操作人员执行的请求

      其他

  6. 打印日志前三问

    1. 是否真的需要打印这行日志? // 避免真正需要的日志被淹没
    2. 输出的日志信息要素真的能帮助定位问题吗?// 信息要素是否全
    3. 日志的级别设置的合理吗?// 避免小题大做