優雅哥 SpringBoot 2.7.2 實戰基礎 - 07 - 日志配置
Java 中日志相關的 jar 包非常多,log4j、log4j2、commons-logging、logback、slf4j 等,本文首先梳理這些包之間關系,然后介紹在 spring boot 中日志的配置,最后介紹多環境的配置,
1 日志框架歷史

1.1 log4j
很多年前,一個叫 Ceki Gülcü 的大佬在一個專案中開發跟蹤 API,這套跟蹤 API 逐步演變成 log4j, 大概1999年,log4j 成為 Apache 的一員,
1.2 JUL
JUL:java.util.logging.
Apache 覺得 log4j 很有價值,就推薦給 SUN 公司(Java 語言是由 SUN 公司的 James Gosling 發明的),希望 SUN 公司在 JDK 中加入 log4j,SUN 公司覺得加入日志 API 很有必要,但是又看不上 log4j,于是便自己搞了一套官方的,于 2002 年 JDK 1.4 中實作了 JUL,
此時,市面上就有兩套日志 API:來自 Apache 的 log4j 和來自官方的 JUL,
1.3 JCL(Jakarta Commons Logging)和 Simple Log
JCL:Jakarta Commons Logging.
log4j 和 JUL 是兩套不同的 API,一個出現較早、一個是官方的,兩個用戶群體都較大,如果在專案中想要切換日志框架就需要改動大量代碼,同時這也不符合”面向介面編程“的設計原則,Apache 就推出了 JCL 專案,名字看著高大上,但這玩意兒就是在 SSH、SSM 時代到處都能看見,那就是搭建框架時經常會看到的 commons-logging 包,該專案是一套日志的抽象層(后來大神們針對這種日志的抽象層一個高端的名字——日志門面),所謂”抽象層“,本質上就是一堆介面,有介面就需要有實作,沒有實作那就是自娛自樂,沒有鳥用,所以 Apache 針對 JCL 提供了一個默認實作,那就是 Simple Log,
JCL 基于動態系結來實作日志的記錄:開發程序中使用 JCL 定義的介面,程式運行的時候使用類路徑 classpath 中的具體實作(Simple Log、log4j、JUL),
可以和 JDBC 類比,Java 官方制定了資料庫訪問層持久化操作的標準 JDBC,各個資料庫廠商(Oracle、MySQL等)實作這套標準,JCL 也是想成為規范制定者,統一日志操作的規范,
JCL 的出現,日志體系顯得比較優雅,面向 JCL 的介面編程,可以很方便的切換日志框架,在這個時候, log4j他爹 Ceki`因為一些未知的原因離開了 Apache,
1.4 slf4j
slf4j:Simple Logging Facade for Java.
寫代碼的人大多有個共性:別人寫的都是垃圾、自己昨天寫的代碼也是垃圾,只有自己現在寫的才是最好的,Ceki 這哥們同樣覺得 JCL 不是特別完美,于是自己又搞了一套新版本的日志介面(高端的名字是:日志門面):slf4j,
問題來了,slf4j 只有介面沒有實作,難不成要讓 log4j 和 JUL 都來實作 slf4j 嗎?肯定是不可能的,
JCL 是采用動態系結機制,而 slf4j 采用”橋接包“,也就是分別開發 log4j 和 JUL 的橋接包,通過橋接包來適配兩者,大牛就是大牛,Ceki提供了這些橋接包 slf4j-log4j、slf4j-jdk14等,由于 JCL 出現比 slf4j 早,很多專案使用了 JCL,所以這大佬也提供了 slf4j-jcl,
還有一種常見:在某個專案中使用了一個第三方框架,這個專案使用了 slf4j 和 log4j,而依賴的第三方框架使用了 JCL 和 JUL,這時候系統就會有兩種日志組態檔和兩種列印方式,亂七八糟的, Ceki Gülcü 也考慮到這種場景,沒有他的橋接包搞不定的場景,于是就弄了個 jcl-over-slf4j 的橋接包...
1.5 logback
Ceki Gülcü 弄了 slf4j 和一堆橋接包,2006 年為 slf4j 提供了一個很厲害的實作:logback,與此同時他還特意寫了一篇文章《Reasons to prefer logback over log4j》,畢竟 log4j 也出自于他的手,里面存在什么問題他最清楚,事實上,logback 的性能和設計確實比 log4j 更厲害,與時俱進嘛,
1.6 log4j2
logback 的出現讓 Apache 坐不住了,2012年推出了新專案 log4j2,看名字像是 log4j的升級版,實際上是一個全新的玩意,它不兼容log4j,log4j2 幾乎包括了 logback的特性,競爭是殘酷的,與slf4j類似,log4j2也想統一日志的天下,也弄了一堆橋接包,通過橋接包 log4j-xxx 去兼容上面各種各樣的日志框架...
亂七八糟扯了一堆,核心就三個概念:
-
日志門面:JCL、slf4j
-
日志產品:log4j、JUL、logback、log4j2
-
橋接包:slf4j-xxx、log4j-xxx
2 Spring Boot 日志配置
Spring Boot 底層默認使用 slf4j 和 logback 的方式記錄日志,咱們 demo 工程中依賴了 spring-boot-starter-web,它又依賴了 spring-boot-starter-logging,所以不需要再手動添加該依賴,
在 Spring Boot 中,application.yml 支持部分 logback 的日志配置,但一些高級配置只能通過獨立的 xml 組態檔實作,經過 Spring Boot 的整合后,可支持多環境配置,但 logback 組態檔需要命名為 logback-spring.xml,如果使用了自定義日志組態檔,application.yml中 logging 有關配置就會失效,
2.1 springboot 默認的 logback 配置
SpringBoot 默認提供了一套 logback 的組態檔,位于 spring-boot依賴中的 org/springframework/boot/logging/logback/base.xml,

該檔案引入了三個 xml 檔案,并設定了root的日志級別為 info,console-appender.xml 和 file-appender.xml 中定義了日志的追加器,分別是名為 CONSOLE 的控制臺追加器 和 名為 FILE 的檔案追加器,
org/springframework/boot/logging/logback/defaults.xml 定義了 logback 的轉換器、一些包的日志級別、日志顯示格式,

默認在控制臺中顯示彩色日志,就是因為使用了轉換器 ColorConverter,顯示的格式為 CONSOLE_LOG_PATTERN 中使用了該轉換器,
在我們的自定義配置中可以復用這個 default.xml 和 console-appender.xml,
2.2 自定義配置
在 src/main/resources/下創建組態檔 logback-spring.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>
</configuration>
上面的配置引入了 spring boot 中 logback 的默認配置和 CONSOLE 追加器,并定義了 root 的日志級別為 info,
2.3 日志級別
日志有五個級別:trace、debug、info、warn、error,級別依次較高,配置了某個級別,就會輸出該級別及其以上的級別,如,配置日志級別為 warn,則日志會輸出 warn、error;如果配置日志級別為 debug,則會輸出 debug、info、warn、error,
在 DemoController 的 hello 方法中添加日志輸出,測驗日志級別:
@RestController
@RequestMapping("demo")
public class DemoController {
private Logger logger = LoggerFactory.getLogger(DemoController.class);
@GetMapping("hello")
public String hello(String msg) {
String result = "Hello Spring Boot ! " + msg;
System.out.println(result);
logger.error("error log");
logger.warn("warn log");
logger.info("info log");
logger.debug("debug log");
logger.trace("trace log");
return result;
}
}
注意,引入的 Logger 和 LoggerFactory 兩個類都是 slf4j 包下面的,上面的代碼分別輸出五個級別的日志,啟動服務,訪問 hello 介面,控制臺輸出:

控制臺值輸出 info、warn、error,可以看出 SpringBoot 默認輸出級別為 info,可通過配置細粒度調整日志的級別:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
...
<logger name="com.yygnb.demo" level="trace"/>
...
</configuration>
上面按照包名更改了日志的顯示級別,com.yygnb.demo 包下面的日志都是 trace 級別,重新訪問 hello 介面,error、warn、info、debug、trace 都會全部列印出來,
如果使用了 lombok,可以不用手動創建 logger 物件,使用注解 @Slf4j 后,就能直接在代碼中使用 log 物件:
@Slf4j
@RestController
@RequestMapping("demo")
public class DemoController {
@GetMapping("hello")
public String hello(String msg) {
String result = "Hello Spring Boot ! " + msg;
System.out.println(result);
log.error("error log");
log.warn("warn log");
log.info("info log");
log.debug("debug log");
log.trace("trace log");
return result;
}
}
關于是否應該使用 lombok,網上各種義正言辭、牽強附會的說辭都有,甚至有些標題黨寫著《我們公司的技術總監規定xxxx》,我只能說遵守公司或專案規定就行,
2.4 檔案追加器
上面復用了 SpringBoot 自帶的控制臺追加器 CONSOLE,這里自定義檔案追加器:
...
<appender name="FILE" >
<filter >
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<rollingPolicy >
<fileNamePattern>logs/hero-springboot-demo.%d.log</fileNamePattern>
<MaxHistory>100</MaxHistory>
</rollingPolicy>
</appender>
...
并在 root 中添加這個自定義 FILE 追加器:
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
上面的 FILE 追加器,日志路徑為專案根路徑下的 logs 目錄,日志名稱形如 hero-springboot-demo.2022-08-02.log,啟動服務,訪問 hello 介面,測驗日志檔案是否生成,
2.5 多環境日志
假設希望在 local 時,只輸出控制臺日志;在其他環境(dev、test等)輸出控制臺日志和檔案日志,SpringBoot 提供了 springProfile 標簽,通過該元素 name 屬性指定環境,修改 root 元素:
<root level="INFO">
<springProfile name="local">
<appender-ref ref="CONSOLE" />
</springProfile>
<springProfile name="!local">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</springProfile>
</root>
分別使用 local 和 dev 啟動服務,測驗多環境是否生效,
我們自定義的 logback-spring.xml 充分利用了 Spring Boot 官方提供的配置,最終完整配置如下:
<?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" />
<logger name="com.yygnb.demo" level="trace"/>
<appender name="FILE" >
<filter >
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<rollingPolicy >
<fileNamePattern>logs/hero-springboot-demo.%d.log</fileNamePattern>
<MaxHistory>100</MaxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<springProfile name="local">
<appender-ref ref="CONSOLE" />
</springProfile>
<springProfile name="!local">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</springProfile>
</root>
</configuration>

/ 程式員優雅哥(youyacoder),今日學習到此結束~~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501893.html
標籤:其他
上一篇:一文說透kafka底層架構
