@[toc]
之前錄過(guò)一個(gè)視頻和大家分享 Spring Boot 日志問(wèn)題,但是總感覺(jué)差點(diǎn)意思宴偿,因此松哥打算再通過(guò)一篇文章來(lái)和大家捋一捋 Java 中的日志問(wèn)題湘捎,順便我們把 Spring Boot 中的日志問(wèn)題也說(shuō)清楚。
1. Java 日志概覽
說(shuō)到 Java 日志窄刘,很多初學(xué)者可能都比較懵窥妇,因?yàn)檫@里涉及到太多東西了:Apache Commons Logging
、Slf4j
娩践、Log4j
活翩、Log4j2
、Logback
翻伺、Java Util Logging
等等材泄,這些框架各自有什么作用?他們之間有什么區(qū)別吨岭?
1.1 總體概覽
下面這張圖很好的展示了 Java 中的日志體系:
可以看到拉宗,Java 中的日志框架主要分為兩大類(lèi):日志門(mén)面和日志實(shí)現(xiàn)。
日志門(mén)面
日志門(mén)面定義了一組日志的接口規(guī)范辣辫,它并不提供底層具體的實(shí)現(xiàn)邏輯旦事。Apache Commons Logging
和 Slf4j
就屬于這一類(lèi)。
日志實(shí)現(xiàn)
日志實(shí)現(xiàn)則是日志具體的實(shí)現(xiàn)急灭,包括日志級(jí)別控制姐浮、日志打印格式、日志輸出形式(輸出到數(shù)據(jù)庫(kù)葬馋、輸出到文件卖鲤、輸出到控制臺(tái)等)肾扰。Log4j
、Log4j2
蛋逾、Logback
以及 Java Util Logging
則屬于這一類(lèi)白对。
將日志門(mén)面和日志實(shí)現(xiàn)分離其實(shí)是一種典型的門(mén)面模式,這種方式可以讓具體業(yè)務(wù)在不同的日志實(shí)現(xiàn)框架之間自由切換换怖,而不需要改動(dòng)任何代碼甩恼,開(kāi)發(fā)者只需要掌握日志門(mén)面的 API 即可。
日志門(mén)面是不能單獨(dú)使用的沉颂,它必須和一種具體的日志實(shí)現(xiàn)框架相結(jié)合使用条摸。
那么日志框架是否可以單獨(dú)使用呢?
技術(shù)上來(lái)說(shuō)當(dāng)然沒(méi)問(wèn)題铸屉,但是我們一般不會(huì)這樣做钉蒲,因?yàn)檫@樣做可維護(hù)性很差,而且后期擴(kuò)展不易彻坛。例如 A 開(kāi)發(fā)了一個(gè)工具包使用 Log4j 打印日志顷啼,B 引用了這個(gè)工具包,但是 B 喜歡使用 Logback 打印日志昌屉,此時(shí)就會(huì)出現(xiàn)一個(gè)業(yè)務(wù)使用兩個(gè)甚至多個(gè)日志框架钙蒙,開(kāi)發(fā)者也需要維護(hù)多個(gè)日志的配置文件。因此我們都是用日志門(mén)面打印日志间驮。
1.2 日志級(jí)別
使用日志級(jí)別的好處在于躬厌,調(diào)整級(jí)別,就可以屏蔽掉很多調(diào)試相關(guān)的日志輸出竞帽。不同的日志實(shí)現(xiàn)定義的日志級(jí)別不太一樣扛施,不過(guò)也都大同小異。
Java Util Logging
Java Util Logging
定義了 7 個(gè)日志級(jí)別屹篓,從嚴(yán)重到普通依次是:
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
因?yàn)槟J(rèn)級(jí)別是 INFO疙渣,因此 INFO 級(jí)別以下的日志,不會(huì)被打印出來(lái)堆巧。
Log4j
Log4j
定義了 8 個(gè)日志級(jí)別(除去 OFF 和 ALL妄荔,可以說(shuō)分為 6 個(gè)級(jí)別),從嚴(yán)重到普通依次是:
- OFF:最高等級(jí)的恳邀,用于關(guān)閉所有日志記錄懦冰。
- FATAL:重大錯(cuò)誤灶轰,這種級(jí)別可以直接停止程序了谣沸。
- ERROR:打印錯(cuò)誤和異常信息,如果不想輸出太多的日志笋颤,可以使用這個(gè)級(jí)別乳附。
- WARN:警告提示内地。
- INFO:用于生產(chǎn)環(huán)境中輸出程序運(yùn)行的一些重要信息,不能濫用赋除。
- DEBUG:用于開(kāi)發(fā)過(guò)程中打印一些運(yùn)行信息阱缓。
- TRACE
- ALL 最低等級(jí)的,用于打開(kāi)所有日志記錄举农。
Logback
Logback
日志級(jí)別比較簡(jiǎn)單荆针,從嚴(yán)重到普通依次是:
- ERROR
- WARN
- INFO
- DEBUG
- TRACE
1.3 綜合對(duì)比
Java Util Logging
系統(tǒng)在 JVM
啟動(dòng)時(shí)讀取配置文件并完成初始化,一旦應(yīng)用程序開(kāi)始運(yùn)行颁糟,就無(wú)法修改配置航背。另外,這種日志實(shí)現(xiàn)配置也不太方便棱貌,只能在 JVM
啟動(dòng)時(shí)傳遞參數(shù)玖媚,像下面這樣:
-Djava.util.logging.config.file=<config-file-name>。
由于這些局限性婚脱,導(dǎo)致 Java Util Logging
并未廣泛使用今魔。
Log4j
雖然配置繁瑣,但是一旦配置完成障贸,使用起來(lái)就非常方便错森,只需要將相關(guān)的配置文件放到 classpath
下即可。在很多情況下篮洁,Log4j
的配置文件我們可以在不同的項(xiàng)目中反復(fù)使用问词。
Log4j
可以和 Apache Commons Logging
搭配使用,Apache Commons Logging
會(huì)自動(dòng)搜索并使用 Log4j
嘀粱,如果沒(méi)有找到 Log4j
激挪,再使用 Java Util Logging
。
比 Log4j
+ Apache Commons Logging
組合更得人心的是 Slf4j
+ Logback
組合锋叨。
Logback
是 Slf4j
的原生實(shí)現(xiàn)框架垄分,它也出自 Log4j
作者(Ceki Gülcü)之手,但是相比 Log4j
娃磺,它擁有更多的優(yōu)點(diǎn)薄湿、特性以及更強(qiáng)的性能。
1.4 最佳實(shí)踐
- 如果不想添加任何依賴(lài)偷卧,使用
Java Util Logging
或框架容器已經(jīng)提供的日志接口豺瘤。 - 如果比較在意性能,推薦:
Slf4j
+Logback
听诸。 - 如果項(xiàng)目中已經(jīng)使用了
Log4j
且沒(méi)有發(fā)現(xiàn)性能問(wèn)題坐求,推薦組合為:Slf4j
+Log4j2
。
2. Spring Boot 日志實(shí)現(xiàn)
Spring Boot 使用 Apache Commons Logging
作為內(nèi)部的日志框架門(mén)面晌梨,它只是一個(gè)日志接口桥嗤,在實(shí)際應(yīng)用中需要為該接口來(lái)指定相應(yīng)的日志實(shí)現(xiàn)须妻。
Spring Boot 默認(rèn)的日志實(shí)現(xiàn)是 Logback
。這個(gè)很好查看:隨便啟動(dòng)一個(gè) Spring Boot 項(xiàng)目泛领,從控制臺(tái)找一行日志荒吏,例如下面這樣:
考慮到最后的 prod 是一個(gè)可以變化的字符,我們?cè)陧?xiàng)目中全局搜索:The following profiles are active
渊鞋,結(jié)果如下:
在日志輸出的那一行 debug绰更。然后再次啟動(dòng)項(xiàng)目,如下圖:
此時(shí)我們就可以看到真正的日志實(shí)現(xiàn)是 Logback
锡宋。
其他的諸如 Java Util Logging
动知、Log4j
等框架,Spring Boot 也有很好的支持员辩。
在 Spring Boot 項(xiàng)目中盒粮,只要添加了如下 web 依賴(lài),日志依賴(lài)就自動(dòng)添加進(jìn)來(lái)了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.1 Spring Boot 日志配置
Spring Boot 的日志系統(tǒng)會(huì)自動(dòng)根據(jù) classpath 下的內(nèi)容選擇合適的日志配置奠滑,在這個(gè)過(guò)程中首選 Logback丹皱。
如果開(kāi)發(fā)者需要修改日志級(jí)別,只需要在 application.properties 文件中通過(guò) logging.level 前綴+包名
的形式進(jìn)行配置即可宋税,例如下面這樣:
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
如果你想將日志輸出到文件摊崭,可以通過(guò)如下配置指定日志文件名:
logging.file.name=javaboy.log
logging.file.name 可以只指定日志文件名,也可以指定日志文件全路徑杰赛,例如下面這樣:
logging.file.name=/Users/sang/Documents/javaboy/javaboy.log
如果你只是想重新定義輸出日志文件的路徑呢簸,也可以使用 logging.file.path
屬性,如下:
logging.file.path=/Users/sang/Documents/javaboy
如果想對(duì)輸出到文件中的日志進(jìn)行精細(xì)化管理乏屯,還有如下一些屬性可以配置:
- logging.logback.rollingpolicy.file-name-pattern:日志歸檔的文件名根时,日志文件達(dá)到一定大小之后,自動(dòng)進(jìn)行壓縮歸檔辰晕。
- logging.logback.rollingpolicy.clean-history-on-start:是否在應(yīng)用啟動(dòng)時(shí)進(jìn)行歸檔管理蛤迎。
- logging.logback.rollingpolicy.max-file-size:日志文件大小上限,達(dá)到該上限后含友,會(huì)自動(dòng)壓縮替裆。
- logging.logback.rollingpolicy.total-size-cap:日志文件被刪除之前,可以容納的最大大小窘问。
- logging.logback.rollingpolicy.max-history:日志文件保存的天數(shù)辆童。
日志文件歸檔這塊,小伙伴們感興趣可以自己試下惠赫,可以首先將 max-file-size 屬性調(diào)小把鉴,這樣方便看到效果:
logging.logback.rollingpolicy.max-file-size=1MB
然后添加如下接口:
@RestController
public class HelloController {
private static final Logger logger = getLogger(HelloController.class);
@GetMapping("/hello")
public void hello() {
for (int i = 0; i < 100000; i++) {
logger.info("hello javaboy");
}
}
}
訪問(wèn)該接口,可以看到最終生成的日志文件被自動(dòng)壓縮了:
application.properties 中還可以配置日志分組汉形。
日志分組能夠把相關(guān)的 logger 放到一個(gè)組統(tǒng)一管理纸镊。
例如我們可以定義一個(gè) tomcat 組:
logging.group.tomcat=org.apache.catalina,org.apache.coyote, org.apache.tomcat
然后統(tǒng)一管理 tomcat 組中的所有 logger:
logging.level.tomcat=TRACE
Spring Boot 中還預(yù)定義了兩個(gè)日志分組 web 和 sql,如下:
不過(guò)在 application.properties 中只能實(shí)現(xiàn)對(duì)日志一些非常簡(jiǎn)單的配置概疆,如果想實(shí)現(xiàn)更加細(xì)粒度的日志配置逗威,那就需要使用日志實(shí)現(xiàn)的原生配置,例如 Logback
的 classpath:logback.xml
岔冀,Log4j
的 classpath:log4j.xml
等凯旭。如果這些日志配置文件存在于 classpath 下,那么默認(rèn)情況下使套,Spring Boot 就會(huì)自動(dòng)加載這些配置文件罐呼。
2.2 Logback 配置
2.2.1 基本配置
默認(rèn)的 Logback
配置文件名有兩種:
-
logback.xml
:這種配置文件會(huì)直接被日志框架加載。 -
logback-spring.xml
:這種配置文件不會(huì)被日志框架直接加載侦高,而是由 Spring Boot 去解析日志配置嫉柴,可以使用 Spring Boot 的高級(jí) Profile 功能。
Spring Boot 中為 Logback
提供了四個(gè)默認(rèn)的配置文件奉呛,位置在 org/springframework/boot/logging/logback/
计螺,分別是:
- defaults.xml:提供了公共的日志配置,日志輸出規(guī)則等瞧壮。
- console-appender.xml:使用 CONSOLE_LOG_PATTERN 添加一個(gè)ConsoleAppender登馒。
- file-appender.xml:添加一個(gè) RollingFileAppender。
- base.xml:為了兼容舊版 Spring Boot 而提供的咆槽。
如果需要自定義 logback.xml
文件陈轿,可以在自定義時(shí)使用這些默認(rèn)的配置文件,也可以不使用秦忿。一個(gè)典型的 logback.xml
文件如下(resources/logback.xml):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>
可以通過(guò) include 引入 Spring Boot 已經(jīng)提供的配置文件麦射,也可以自定義。
2.2.2 輸出到文件
如果想禁止控制臺(tái)的日志輸出灯谣,轉(zhuǎn)而將日志內(nèi)容輸出到一個(gè)文件法褥,我們可以自定義一個(gè) logback-spring.xml
文件,并引入前面所說(shuō)的 file-appender.xml
文件酬屉。
像下面這樣(resources/logback-spring.xml
):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
2.3 Log4j 配置
如果 classpath 下存在 Log4j2
的依賴(lài)半等,Spring Boot 會(huì)自動(dòng)進(jìn)行配置。
默認(rèn)情況下 classpath 下當(dāng)然不存在 Log4j2
的依賴(lài)呐萨,如果想使用 Log4j2
杀饵,可以排除已有的 Logback
,然后再引入 Log4j2
谬擦,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
Log4j2
的配置就比較容易了切距,在 reources 目錄下新建 log4j2.xml 文件,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn">
<properties>
<Property name="app_name">logging</Property>
<Property name="log_path">logs/${app_name}</Property>
</properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d][%t][%p][%l] %m%n" />
</console>
<RollingFile name="RollingFileInfo" fileName="${log_path}/info.log"
filePattern="${log_path}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="INFO" />
<ThresholdFilter level="WARN" onMatch="DENY"
onMismatch="NEUTRAL" />
</Filters>
<PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="2 MB" />
</Policies>
<DefaultRolloverStrategy compressionLevel="0" max="10"/>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${log_path}/warn.log"
filePattern="${log_path}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="WARN" />
<ThresholdFilter level="ERROR" onMatch="DENY"
onMismatch="NEUTRAL" />
</Filters>
<PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="2 MB" />
</Policies>
<DefaultRolloverStrategy compressionLevel="0" max="10"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${log_path}/error.log"
filePattern="${log_path}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
<ThresholdFilter level="ERROR" />
<PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="2 MB" />
</Policies>
<DefaultRolloverStrategy compressionLevel="0" max="10"/>
</RollingFile>
</appenders>
<loggers>
<root level="info">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileWarn" />
<appender-ref ref="RollingFileError" />
</root>
</loggers>
</configuration>
首先在 properties 節(jié)點(diǎn)中指定了應(yīng)用名稱(chēng)以及日志文件位置惨远。
然后通過(guò)幾個(gè)不同的 RollingFile 對(duì)不同級(jí)別的日志分別處理谜悟,不同級(jí)別的日志將輸出到不同的文件话肖,并按照各自的命名方式進(jìn)行壓縮。
這段配置比較程式化葡幸,小伙伴們可以保存下來(lái)做成 IntelliJ IDEA 模版以便日常使用最筒。
3.小結(jié)
好啦,這就是松哥和小伙伴們分享的 Spring Boot 日志了蔚叨,整體來(lái)說(shuō)并不難床蜘,小伙伴們可以仔細(xì)品一品。
最后蔑水,松哥還搜集了 50+ 個(gè)項(xiàng)目需求文檔邢锯,想做個(gè)項(xiàng)目練練手的小伙伴不妨看看哦~