使用Spring Boot日誌框架在已有的微服務代碼中添加日誌功能
引言:我們需要在已有的微服務代碼中添加日誌功能,用於輸出需要關注的內容,這是最平常的技術需求了。由於我們的微服務代碼是基於SpringBoot開發的,那麼問題就轉換為如何在Spring Boot應用程序中輸出相應的日誌。
在傳統Java應用程序中,我們一般會使用類似Log4j這樣的日誌框架來輸出日誌,而不是直接在代碼中通過System.out.println()來輸出日誌。為什麼要這麼做呢?原因有兩點。其一,我們希望日誌能輸出到文件中,而不是輸出到應用程序的控製台中,這樣更加容易收集和分析。其二,我們可以通過異步多線程的方式,將日誌輸出到文件中,這樣既不會影響主線程,可以提高應用程序的吞吐率,也是一種節省性能開銷的方法。直接將內容打印到控製台中,這種做法比較粗暴,不是業界所推薦的做法。
這樣一來,我們最終要解決的問題就非常清楚了,那就是如何在Spring Boot中添加日誌框架。幸運的是,Spring Boot自帶了一款名為Spring Boot Logging的插件(在Spring Boot中,稱插件為Starter),它已經為我們提供了日誌功能。
1 使用Spring Boot Logging插件
Spring Boot使用Apache開源項目Commons Logging作為內部的日誌框架,它是一個日誌接口,在實際應用中,我們需要為該接口指定相應的日誌實現。Spring Boot默認的日誌實現是Java Util Logging,它是JDK自帶的日誌包,一般場景下很少會用到。此外,Spring Boot也提供了Log4J、Logback這類流行的日誌實現,我們隻需要添加簡單的配置,就能開啟對這些日誌實現的支持。
為了便於描述,我們將以上提到的“日誌實現”統稱為“日誌框架”。
大家可以通過以下網站,進一步學習這類日誌框架。
- Commons Logging官網:https://commons.apache.org/proper/commons-logging/。
- Log4J官網:https://logging.apache.org/log4j/2.x/。
- Logback官網:https://logback.qos.ch/。
在Java應用程序中,日誌一般分為以下5個級別。
- ERROR:錯誤信息;
- WARN:警告信息;
- INFO:一般信息;
- DEBUG:調試信息;
- TRACE:跟蹤信息。
以上日誌級別按照嚴重程度,從高往低排序,一般常用的三種日誌級別是ERROR、INFO、DEBUG。Spring Boot Logging插件默認輸出到INFO級別,也就是說,隻包含ERROR、WARN、INFO,不包含DEBUG、TRACE。如果我們希望日誌可以輸出到DEBUG級別,則需在Spring Boot的application.properties文件中添加如下配置:
logging.level.root=DEBUG
重新運行應用程序,我們就可在代碼中看到DEBUG級別的日誌了。
以下是Spring Boot的應用程序代碼片段,我們使用SLF4J類庫輸出日誌,而不要使用具體的日誌實現類庫,比如Log4J。
package demo.msa;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
@RestController
@SpringBootApplication
public class HelloApplication {
private static Logger logger =LoggerFactory.getLogger(HelloApplication.class);
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
@GetMapping("/hello")
public String hello() {
logger.debug("log..."); // 輸出DEBUG級別的日誌
return "hello";
}
}
運行以上Spring Boot應用程序,會發現控製台中輸出了大量INFO級別的日誌,這些日誌是由Spring Boot框架輸出的。因為我們調整日誌輸出到DEBUG級別,而INFO級別在DRBUG級別之上,所以INFO級別的日誌也會輸出,但TRACE級別的日誌不會輸出。
當我們打開瀏覽器,發送https://localhost:8080/hello請求時,可在控製台中看到我們想要輸出的DEBUG級別日誌。
如果我們不想關注Spring Boot框架的日誌,則可將日誌級別統一設置為ERROR,此時隻會輸出ERROR級別的日誌。隨後,再將Spring Boot應用程序指定的包(應用程序所對應的包)設置為DEBUG級別的日誌,此時我們看到的就隻是指定包中的日誌了。
logging.level.root=ERROR
logging.level.demo.msa=DEBUG
上麵的logging.level.root表示所有包,logging.level.demo.msa表示應用程序的指定包(demo.msa是包名)。以上配置可以理解為,整個應用程序的日誌輸出到ERROR級別,除了demo.msa包中的日誌輸出到DEBUG級別。這是一種“先禁止所有,再允許個別”的配置方法,這種配置方法在很多技術中都應用過。
默認情況下日誌框架會將日誌輸出到控製台中,我們需要在application.properties文件中添加如下配置,才能將日誌輸出到文件中:
logging.file=${user.home}/logs/hello.log
其中,${user.home}表示當前用戶目錄(該變量由Spring Boot框架在運行時傳入),後麵的/logs/hello.log是相對於該目錄的路徑。大家可根據實際情況,設置所需的日誌文件路徑,以上僅為示例。
重新運行應用程序,就能看到日誌輸出到指定路徑下的文件中了。
目前我們雖然可以將日誌輸出到文件中,但控製台中仍然會輸出同樣的日誌,這不是我們最終想要的效果。我們希望的是日誌全部輸出到文件中,控製台中不輸出任何日誌。也就是說,我們需要關閉控製台中的輸出。通過以上的嚐試,我們不難發現,僅通過修改Spring Boot的配置,貌似是無法做到的。
下麵我們不妨考慮集成經典的Log4J日誌框架,看看能否實現我們的需求。
2 集成Log4J日誌框架
Spring Boot Logging默認集成了Logback,我們隻需提供Logback的配置文件就能開啟Logback日誌功能,但我們現在想要嚐試的是自己熟知的Log4J,而不是比較新潮的Logback。毫不猶豫,現在我們就來開啟對Log4J的支持。通過學習Spring Boot的官方文檔與示例代碼,我們了解到,隻需在pom.xml文件中添加如下Maven配置,就能在Spring Boot中集成Log4J。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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>
在第一段dependency配置中,我們排除掉spring-boot-starter-logging依賴是因為要去掉默認集成的Logback日誌功能。在第二段dependency配置中,我們自行添加了spring-boot-starter- log4j2依賴,它是Spring Boot所提供的Log4J插件,此時使用的是Log4J的2.x版本。
當完成了Maven依賴配置以後,我們接下來需要在源碼中的resources目錄下添加log4j2.xml文件,其內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appenders>
<File name="file" fileName="${sys:user.home}/logs/hello.log">
<PatternLayout pattern="%d{HH:mm:ss,SSS} %p %c (%L) - %m%n"/>
</File>
</appenders>
<loggers>
<root level="ERROR">
<appender-ref ref="file"/>
</root>
<logger name="demo.msa" level="DEBUG"/>
</loggers>
</configuration>
log4j2.xml配置文件分為兩大部分,即appenders與loggers。在appenders中,我們添加了一個File類型的appenders,表示日誌以文件的方式進行輸出,該文件路徑基於根目錄${sys:user.home},即當前用戶目錄(該變量由Log4J框架在運行時傳入)。此外,還需指定PatternLayout為日誌輸出格式。在loggers中,我們先後添加了兩段配置,第一段的root表示將所有包中的日誌輸出到ERROR級別,第二段的logger表示將指定包demo.msa中的日誌輸出到DEBUG級別。很明顯,這段配置與之前在Spring Boot中配置的意義相同。
通過以上配置,可將Log4J集成到Spring Boot應用中。
重新運行應用程序,日誌不再輸出到控製台中,而是全部輸出到指定路徑下的文件中了。
大家如果想了解更為詳盡的Spring Boot日誌特性,可參考它的官方技術文檔。
- Spring Boot日誌: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html。
目前,雖然日誌已經成功輸出到文件中,但是我們的微服務是以Docker容器的方式來運行的,此時輸出的日誌文件仍然和應用程序在一個Docker容器中,我們得想辦法將日誌文件輸出到Docker容器外。也就是說,需要將數據與程序相分離,以便後續更加方便地獲取並分析日誌內容。
3 將日誌輸出到Docker容器外
最容易想到的辦法就是,通過Docker數據卷的方式,將文件路徑掛載到Docker容器上,這樣日誌文件就自然與Docker文件分離了,就像下麵這樣啟動Docker容器。
docker run -v ~/logs:~/logs hello
這樣一來,我們可隨時在宿主機上查看Docker容器內部的日誌了。但是回過頭想想,卻不難發現,其實完全不需要將日誌輸出到文件中,因為即便將日誌輸出到控製台中,我們也能隨時通過docker logs的方式來獲取日誌內容,將日誌輸出到文件似乎有些多餘了,還占用了磁盤空間。
那我們現在做的事情是否有意義呢?感興趣的讀者不妨到《架構探險:輕量級微服務架構(下冊)》一書中探索詳情~
本文選自《架構探險:輕量級微服務架構(下冊)》,點此鏈接可在博文視點官網查看此書。
想及時獲得更多精彩文章,可在微信中搜索“博文視點”或者掃描下方二維碼並關注。
最後更新:2017-09-06 16:32:35