为什么用日志库而不是print进行日志输出

为什么用日志库而不是print进行日志输出

2020, Feb 11    

你是否也用了很久的log4j但是不知道为啥?

背景介绍

某次线上故障排查,发现某个服务在凌晨4点挂了。不知道其他的任何原因。这是一个SpringBoot项目,负责对外提供接口数据与一些对数据的定时操作。排查logback输出的日志文件都是INFO级别。一个WARN、ERROR、FATAL都没有出现。一切正常的情况下怎么会突然挂了呢。

4点钟

挂的时间是4点整,而凌晨的时间其实有大量的定时任务调度与其他相关服务的定时请求。(凌晨用户访问量很小,有一些定时脚本就会对数据进行一个订正之类的,或者是做一些前一天的统计查询)
排查到4点整的access.log中发起的请求,发现这个时间存在一个定时任务与接口访问。
大概看了一下接口访问的逻辑,由于没有对接口的参数进行限制,可能会有一个较大的请求出现。会导致一次拉取非常多的数据出现内存溢出。但是接口的其他的拦截器信息查不到。说明请求都没有打入到服务。

又开始排查定时任务,梳理发现确实会存在异常。但是它长这样…

try{
    
}catch(Exception e){
    System.out.println("....");
}

之后去找到SpringBoot启动的nohup.out文件。终于定位到确实就是这个问题…

为什么要用日志库而不是System.out.println()

类似于上面出现的情况,我们大多数情况下对于日志的输出都会有个固定的目录。其中涉及到服务器的管理、架构、权限、灵活性等。如果我们使用System.out.println()输出那么其目录就是固定的一个路径。而且每个人都会有自己的一个输出的风格。不便于管理。

同时可能又存在其他错误告警通知的方式,常见的有邮件、钉钉、webhock、短信通知等。如果不使用日志库的方案的话每次修改通知类型都需要去项目中修改代码。

log4j输出的好处

灵活性

当你输出日志的时候总是会遇到几个情况. 输出的路径、输出的格式、甚至会出现输出到远程地址、发送邮件等方式。 那么如果使用System.out.println()就无法支持该方式。如果需要进行目录变更、通知方式变更、格式变更的情况就需要进入环境中修改代码来实现。
log4j可以通过修改配置文件来实现这些方案的支持、例如修改日志文件的路径、修改通知的方式等。无需通过配置文件来进行变更。
当我们需要删除某些日志的输出时,也可以通过配置文件来进行处理。而不需要进行应用程序代码的修改。

优先级

log4j提供优先级支持,log4j提供了几种优先级的支持:DEBUG、INFO、WARN、ERROR、FATAL。顺序即表示优先级,例如我们常规需要WARN以上级别的日志进行发送邮件,或者短信告警。其他所有级别均需要写入到本地文件,对于级别变更等均可以直接通过配置文件实现。

  • 测试/开发环境 ```
*   生产环境

#####   调试完善
我们调试时如果是使用System.out.println()需要获取到当前的类、行等信息。如果调试多线程的情况下还需要获取到线程id等信息,并且每次的输出都需要添加。那么如果使用log4j的话可以直接实现配置输出的格式。调整输出的日志格式。

```

丰富的插件化可定制化

某些日志库本身就带有大量的插件,例如写入文件、发送邮件等简单的插件。还提供接口提供我们来自由定制的插件来实现项目的需求。例如文件统一发送到某个远程文件系统或直接发送告警短信、或者发送HTTP请求等。例如当前我们统一接入告警系统需要发送一些webhock。而直接通过logback自定义Appender的方式即可实现方案。方便灵活的实现需求。

整体总结

  • 使用日志库而不是System.out.println()因为其更具有灵活性,能够自定义的实现标准输出与设置过滤日志级别等,通过级别增加通知方式。而不是需要修改代码的来实现。
  • 日志库允许逐级控制日志目录,提供细粒度的日志输出控制。而System.out.println()只能提供应用程序级别的控制。
  • 快速实现自定义的输出(通知)。我们可以直接通过增加日志库插件的方式实现增加通知方案。例如 短信、钉钉、webhock、远程文件系统等。