文章目录

  • java日志总结系列1-日志规范
  • 日志级别
  • 日志内容
  • 日志注意事项


java日志总结系列1-日志规范

日志是系统的重要组成部分,用于记载系统的执行记录、审计、排查问题、数据采集等。日志需要持久化,通常日志仅仅需要持久化到磁盘,或者存储到ES,有些场景也需要将日志存储到MySQL中,例如重要的请求日志、用户抽奖的执行记录、提现操作等。
本系列主要讲述java系统中存储到文件、ES中的日志。

日志级别

由高到低可分为error、warn、info、debug。

error:表示系统发生了错误,应该马上介入处理,此时已经影响到了系统正常运转。这样情况最好配置告警。例如

  1. 读写文件错误
  2. 网络中断
  3. 与第三方对接的异常
  4. 其他影响功能使用的异常(NPE/DB异常等)

warn:需要引起重视,但是不需要马上处理的问题。例如

  1. 有容错机制的异常
  2. 性能接近临界值
  3. 找不到配置文件,但是有默认值(参考第一条,许多框架也是这么做的)
  4. 业务异常
  5. 其他

info:记录用户/系统行为

  1. 对外提供的接口入口处:打印接口的唯一标识和简短描述,并且要将调用方传入的参数原样打印出来,这样当系统出现问题时,就能很容易的判断出是否是调用方出现了问题
  2. 系统操作:读写文件,定时任务
  3. 不符合业务逻辑预期:打印关键的参数,要能从这些参数中清楚地看出,谁的操作与预期不符,为什么与预期不符。
  4. 调用其它接口的前后:打印所调用接口的系统名称/接口名称和传入参数/响应参数,这样能方便做问题定界,通过这两条日志可以清楚地看出是否是所调用的系统出现了问题
  5. 系统模块的入口和出口处:重要方法级或模块级,记录它的输入与输出,方便定位
  6. 非预期执行:为程序在“有可能”执行到的地方打印日志
  • switch case语句块中的default
  • if…else if…else中很少出现的else情况
  • try catch语句块中catch分支。
  1. 服务状态变化(尽可能记录线索):程序中重要的状态信息的变化应该记录下来,方便查问题时还原现场,推断程序运行过程
  2. 一些可能很耗时的业务处理:批处理,IO操作

debug:方便排查问题的信息,一般线上不允许存储该级别的日志

  1. 开发和测试人员都能看懂
  2. 不需要重现问题,直接就能定位问题

日志内容

必要前缀

  1. 时间:时区,毫秒
  2. 进程ID
  3. 线程ID,在多线程时非常重要
  4. 日志级别
  5. 模块,哪个类;如果是微服务,指明哪个服务ID或者名字
  6. host,机器ip/name

自定义标签

  1. traceId
  2. 上下文重要标签信息等

日志消息体

  1. 日志表述的内容
  2. filter:用于查找时方便,自定义。(比如可以给重要的日志编号,这样搜索日志会很方便,但是要注意编码要尽量不与其他数据冲突,比如123就是不好的编号)
  3. 会话信息(用户,登录账号,session),其他信息,比如状态信息(开始,中断,结束),版本号
  4. 案发现场上下文信息和异常堆栈信息

日志注意事项

  1. 不要打印敏感信息,比如密码、身份证号、银行卡号等等,避免敏感信息泄露
  2. 不要使用System.err 打印日志,这是同步的。线上高qps下容易造成阻塞,引发性能问题
  3. 不要打印大量的日志,一般一个服务一小时500M已经算比较多了。
  4. 使用logger.xxx()时使用参数化日志特性,不要进行字符串拼接。这样可以提高在关闭某一级别日志时系统的性能。因为如果使用字符串拼接,那么无论如何对象的toString()方法都会执行;而如果使用参数化打印,某一级别的日志不打印时,是不会执行toString()方法的。
public void info(String format, Object arg) {
        if (logger.isLoggable(Level.INFO)) { //如果不打印info,直接就跳过了
            FormattingTuple ft = MessageFormatter.format(format, arg);
            log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
        }
    }
  1. 打印异常时的做法logger.error("filter. message. params = {}.",params.e)。不要只打印e.getMessage(),也不要使用e.printStackTrace(),它是打印到System.err中的
  2. 不要在打印日志的时候出现异常。例如logger.info("msg. id = {}".dto.getId()).dto为null时会抛NPE
  3. 使用日志门面框架,例如SLF4J,不要直接使用某一个具体日志类
  4. 一个项目底层只依赖一个实现,其他的日志类需要全部排除。