Spring Boot 日志

Spring Boot 日志

《Spring Boot 開發實戰》—— 基于 Gradle + Kotlin的企業級應用開發最佳實踐

在任何一個生產系統中,對日志的合理記錄是非常重要的。這對系統故障的定位處理極其關鍵。Spring Boot支持Java Util Logging,Log4j2,Lockback作為日志框架, Spring Boot使用Logback作為默認日志框架。無論使用哪種日志框架,Spring Boot都支持配置將日志輸出到控制臺或者文件中。
本章我們來詳細介紹 Spring Boot 應用的日志的配置與使用。

1.1 SLF4J與Logback簡介

Java日志框架眾多,常用的有java.util.logging、log4j、 logback、commons-logging等。本節簡單介紹logback日志框架。

1.1.1 SLF4J日志接口

SLF4J (Simple Logging Facade For Java),它是一個針對于各類Java日志框架的統一Facade抽象。SLF4J定義了統一的日志抽象接口,而真正的日志實現則是在運行時決定。
LogBack是由log4j的創始人開發的新一代日志框架,用于替代log4j。它效率更高、能夠適應諸多的運行環境。LogBack的架構設計足夠通用,可適用于不同的環境。目前LogBack分為三個模:lobback-core,logback-classic和logback-access。core模塊是其它兩個模塊的基礎,classic是core的擴展,是log4j巨大改進的版本。LogBack-classic本身實現了SL4J的API,因此可以很容易的在logback與其它日志系統之間轉換,例如log4j、JDK1.4中的java.util.logging(JUL)。第三個模塊access,它集成了Servlet容器,提供了通過HTTP訪問日志的功能,詳細了解access可訪問文檔: http://logback.qos.ch/access.html
LogBack的日志級別有trace、debug、info、warn、error,級別排序為:
TRACE < DEBUG < INFO < WARN < ERROR

提示:關于日志級別詳細信息,可參考官方文檔: http://logback.qos.ch/manual/architecture.html

一般情況下,我們不需要單獨引入spring-boot-starter-logging,因為這是spring-boot-starter默認引入的依賴。其依賴樹如下

從上面的依賴樹,我們可以看出,spring-boot-starter-logging依賴logback-classic, logback-classic依賴logback-core, sl4j-api。
Spring Boot為我們提供了功能齊全的默認日志配置,基本上就是“開箱即用”。
默認情況下,Spring Boot的日志是輸出到控制臺的,不寫入任何日志文件。
要讓Spring Boot輸出日志文件,最簡單的方式是在application.properties配置文件中配置logging.path鍵值,如下:
logging.path={user.home}/logs 這樣在{user.home}/logs目錄下會生成默認的文件名命名的日志文件spring.log。
我們可以在application.properties配置文件中配置logging.file鍵值,如下:
spring.application.name=lightsword
logging.file={user.home}/logs/{spring.application.name}.log
這樣日志文件的名字就是lightsword.log了。另外,二者不能同時使用,如同時使用,則只有logging.file生效。

1.2 配置logback日志

Spring Boot 提供了一套日志系統,優先選擇logback。日志服務一般都在ApplicationContext創建前就初始化了,所以日志配置,可以獨立于Spring的配置。我們也可以通過系統屬性和傳統的Spring Boot外部配置文件,實現日志控制和管理。

根據不同的日志系統,SpringBoot按如下“約定規則”組織配置文件名加載日志配置文件:
日志框架
配置文件
Logback
logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j
log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2
log4j2-spring.xml, log4j2.xml
JDK (Java Util Logging)
logging.properties

Spring Boot官方推薦優先使用帶有-spring的文件名作為你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名為logback-spring.xml的日志配置文件,spring boot可以為它添加一些spring boot特有的配置項。
LogBack讀取配置或屬性文件的步驟是:

  1. LogBack在類路徑下嘗試查找logback.groovy的文件。
  2. 如果logback.groovy沒有找到,就在類路徑下查找logback-test.xml文件。
  3. 若logback-test.xml文件沒有找到,就會在類路徑下查找logback.xml文件。

我們也可以自定義logback.xml名稱,然后在application.properties中指定它。例如:

#logging
logging.config=classpath:logback-dev.groovy

要把這個logback-dev.groovy配置文件放到類路徑下。如下圖

我們在application.properties指定環境

spring.profiles.active=daily

對應的application-daily.properties指定日志的配置文件如下

#logging
logging.config=classpath:logback-daily.groovy

另外,如果我們沒有配置任何的logback.xml、logback-*.groovy等文件,LogBack就會使用BasicConfigurator啟動默認配置,該配置會將日志輸出到控制上。這樣就意味著使用缺省配置,它提供了默認的最基礎的日志功能。

1.3 logback.groovy配置文件

本節介紹 logback 配置文件的具體內容。

1.3.1 顯示系統 Log 級別

我們首先編寫一個/log 接口來展示當前系統的日志級別。代碼如下

package com.easy.springboot.demo_logging

import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class LoggerController {
    val log = LoggerFactory.getLogger(LoggerController::class.java)

    @GetMapping("/log")
    fun log(): String {
        var logLevel = ""

        if (log.isTraceEnabled) {
            log.trace("5-TRACE")
            logLevel += "5-TRACE|"
        }

        if (log.isDebugEnabled) {
            log.debug("4-DEBUG")
            logLevel += "4-DEBUG|"
        }

        if (log.isInfoEnabled) {
            log.info("3-INFO")
            logLevel += "3-INFO|"
        }

        if (log.isWarnEnabled) {
            log.warn("2-WARN")
            logLevel += "2-WARN|"
        }

        if (log.isErrorEnabled) {
            log.error("1-ERROR")
            logLevel += "1-ERROR|"
        }
        return logLevel
    }
}

1.3.2 使用logback.groovy配置

領域特定語言(Domain-specific languages ,DSL)用途非常廣泛。logback.xml配置文件繁瑣而冗長。Groovy是一門優秀的DSL。logback框架支持logback.groovy簡潔的DLS風格的配置。詳細的配置語法介紹可以參考:https://logback.qos.ch/manual/groovy.html。同時,logback提供了直接把logback.xml轉換成logback.groovy的工具:https://logback.qos.ch/translator/asGroovy.html (測試過,這個工具include標簽暫時未作解析)。
推薦使用 Groovy DSL 作為logback 日志配置文件的最佳實踐。配置logback-daily.groovy如下:

import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.filter.ThresholdFilter
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy

import java.nio.charset.Charset

import static ch.qos.logback.classic.Level.INFO

def USER_HOME = System.getProperty("user.home")
def APP_NAME = "demo_logging"
def LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg %n"
def LOG_FILE = "${USER_HOME}/logs/${APP_NAME}"
def FILE_NAME_PATTERN = "${APP_NAME}.%d{yyyy-MM-dd}.log"

scan("60 seconds")

context.name = "${APP_NAME}"
jmxConfigurator()

logger("org.springframework.web", INFO)
logger("com.easy.springboot.demo_logging", INFO)

appender("CONSOLE", ConsoleAppender) {
    encoder(PatternLayoutEncoder) {
        pattern = "${LOG_PATTERN}"
        charset = Charset.forName("utf8")
    }
}

appender("dailyRollingFileAppender", RollingFileAppender) {
    file = "${LOG_FILE}"
    rollingPolicy(TimeBasedRollingPolicy) {
        fileNamePattern = "${FILE_NAME_PATTERN}"
        maxHistory = 30
    }
    filter(ThresholdFilter) {
        level = INFO
    }
    encoder(PatternLayoutEncoder) {
        pattern = "${LOG_PATTERN}"
    }
}
root(INFO, ["CONSOLE", "dailyRollingFileAppender"])

上面的 Groovy 配置文件等價于如下的logback-daily.xml配置文件內容:

<?xml version="1.0" encoding="UTF-8"?>
<!--scan:當此屬性設置為true時,配置文件如果發生改變,將會被重新加載,默認值為true。-->
<!--scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒。當scan為true時,此屬性生效。默認的時間間隔為1分鐘。-->
<!--debug:當此屬性設置為true時,將打印出logback內部日志信息,實時查看logback運行狀態。默認值為false。-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <property name="APP_NAME" value="lightsword"/>
    <contextName>${APP_NAME}</contextName>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <jmxConfigurator/>

    <logger name="org.springframework.web" level="INFO"/>
    <logger name=" com.easy.springboot.demo_logging " level="TRACE"/>

    <appender name="dailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${user.home}/logs/${APP_NAME}</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rolling over -->
            <FileNamePattern>${APP_NAME}.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!-- keep 30 days' log history -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg %n</Pattern>
        </encoder>
    </appender>
    <!--TRACE, DEBUG, INFO, WARN, ERROR-->
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
        <!--<appender-ref ref="FILE"/>-->
        <appender-ref ref="dailyRollingFileAppender"/>
    </root>
</configuration>

我們可以看出,使用groovy表達的配置,更加簡潔、富表現力。只需要在application.properties里面配置logging.config指定日志配置文件即可:

#logging
logging.config=classpath:logback-dev.groovy

另外,需要在build.gradle中添加groovy依賴:

dependencies {
    ...
    compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.15'
}

完成上述配置,即可使用跟logback.xml配置一樣的日志功能了。

1.3.3 配置文件說明

在logback.xml形式配置文件內,總體結構是:最頂層是一個<configuration>標簽,在<configuration>標簽下可以有0到n個<appender>標簽,0到n個<logger>標簽,最多只能有1個<root>標簽,以及其他一些高級配置。下面我們針對上面的logback.xml配置文件作簡要說明。

1.configuration 節點
配置文件中的根節點中的<configuration scan="true" scanPeriod="60 seconds" debug="false"> 包含的屬性簡單說明如下:
? scan:當此屬性設置為true時,配置文件如果發生改變,將會被重新加載,默認值為true。
? scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒。當scan為true時,此屬性生效。默認的時間間隔為1分鐘。
? debug:當此屬性設置為true時,將打印出logback內部日志信息,實時查看logback運行狀態。默認值為false。

2.jmxConfigurator

<jmxConfigurator/>標簽對應 Groovy 配置腳本中的jmxConfigurator()。這個配置是開啟JMX的功能。JMX(Java Management Extensions,即Java管理擴展)是一個為應用程序、設備、系統等植入管理功能的框架。JMX可以跨越一系列異構操作系統平臺、系統體系結構和網絡傳輸協議,靈活的開發無縫集成的系統、網絡和服務管理應用。
有了這個配置,我們可以直接在命令行輸入:jconsole ,這個命令會啟動jconsole的GUI界面。如下圖

點擊到MBeans Tab,我們可以看到關于logback的信息:

啟動系統,采用spring.profiles.active=daily配置,日志級別是
logger("com.easy.springboot.demo_logging", INFO)
訪問http://127.0.0.1:8080/log ,響應輸出:“3-INFO|2-WARN|1-ERROR|”。
然后,點擊“setLoggerLevel”,我們可以在設置界面動態修改系統的日志級別:

設置 p1參數為:com.easy.springboot.demo_logging,設置 p2參數為: TRACE,點擊“setLoggerLevel” 按鈕,提示“Method successfully invoked”,如下圖:

再次訪問http://127.0.0.1:8080/log ,響應輸出:“5-TRACE|4-DEBUG|3-INFO|2-WARN|1-ERROR|”。我們可以看出系統的日志級別已經變成了TRACE 。

提示:關于jconsole的詳細介紹, 可以參考:https://docs.oracle.com/javase/8/docs/technotes/guides/management/jconsole.html

3.Logger節點

在配置<logger name="org.springframework.web" level="INFO"/>中,我們定義了一個 捕獲 org.springframework.web 的日志,日志級別是 DEBUG。一個捕獲com.springboot.in.action的日志,日志級別是TRACE。
上面引用的org/springframework/boot/logging/logback/base.xml 文件是SpringBoot內置的,其內容為:

<?xml version="1.0" encoding="UTF-8"?>
<included>
    <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/console-appender.xml" />
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</included>

其中,引用的defaults.xml,console-appender.xml,file-appender.xml都在同一個目錄下。默認情況下包含兩個appender——一個是控制臺,一個是文件,分別定義在console-appender.xml和file-appender.xml中。這里面的內容就是SpringBoot默認實現的logback的日志配置。
Spring Boot的日志模塊里,預定義了一些系統變量:
? PID,當前進程ID
? LOG_FILE,Spring Boot配置文件中logging.file的值
? LOG_PATH, Spring Boot配置文件中logging.path的值
? CONSOLE_LOG_PATTERN, Spring Boot配置文件中logging.pattern.console的值
? FILE_LOG_PATTERN, Spring Boot配置文件中logging.pattern.file的值。
對于應用的日志級別也可以通過application.properties進行定義:
logging.level.org.springframework.web=DEBUG
這樣相當于我們在logback.xml 中配置的對應的日志級別。名稱以logging.level開頭,后面跟要輸入日志的包名。
另外,如果在 logback.xml 和 application.properties 中定義了相同的配置(如都配置了 org.springframework.web)但是輸出級別不同,由于application.properties 的優先級高于 logback.xml ,所以會使用application.properties的配置。

  1. ConsoleAppender

Logback使用appender來定義日志輸出,在開發過程中最常用的是將日志輸出到控制臺。我們直接使用SpringBoot內置的ConsoleAppender配置。這個配置的內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<included>
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <appender name="DEBUG_LEVEL_REMAPPER" class="org.springframework.boot.logging.logback.LevelRemappingAppender">
        <destinationLogger>org.springframework.boot</destinationLogger>
    </appender>

    <logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
    <logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
    <logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
    <logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/>
    <logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
    <logger name="org.crsh.plugin" level="WARN"/>
    <logger name="org.crsh.ssh" level="WARN"/>
    <logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/>
    <logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
    <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="WARN"/>
    <logger name="org.springframework.boot.actuate.endpoint.jmx" additivity="false">
        <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
    </logger>
    <logger name="org.thymeleaf" additivity="false">
        <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
    </logger>
</included>
<included>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
</included>

其中,
? charset,表示對日志進行編碼
? pattern簡單說明:
? %d{HH:mm:ss.SSS}——日志輸出時間
? %thread——輸出日志的進程名字,方括號括起來。這個信息在Web應用以及異步任務處理中很有用。
? %-5level——日志級別,并且使用5個字符靠左對齊
? %logger{36}——日志輸出者的名字
? %msg——日志消息
? %n——平臺的換行符
在這種格式下一條日志的輸出內容格式如下:

02:37:22.752 [http-nio-8888-exec-1] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6fab4e5e: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffe3f86: RemoteIpAddress: 127.0.0.1; SessionId: E30F2AF513F94C7FC7611353B61A26C6; Granted Authorities: ROLE_ANONYMOUS'

5.RollingFileAppender

另一種通用功能是將日志輸出到文件。同時,隨著應用的運行時間越來越長,日志也會增長的越來越多,將他們輸出到同一個文件并非一個好辦法。我們有RollingFileAppender用于切分文件日志:

<appender name="dailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${user.home}/logs/${APP_NAME}</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rolling over -->
            <FileNamePattern>${APP_NAME}.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!-- keep 30 days' log history -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg %n</Pattern>
        </encoder>
</appender>

其中,核心的配置部分是是rollingPolicy的定義。上面的

<FileNamePattern>${APP_NAME}.%d{yyyy-MM-dd}.log</FileNamePattern>
<maxHistory>30</maxHistory>

其中,
? FileNamePattern,定義了日志的切分方式——把每一天的日志歸檔到一個文件中。
? maxHistory,30表示只保留最近30天的日志,以防止日志填滿整個磁盤空間。我們也可以使用%d{yyyy-MM-dd_HH-mm}來定義精確到分的日志切分方式。

6.Threshold filter

ThresholdFilter是 logback定義的日志打印級別的過濾器。例如配置ThresholdFilter來過濾掉ERROR級別以下的日志不輸出到文件中:

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <level>ERROR</level>
</filter>

7.root節點

關于 root 節點的配置如下:

<root level="DEBUG">
    <appender-ref ref="CONSOLE"/>
    <!--<appender-ref ref="FILE"/>-->
    <appender-ref ref="dailyRollingFileAppender"/>
</root>

root節點是必選節點,用來指定最基礎的日志輸出級別,只有一個level屬性。level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能設置為INHERITED或者同義詞NULL。默認是DEBUG。

1.4 本章小結

Spring Boot 集成logback日志框架非常簡單。同時,使用基于 Groovy DSL的 logback.groovy 配置文件,風格簡潔優雅。使用 spring.profile 配置多環境(dev、daily、prod 等)的日志配置文件也非常簡單方便。通過配置jmxConfigurator我們可以在 jconsole 管理后臺動態修改系統的日志級別。

提示:本章實例工程源代碼 https://github.com/EasySpringBoot/demo_logging

新書上架:《Spring Boot 開發實戰》

— 基于 Kotlin + Gradle + Spring Boot 2.0 的企業級服務端開發實戰

京東下單鏈接

https://item.jd.com/31178320122.html

天貓下單鏈接

https://detail.tmall.com/item.htm?id=574928877711

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容