程序中如何正确使用日志

 前言

线上出现问题,你的第一反应是什么?

如果是我的话,第一时间想的应该是查日志:

  1. if…else 到底进入了哪个分支?
  2. 关键参数是不是有缺失?
  3. 入参是不是有问题,没做好校验放进去了?

良好的日志能帮我们快速定位到问题所在,坑你的东西往往最为无形,良好的日志就是要让这些玩意无所遁形!

 使用正确的格式

如果你是这样打印日志的:

log.info("根据条件id:{}" + id + "查询用户信息");
复制代码

不要这样做,会产生大量的字符串对象,占用空间的同时也会影响性能。

正确的做法是使用参数化信息的方式:

log.info("根据条件id:[{}],查询用户信息", id);
复制代码

这样做除了能避免大量创建字符串之外,还能明确的把参数隔离出去,当你需要把参数复制出来的时候,只需要双击鼠标即可,而不是用鼠标慢慢对准再划拉一下。

这样打出来的日志,可读性强,对排查问题的帮助也很大!


小技巧

1)多线程

遇到多个线程一起执行的日志怎么打?

有些系统,涉及到并发执行,定时调度等等,就会出现多次执行的日志混在一起,出问题不好排查,我们可以把线程名打印进去,或者加一个标识用来表明这条日志属于哪一次执行:

if (log.isDebugEnabled()) {
    log.debug("执行ID=[{}],处理了ID=[{}]的消息,处理结果:[{}]", execId, id, result);
}
复制代码

2)使用 SpringBoot Admin 灵活开关日志级别

程序中如何正确使用日志_第1张图片

日志级别

Java应用中,日志一般分为以下5个级别:

  • ERROR 错误信息
  • WARN 警告信息
  • INFO 一般信息
  • DEBUG 调试信息
  • TRACE 跟踪信息

1)ERROR

ERROR 级别的日志一般在 catch 块里面出现,用于记录影响当前线程正常运行的错误,出现 Exception 的地方就可以考虑打印 ERROR 日志,但不包括业务异常。

需要注意的是,如果你抛出了异常,就不要记录 ERROR 日志了,应该在最终的地方处理,下面这样做就是不对的:

try {
    int i = 1 / 0;
} catch (Exception e) {
    log.error("出错了,什么错我不知道,啊哈哈哈!", e);
    throw new CloudBaseException();
}
复制代码

2)WARN

不应该出现,但是不会影响当前线程执行的情况可以考虑打印 WARN 级别的日志,这种情况有很多,比如:

  • 各种池(线程池、连接池、缓存池)的使用超过阈值,达到告警线
  • 记录业务异常
  • 出现了错误,但是设计了容错机制,因此程序能正常运行,但需要记录一下

3)INFO

使用最多的日志级别,使用范围很广,用来记录系统的运行信息,比如:

  • 重要模块中的逻辑步骤呈现
  • 客户端请求参数记录
  • 调用第三方时的参数和返回结构

4)DEBUG

Debug 日志用来记录自己想知道的所有信息,常常是某个功能模块运行的详细信息,已经中间的数据变化,以及性能信息。

Debug 信息在生产环境一般是关闭状态的,需要使用开关管理(比如 SpringBoot Admin 可以做到),一直开启会产生大量的 Debug,而 Debug 日志在程序正常运行时大部分时间都没什么用。

if (log.isDebugEnabled()) {
    log.debug("开始执行,开始时间:[{}],参数:[{}]", startTime, params);
    log.debug("通过计算,得到参数1:[{}],参数2:[{}]", param1, param2);
    log.debug("最后处理结果:[{}]", result);
}
复制代码

5)TRACE

特别详细的系统运行完成信息,业务代码中一般不使用,除非有特殊的意义,不然一般用 DEBUG 代替,事实上,我编码到现在,也没有用过这个级别的日志。

写在最后

一开始写代码的时候,没有规范日志的意识,不管哪里,都打个 INFO,打印出来的东西也没有思考过,有没有意义,其实让自己踩了不少坑,加了不少班,回过头,我想对学习时期的我说一句:”能让你加班的东西,都藏在各种细节里!写代码之前,先好好学习如何打日志!“


 

你可能感兴趣的:(java,jvm,开发语言)