閱讀517 返回首頁    go 阿裏雲 go 技術社區[雲棲]


使用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這類流行的日誌實現,我們隻需要添加簡單的配置,就能開啟對這些日誌實現的支持。
  為了便於描述,我們將以上提到的“日誌實現”統稱為“日誌框架”。
  大家可以通過以下網站,進一步學習這類日誌框架。

  在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日誌特性,可參考它的官方技術文檔。

  目前,雖然日誌已經成功輸出到文件中,但是我們的微服務是以Docker容器的方式來運行的,此時輸出的日誌文件仍然和應用程序在一個Docker容器中,我們得想辦法將日誌文件輸出到Docker容器外。也就是說,需要將數據與程序相分離,以便後續更加方便地獲取並分析日誌內容。

3 將日誌輸出到Docker容器外

  最容易想到的辦法就是,通過Docker數據卷的方式,將文件路徑掛載到Docker容器上,這樣日誌文件就自然與Docker文件分離了,就像下麵這樣啟動Docker容器。

docker run -v ~/logs:~/logs hello

  這樣一來,我們可隨時在宿主機上查看Docker容器內部的日誌了。但是回過頭想想,卻不難發現,其實完全不需要將日誌輸出到文件中,因為即便將日誌輸出到控製台中,我們也能隨時通過docker logs的方式來獲取日誌內容,將日誌輸出到文件似乎有些多餘了,還占用了磁盤空間。
  那我們現在做的事情是否有意義呢?感興趣的讀者不妨到《架構探險:輕量級微服務架構(下冊)》一書中探索詳情~
  本文選自《架構探險:輕量級微服務架構(下冊)》,點此鏈接可在博文視點官網查看此書。
                 圖片描述
  想及時獲得更多精彩文章,可在微信中搜索“博文視點”或者掃描下方二維碼並關注。
                    圖片描述

最後更新:2017-09-06 16:32:35

  上一篇:go  OSS訪問驗證流程
  下一篇:go  Java常用算法1——冒泡排序