对日志规范的思考

概述

      系统上线之后,一旦发生异常,第一件事就是要弄清楚当时发生了什么,用户当时做了什么操作,环境有无影响,数据有什么变化,是不是反复发生等,然后再进一步的确定大致是哪个方面的问题。这时,日志就给我们提供了第一手的资料。

基本原则

● 不影响系统正常运行;

● 不允许产生安全问题;

● 不允许输出机密信息;

● 日志可供开发人员定位问题的真正原因;

● 日志可供监控系统自动监控与分析;

日志级别

       ERROR:系统发生了错误事件,但仍然不影响系统的继续运行。系统需要将错误或异常细节记录ERROR日志中,方便后续人工回溯解决。

       WARN:系统在业务处理时触发了异常流程,但系统可恢复到正常态,下一次业务可以正常执行。如程序调用了一个旧版本的接口,可选参数不合法,非业务预期的状态但仍可继续处理等

       INFO:记录系统关键信息,旨在保留系统正常工作期间关键运行指标,开发人员可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录到INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现

      DEBUG:可以将各类详细信息记录到DEBUG里,起到调试的作用,包括参数信息,调试细节信息,返回值信息等等。

      TRACE:更详细的跟踪信息。

       上述日志级别从高到低排列,是开发中最常用的五种。生产系统一般只打印INFO 级别以上的日志,对于 DEBUG 级别的日志,只在测试环境中打印。打印错误日志时,需要区分是业务异常(如:用户名不能为空)还是系统异常(如:调用 会员核心异常),业务异常使用 warn 级别记录,系统异常使用 error 记录。

日志打点

       系统初始化:系统初始化时会依赖一些关键配置,根据参数不同会提供不一样的服务。将系统的启动参数记录INFO日志,打印出参数以及启动完成态服务表述。

       业务流程与预期不符:项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。

       系统核心的关键动作:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录INFO级别日志,比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改等等。

       系统异常:这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用warn或者error级别。

日志格式

[线程 ID:]业务描述:方法描述:[业务流水号:]参数内容

日志规则

  • 对象声明

       建议使用private static final。声明为private可防止logger对象被其他类非法使用。声明为static可防止重复new出logger对象,造成还可以防止logger被序列化,造成安全风险。声明为final是因为在类的生命周期内无需变更logger。

对日志规范的思考

  • 使用API

       统一使用 slf4j,它本质是Facade, 便于我们后期随时切换日志实现。避免在代码中直接使用log4j或java logging 等实现类

对日志规范的思考

  • 语言

     最好在打印日志时输出英文,防止中文不支持而打印出乱码的情况。

  • 禁用System输出

     不要直接使用System.out或System.err 输出日志,也不允许使用 STDOUT、STDERR 作为Logger名字。

对日志规范的思考

  • if..else判断

       对于else 是非正常的情况,需要根据情况选择打印warn 或 error 日志。对于只有 if 没有 else 的地方,如果 else 的路径是不可能的,应当加上 else 语句,并打印 error 日志。

对日志规范的思考

  • 不打印无意义日志

      不记录对于排查故障毫无意义的日志信息,日志信息一定要带有业务信息

对日志规范的思考

  • 不推荐字符串拼接

       如果信息本身需要计算或合并的,打印前要对isxxxEnable()方法进行判断。这是因为在WARN级别时,即使INFO信息不打印,也会执行字符串拼接,造成资源浪费。

对日志规范的思考

      实际上,不推荐使用字符串拼接的方式打印日志,可读性和可维护性都比较差。建议使用占位符

对日志规范的思考

  • 循环体内不要打印 INFO 级别日志

对日志规范的思考

  • 打印日志的代码任何情况下都不允许失败

     一定要确保不会因为Log语句的问题而抛出异常造成中断。如下,如果request为null,就会抛空指针异常

对日志规范的思考

  • 重要方法入口

     建议记录方法调用、入参、返回值,对于排查问题会有很大帮助。

对日志规范的思考

  • 异常

      catch中的异常记录必须打印堆栈信息,不要用e.printStackTrace()。

对日志规范的思考

       不要记录日志后又抛出异常。抛出去的异常,一般外层会处理。如果不处理,那为什么还要抛出去?另外一个原则是,无论是否发生异常,都不要在不同地方重复记录针对同一事件的日志消息

对日志规范的思考

     输出Exceptions的全部Throwable信息。否则会丢失最重要的堆栈信息。

对日志规范的思考