slf4j的使用規范


一、結論:應用中不可直接使用日志系統(Log4j、Logback)中的API,而應依賴使用日志框架SLF4J中的API,使用門面模式的日志框架,有利於維護和各個類的日志處理方式統一。代碼中的使用格式如下:

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

private static final Logger LOGGER = LoggerFactory.getLogger(Abc.class);

LOGGER.info("訂單{}開始取消訂單,請求參數:{}", orderNo, object.toJSONString());{}可以讀取到后面追加的參數並放置里面。

日志工具對象logger應聲明為private static final:

1、聲明為private是出於安全性考慮,防止logger對象被其他類非法使用。

2、聲明為static是為了防止重復new出logger對象,同時防止logger被序列化,造成安全風險,出於資源利用的考慮,LOGGER的構造方法參數是Class,決定了LOGGER是根據類的結構來進行區分日志,所以一個類只要一個LOGGER就可以了,故static。

3、聲明為final是因為在類的生命周期內無需變更logger,只是記錄該類的信息。

 

二、SLF4J,全稱為:The Simple Logging Facade for Java,官方解釋為:The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks, such as java.util.logging, logback and log4j. SLF4J allows the end-user to plug in the desired logging framework at deployment time. Note that SLF4J-enabling your library/application implies the addition of only a single mandatory dependency, namely slf4j-api-1.7.21.jar.

slf4j是一系列的日志接口,而log4j logback是具體實現了的日志框架。SLF4J(Simple logging Facade for Java)不是一個真正的日志實現,而是一個抽象層,它允許你在后台使用任意一個日志類庫。slf4j譯為簡單日志門面,是日志框架的抽象。而log4j和logback是眾多日志框架中的幾種。

 

三、log4j和logback的區別:logback是直接實現了slf4j的接口,是不消耗內存和計算開銷的。而log4j不是對slf4j的原生實現,所以slf4j api在調用log4j時需要一個適配層(適配器模式)。Logback相對log4j而言有了相對多的改進。但是兩者的用法幾乎差別不大。下面是logback的優勢:

•更快的執行速度

•充分的測試

•logback-classic非常自然的實現了SLF4J

•豐富的擴展文檔

•可以使用使用XML配置文件或者Groovy

•自動重新載入配置文件

•優雅地從I/O錯誤中恢復

•自動清除舊的日志歸檔文件

•自動壓縮歸檔日志文件

注意:如果單獨使用slf4j,是無法在控制台輸出日志的。也就是說我們在具體開發中,需要綁定一個具體的日志框架,也就是slf4j的實現,才能正常的使用slf4j。

 

四、總結如下:

1、slf4j是java的一個日志門面,實現了日志框架一些通用的api,log4j和logback是具體的日志框架。

2、他們可以單獨的使用,也可以綁定slf4j一起使用。

單獨使用。分別調用框架自己的方法來輸出日志信息。綁定slf4j一起使用。調用slf4j的api來輸入日志信息,具體使用與底層日志框架無關(需要底層框架的配置文件)。顯然不推薦單獨使用日志框架。假設項目中已經使用了log4j,而我們此時加載了一個類庫,而這個類庫依賴另一個日志框架。這個時候我們就需要維護兩個日志框架,這是一個非常麻煩的事情。而使用了slf4j就不同了,由於應用調用的抽象層的api,與底層日志框架是無關的,因此可以任意更換日志框架。

 

五、使用slf4j綁定日志系統的優勢:

1、軟件工程的角度。抽象,解耦,便於維護。在你的開源或內部類庫中使用SLF4J會使得它獨立於任何一個特定的日志實現,這意味着不需要管理多個日志配置或者多個日志類庫。

2、語法設計角度。slf4j有{}占位符,而log4j需要用“+”來連接字符串,既不利於閱讀,同時消耗了內存(heap memory)。在SLF4J,我們不需要字符串連接而且不會導致暫時不需要的字符串消耗。取而代之的,我們在一個以占位符和以參數傳遞實際值的模板格式下寫日志信息,同時提高了代碼的可讀性。

占位符(place holder),在代碼中表示為“{}”的特性。占位符是一個非常類似於在String的format()方法中的%s,因為它會在運行時被某個提供的實際字符串所替換。這不僅降低了你代碼中字符串連接次數,而且還節省了新建的String對象。即使你可能沒需要那些對象,但這個依舊成立,取決於你的生產環境的日志級別,例如在DEBUG或者INFO級別的字符串連接。因為String對象是不可修改的並且它們建立在一個String池中,它們消耗堆內存( heap memory)而且大多數時間他們是不被需要的,例如當你的應用程序在生產環境以ERROR級別運行時候,一個String使用在DEBUG語句就是不被需要的。通過使用SLF4J,你可以在運行時延遲字符串的建立,這意味着只有需要的String對象才被建立。而如果你已經使用log4j,那么你已經對於在if條件中使用debug語句這種變通方案十分熟悉了,但SLF4J的占位符就比這個好用得多。

對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);

3、通過使用SLF4J的日志方法,你可以延遲構建日志信息(Srting)的開銷,直到你真正需要,這對於內存和CPU都是高效的。

4、作為附注,更少的暫時的字符串意味着垃圾回收器(Garbage Collector)需要做更好的工作,這意味着你的應用程序有為更好的吞吐量和性能。

六、slf4j綁定logback

Springboot默認的日志框架為logback,pom添加依賴:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.7</version>
</dependency>

配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:當此屬性設置為true時,配置文件如果發生改變,將會被重新加載,默認值為true。 scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒當scan為true時,此屬性生效。默認的時間間隔為1分鍾。 debug:當此屬性設置為true時,將打印出logback內部日志信息,實時查看logback運行狀態。默認值為false。 -->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    
    <!-- 彩色日志 -->
    <!-- 彩色日志依賴的渲染類 -->
    <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([%thread]){faint} %clr(%-40.40logger{39}){cyan} %clr([%L]) %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
    <!-- Console 輸出設置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 定義日志級別 -->
    <property name="log.level" value="INFO" />

    <!-- 定義日志文件名稱,注意修改此處!!! -->
    <property name="log.name" value="shop" />

    <!-- 定義日志文件的存儲地址 -->
    <!-- <property name="log.path" value="/app/wo_sale/servers/logs/${log.name}" /> -->
    <property name="log.path" value="logs/${log.name}" />

    <!-- 定義日志文件名格式化 -->
    <property name="log.timeFormat" value="yyyy-MM-dd" />
    
    <!-- 定義日志文件的輸出格式。%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度,%logger{50} 表示logger名字最長50個字符,否則按照句點分割。%msg:日志消息,%n是換行符 -->
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />

    <!-- 定義日志文件保留天數 -->
    <property name="log.maxHistory" value="30" />

    <!-- 定義日志文件最大限制 -->
    <property name="log.maxFileSize" value="10MB" />

    <!-- 控制台輸出 -->
    <!-- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender> -->

    <!-- 滾動記錄文件,先將日志記錄到指定文件,當符合某個條件時,將日志記錄到其他文件,並按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/${log.name}.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${log.level}</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--    滾動時產生的文件的存放位置及文件名稱 %d{yyyy-MM-dd}:按天進行日志滾動 %i:當文件大小超過maxFileSize時,按照i進行文件滾動 -->
            <FileNamePattern>${log.path}/${log.name}.%d{${log.timeFormat}}-%i.log</FileNamePattern>
            <MaxHistory>${log.maxHistory}</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>${log.maxFileSize}</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件。僅記錄錯誤日志 -->
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/error/${log.name}_error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/error/${log.name}_error.%d{${log.timeFormat}}-%i.log</FileNamePattern>
            <MaxHistory>${log.maxHistory}</MaxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>${log.maxFileSize}</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!-- 日志輸出級別,root與logger是父子關系,沒有特別定義則默認為root,任何一個類只會和一個logger對應, 要么是定義的logger,要么是root,判斷的關鍵在於找到這個logger,然后判斷這個logger的appender和level。 -->
    <root level="${log.level}">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="FILE-ERROR" />
    </root>
</configuration>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM