日志用来记录你程序运行的中的一些关键信息,方便你调试以及后期上线的时候查找问题。典型的用法就是应用出现5xx错误了之后,上日志里找找哪里抛出了异常,异常调用栈是什么。
其实Java语言本身有着一套日志工具,在 java.util.logging
包下,简称JUL。JUL大家好像不怎么用,易用性不行,性能也没跟得上。大家都用 Log4j
,使用得也比较广泛,本身属于Apache软件基金会(ASF)。Log4j的作者 Ceki
后来搞了个 Logback
,弥补了Log4j的缺点。ASF后来也把Log4j升级成了 Log4j2
,前者于15年停止更新,后者对于Logback和Log4j也是取其精华,弃之糟粕(PS:Ceki和ASF绝对有过节)。 所以目前可用的日志实现也就是JUL、Log4j2和Logback
。
日志框架这么多,想在不同的框架之间切换还挺不方便,然后就诞生了 Commons Logging 和 SLF4J ,他们都提供统一的API,你只针对API编程就行,底层的实现可以方便地切换。SLF4J的API对应的中央仓库模块是 slf4j-api ,SLF4J有一个是实现是 slf4j-simple ,一般不用。其实Log4j2也有两个模块, log4j-api 和 log4j-core ,分别是API和具体实现,你用log4j-api来编写日志代码就行,如果你提供了log4j-core那就用真正的Log4j2的实现,如果提供了其他实现,就用其他的,如果任何实现都没提供,log4j-api里还包含一个简单的实现叫做SimpleLogger,一般不用。虽然Log4j2无意成为和Commons Logging和SLF4J一样的日志门面(Logging Facade),但他的确可以这么做。 所以说目前可用的日志接口也就Commons Logging、slf4j-api和log4j-api 。
这世界要是这么简单美好就好了,可惜由于同一项目不同团队的选型不一致,或者项目本身和所依赖的库选型不一致,你需要将日志桥接起来。比如你的A项目使用了SLF4J,但是依赖的库B使用了JUL,你可能需要使用 jul-to-slf4j 将JUL的日志导流到SLF4J。诸如此类,导来导去的需求非常多,我曾经想把他们的关系理清楚,后来我的理智制止了我,并对我说:“你那几千行的小破项目,浪费时间干啥,Spring Boot日志默认的不够你用么?”
Spring的 内部 使用了Commons Logging,如果你的项目里有Log4j2,那就用Log4j2;如果有SLF4J,那就用SLF4J;如果都没有,就用JUL。
如果你引入了 Web Starter ,它会自动引入入 Logging Starter ,后者会引入Logback以及SLF4J,你可以使用SLF4J的API来编写log代码,实际底层会调用Logback。另外Logging Starter还包含了log4j-api依赖,用此依赖你可以使用Log4j2的API来编写代码,但是Logging Starter并没有使用引入log4j-core这个具体实现,而是用了log4j-to-slf4j这个实现,它将Log4j2的调用转发到SLF4J上。
Logging Starter还包含了 jul-to-slf4j 依赖,它将JUL的日志转到的SLF4J。所以,总的来说,不管你写代码时候用的是Spring内部的Commons Logging,还是JUL,还是slf4j-api,还是log4j-api,最终都调用到了Logback。
那最终用哪个API好呢?我的建议是用SLF4J,如果感觉它的API(slf4j-api)不够用,比如你需要lambda来对日志参数进行延迟计算,再去换用Log4J2的API,也就是log4j-api模块。Commons Logging已经许久没有维护,Spring用它应该也是历史原因吧。SLF4J的API使用比较简单,如下图(实用主义者其实本文其他内容不用看,直接看了图,就能去代码里用了):
图中日志语句执行之后,会有如下输出:
在“Home Page Now”字符串的前面,还添加了许多信息,话说我没让日志框架输出这些东西,他咋就输出了呢?
因为Spring Boot已经默认配置好了输出的格式,你可以看到日志级别、时间、进程号、线程名等等。不光是Logback,Log4j2和JUL的配置工作Spring Boot也做好了,所以不管你用什么实现,最终输出的东西都是差不多的。
SLF4J日志级别有五个,从高到底分别为 ERROR
、 WARN
、 INFO
、 DEBUG
和 TRACE
。默认情况下,应用只会输出 INFO
及其以上的级别。如果你想查看 DEBUG
及以上的日志,可以给 java -jar app.jar
添加 --debug
参数,或者在 application.properties
添加 debug=true
属性,想看 TRACE
同理。不过这样只会控制Spring Boot内部以及tomcat或者hibernate等的日志,你自己编写的日志是管不到的。想控制全局默认日志输出级别?配置 logging.level.root
属性。
默认情况下,日志会被输出到标准输出,也就是你在命令行里看到的。你还可以将日志同时输出到文件里。
你可以使用 logging.file
指明日志具体保存到哪个文件,可以是绝对路径或者相对路径。你可以使用 logging.path
指明具体哪个文件夹,日志文件会保存在这个文件夹下,并命名为spring.log,文件夹可以是绝对路径或者相对路径。 logging.file
和 logging.path
只需要设置一个就行。
默认情况下,日志每天都会打包一下, 压缩成 .gz
文件保存下来。同时你可以使用 logging.file.max-size
可以指定最大的日志文件大小,比如 100MB
,如果到达这个大小之后,就会被压缩成 .gz
文件保存下来,一天中保存的多个压缩文件会用序号来区分,如上图。 logging.file.max-history
属性可以设置压缩文件最多可以保存多少天,超过期限的就删掉。以上规则Spring Boot文档中其实写的不是很清楚,我是看看了源码才搞懂的。
Spring Boot中很多日志属性是和Logback严格相关的,如果你感兴趣,可以查看 org.springframework.boot:spring-boot:2.1.7.RELEASE
源码里的 org.springframework.boot.logging.logback
包下的相关配置文件defaults.xml和base.xml等等。实在不行还可以看 Logback文档
。真要自定义一些配置的话,可以在 src/main/resources
目录下新建 logback.xml
或者 logback-spring.xml
文件,Spring Boot推荐使用 logback-spring.xml
,方便它做一些初始化的工作。
日志框架会用就行,真的需要定制之前别钻太深。