Springboot 日志(待續)

簡單的說,日志就是記錄程序的運行軌跡,方便查找關鍵信息,也方便快速定位解決問題。
本篇文章分為三部分講解:

  • 常用日志框架
  • SpringBoot 配置Logback
  • 阿里日志規約

常用日志框架

  • Logging
    這是 Java 自帶的日志工具類,在 JDK 1.5 開始就已經有了,在 java.util.logging 包下。
  • Log4j
    Log4j 是 Apache 的一個開源日志框架,也是市場占有率最多的一個框架。大多數沒用過 Java Logging, 但沒人敢說沒用過 Log4j 吧,反正從我接觸 Java 開始就是這種情況,做 Java 項目必有 Log4j 日志框架。
  • commons-logging
    commons-logging 就是日志的門面接口,它也是 apache 最早提供的日志門面接口,用戶可以根據喜好選擇不同的日志實現框架,而不必改動日志定義,這就是日志門面的好處,符合面對接口抽象編程。
  • Slf4j
    全稱:Simple Logging Facade for Java,即簡單日志門面接口,和 Apache 的 commons-logging 是一樣的概念,它們都不是具體的日志框架,你可以指定其他主流的日志實現框架。
  • Logback
    Logback 是 Slf4j 的原生實現框架,同樣也是出自 Log4j 一個人之手,但擁有比 log4j 更多的優點、特性和更做強的性能,現在基本都用來代替 log4j 成為主流。

為什么 Logback 會成為主流?
無論從設計上還是實現上,Logback相對log4j而言有了相對多的改進。
更快的執行速度
基于我們先前在log4j上的工作,logback 重寫了內部的實現,在某些特定的場景上面,甚至可以比之前的速度快上10倍。在保證logback的組件更加快速的同時,同時所需的內存更加少。

日志框架總結

  1. commons-loggin、slf4j 只是一種日志抽象門面,不是具體的日志框架。
  2. log4j、logback 是具體的日志實現框架。
  3. 一般首選強烈推薦使用 slf4j + logback。當然也可以使用slf4j + log4j、commons-logging + log4j 這兩種日志組合框架。

SpringBoot logback

Spring Boot會用Logback來記錄日志,并用INFO級別輸出到控制臺。在運行應用程序和其他例子時,你應該已經看到很多INFO級別的日志了。

日志依賴

依賴 spring-boot-starter-web 默認包含spring-boot-starter-logging
那么,我們的Spring Boot應用將自動使用logback作為應用日志框架,Spring Boot啟動的時候,由org.springframework.boot.logging.Logging-Application-Listener根據情況初始化并使用。

image.png

默認配置屬性支持

Spring Boot為我們提供了很多默認的日志配置,所以,只要將spring-boot-starter-logging作為依賴加入到當前應用的classpath,則“開箱即用”。 下面介紹幾種在application.properties就可以配置的日志相關屬性。

控制臺輸出

日志級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果設置為WARN,則低于WARN的信息都不會輸出。 Spring Boot中默認配置ERROR、WARN和INFO級別的日志輸出到控制臺。您還可以通過啟動您的應用程序–debug標志來啟用“調試”模式(開發的時候推薦開啟),以下兩種方式皆可:

  • 在運行命令后加入–debug標志,如:$ java -jar springTest.jar --debug。
  • 在application.properties中配置debug=true,該屬性置為true的時候,核心Logger(包含嵌入式容器、hibernate、spring)會輸出更多內容,但是你自己應用的日志并不會輸出為DEBUG級別。

文件輸出

默認情況下,Spring Boot將日志輸出到控制臺,不會寫到日志文件。如果要編寫除控制臺輸出之外的日志文件,則需在application.properties中設置logging.file或logging.path屬性。

  • logging.file,設置文件,可以是絕對路徑,也可以是相對路徑。如:logging.file=my.log。
  • logging.path,設置目錄,會在該目錄下創建spring.log文件,并寫入日志內容,如:logging.path=/var/log。

如果只配置 logging.file,會在項目的當前路徑下生成一個 xxx.log 日志文件。
如果只配置 logging.path,在 /var/log文件夾生成一個日志文件為 spring.log。

級別控制

所有支持的日志記錄系統都可以在Spring環境中設置記錄級別(例如在application.properties中) 格式為:’logging.level.* = LEVEL’

  • logging.level:日志級別控制前綴,*為包名或Logger名
  • LEVEL:選項TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF

舉例:

  • logging.level.com.mrbird=DEBUG:com.mrbird包下所有class以DEBUG級別輸出。
  • logging.level.root=WARN:root日志以WARN級別輸出。

自定義日志配置

由于日志服務一般都在ApplicationContext創建前就初始化了,它并不是必須通過Spring的配置文件控制。因此通過系統屬性和傳統的Spring Boot外部配置文件依然可以很好的支持日志控制和管理。

根據不同的日志系統,你可以按如下規則組織配置文件名,就能被正確加載:

  • 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特有的配置項(下面會提到)。

上面是默認的命名規則,并且放在src/main/resources下面即可。

如果你即想完全掌控日志配置,但又不想用logback.xml作為Logback配置的名字,可以在application.properties配置文件里面通過logging.config屬性指定自定義的名字:

logging.config=classpath:logging-config.xml

雖然一般并不需要改變配置文件的名字,但是如果你想針對不同運行時Profile使用不同的日 志配置,這個功能會很有用。

下面我們來看看一個普通的logback-spring.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">

<!--    每個logger都關聯到logger上下文,默認上下文名稱為“default”。但可以使用設置成其他名字,用于區分不同應用程序的記錄。
    一旦設置,不能修改,可以通過%contextName來打印日志上下文名稱。-->
    <contextName>logback</contextName>

<!--    設置變量<property> 用來定義變量值的標簽,有兩個屬性,name和value;其中name的值是變量的名稱,value的值時變量定義的值。
    通過定義的值會被插入到logger上下文中。定義變量后,可以使“${}”來使用變量。-->
    <property name="log.path" value="log" />

<!--  appender用來格式化日志輸出節點,有倆個屬性name和class,class用來指定哪種輸出策略,常用就是控制臺輸出策略和文件輸出策略。-->

<!--    <encoder>表示對日志進行編碼:

    %d{HH: mm:ss.SSS}——日志輸出時間。

    %thread——輸出日志的進程名字,這在Web應用以及異步任務處理中很有用。

    %-5level——日志級別,并且使用5個字符靠左對齊。

    %logger{36}——日志輸出者的名字。

    %msg——日志消息。

    %n——平臺的換行符。

    ThresholdFilter為系統定義的攔截器,例如我們用ThresholdFilter來過濾掉ERROR級別以下的日志不輸出到文件中。如果不用記得注釋掉,不然你控制臺會發現沒日志~-->
    <!--輸出到控制臺-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
             <level>ERROR</level>
         </filter>-->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

<!--    <fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>定義了日志的切分方式——把每一天的日志歸檔到一個文件中;

    <maxHistory>30</maxHistory>表示只保留最近30天的日志,以防止日志填滿整個磁盤空間。同理,可以使用%d{yyyy-MM-dd_HH-mm}來定義精確到分的日志切分方式;

    <totalSizeCap>1GB</totalSizeCap>用來指定日志文件的上限大小,例如設置為1GB的話,那么到了這個值,就會刪除舊的日志。-->
    <!--輸出到文件-->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

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

<!--    <logger>用來設置某一個包或者具體的某一個類的日志打印級別、以及指定<appender>。<logger>僅有一個name屬性,一個可選的level和一個可選的addtivity屬性。-->
    <!-- logback為java中的包 -->
    <logger name="top.lconcise.controller.HelloController"/>

<!--    name:用來指定受此logger約束的某一個包或者具體的某一個類。

    level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,還有一個特俗值INHERITED或者同義詞NULL,
    代表強制執行上級的級別。如果未設置此屬性,那么當前logger將會繼承上級的級別。

    addtivity:是否向上級logger傳遞打印信息。默認是true。-->
    <!--logback.LogbackDemo:類的全路徑 -->
    <logger name="top.lconcise.controller.HelloController2" level="WARN" additivity="false">
        <appender-ref ref="console"/>
    </logger>
</configuration>

阿里日志規約

1.【強制】應用中不可直接使用日志系統(Log4j、 Logback) 中的 API,而應依賴使用日志框架
SLF4J 中的 API,使用門面模式的日志框架,有利于維護和各個類的日志處理方式統一。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(A.class);

2.【強制】日志文件推薦至少保存 15 天,因為有些異常具備以“周”為頻次發生的特點。

3.【強制】應用中的擴展日志(如打點、臨時監控、訪問日志等) 命名方式:
appName_logType_logName.log。 logType:日志類型,推薦分類有
stats/desc/monitor/visit 等; logName:日志描述。這種命名的好處:通過文件名就可知
道日志文件屬于什么應用,什么類型,什么目的,也有利于歸類查找。
正例: mppserver 應用中單獨監控時區轉換異常,如:
mppserver_monitor_timeZoneConvert.log
說明: 推薦對日志進行分類, 如將錯誤日志和業務日志分開存放,便于開發人員查看,也便于
通過日志對系統進行及時監控。

4.【強制】對 trace/debug/info 級別的日志輸出,必須使用條件輸出形式或者使用占位符的方式。
說明:

 logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);

如果日志級別是 warn,上述日志不會打印,但是會執行字符串拼接操作,如果 symbol 是對象,會執行 toString()方法,浪費了系統資源,執行了上述操作,最終日志卻沒有打印。
正例: (條件)

if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}

正例: (占位符)

logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);

5.【強制】避免重復打印日志,浪費磁盤空間,務必在 log4j.xml 中設置 additivity=false。
正例: <logger name="com.taobao.dubbo.config" additivity="false">
6.【強制】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那么通過
關鍵字 throws 往上拋出。
正例: logger.error(各類參數或者對象 toString + "_" + e.getMessage(), e);

7.【推薦】謹慎地記錄日志。生產環境禁止輸出 debug 日志; 有選擇地輸出 info 日志; 如果使
用 warn 來記錄剛上線時的業務行為信息,一定要注意日志輸出量的問題,避免把服務器磁盤
撐爆,并記得及時刪除這些觀察日志。
說明: 大量地輸出無效日志,不利于系統性能提升,也不利于快速定位錯誤點。 記錄日志時請
思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?

8.【參考】可以使用 warn 日志級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適
從。注意日志輸出的級別, error 級別只記錄系統邏輯出錯、異常等重要的錯誤信息。如非必
要,請不要在此場景打出 error 級別。

測試源碼:https://github.com/lbshold/springboot/tree/master/Spring-Boot-Log
參考文章:https://mrbird.cc/Spring-Boot-logback.html

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