java日志體系

為什么要使用日志

剛開始接觸java時(shí)都使用過System.out來調(diào)試,通過它我們能打印出一些關(guān)注的信息到控制臺(tái)便于我們調(diào)試。這種方式只限于我們平常開發(fā)時(shí)簡單測試,但是生產(chǎn)環(huán)境這樣是不行的。首先這樣做會(huì)有性能上的問題,其次使用這種方式會(huì)很麻煩。例如我打印的結(jié)果需要顯示方法名稱、線程名稱、時(shí)間等等,你總不能每次都手動(dòng)拼接然后打印吧;還例如我需要將它輸入到不同的文件中。以上說的都是System.out所不能實(shí)現(xiàn)或者特別麻煩的地方。

混亂的日志體系

上面我們總結(jié)了System.out不足之處,所以我們正式項(xiàng)目一般都是使用的日志。但是java中的日志體系特別亂,特別對于新手來說簡直就是一臉悶逼。你肯定聽過Log4j、JUL、JCL、Slf4j、Logback、Log4j2這些里面的幾種,面對這么多日志相關(guān)的東西,你除此接觸簡直就是一臉悶逼。下面我們來簡單說說這些日志工具的關(guān)系和歷史。

Log4j:Java之前官方并沒有提供日志相關(guān)的工具,而Log4j就是最早提供日志功能的工具了,然后大家都開始使用這個(gè)工具,而Log4j也幾乎成為Java標(biāo)準(zhǔn)的日志庫了。

JUC:log4j很流行但是并沒有成為java標(biāo)準(zhǔn)的日志庫,Sun公司推出了JUL(java Util Logging)。因?yàn)樵谥按蠹乙呀?jīng)習(xí)慣使用Log4j了,所以JUL并沒有流行起來。

JCL:上面說了已經(jīng)存在Log4j和JUL,如果想從Log4j換成JUC或者從JUC換成Log4j很麻煩,需要修改所有日志調(diào)用的地方,所以Apache為了解決問題推出了JCL(Jakarta Commons Loggin)。JCL中只是定義了一套日志接口,支持在運(yùn)行時(shí)動(dòng)態(tài)的加載日志組件的實(shí)現(xiàn)。也就是說我們在代碼里使用JCL的API,底層我們可以使用Log4j或者JUC實(shí)現(xiàn),這樣我們在切換日志實(shí)現(xiàn)時(shí),不需要修改大量的代碼。

Slf4j和Logback:而之前開發(fā)Log4j的作者離開原來的公司之后,覺得之前的日志框架還不夠牛逼,于是他再次出手,掏出Slfj4和Logback(Sl4j的實(shí)現(xiàn))兩個(gè)項(xiàng)目。這下java日志領(lǐng)域基本上就分成兩個(gè)幫派了,Commons Loggin和Slf4j。而隨著時(shí)間推移,Slf4j搭配Logback慢慢的搶占了Log4j的用戶。

Log4j 2:上面說了Slf4j搭配Logback慢慢的搶占了Log4j的用戶,這個(gè)時(shí)候Log4j推出了Log4j 2.x與Logback對戰(zhàn)。

看了上面的java日志工具的發(fā)展,現(xiàn)在大概明白了日志工具的體系了吧。對于上面說的工具基本上可以分為兩類,一類是日志接口,也就是日志門面,它們不提供實(shí)現(xiàn)或者提供簡單的實(shí)現(xiàn)。而另外一類是日志實(shí)現(xiàn),也就是實(shí)際上實(shí)現(xiàn)日志功能的工具。

對于日志工具包門面工具主要就是Slf4j和JCL這個(gè)兩個(gè),而實(shí)際上因?yàn)樾蕟栴},目前日志門面大家都使用的是Slf4j,而JCL逐漸的退出了舞臺(tái)。

對于日志的實(shí)現(xiàn)現(xiàn)在基本上的選擇就是Log4j、Logback、Log4j 2。

Slf4j和Logback組合使用

這個(gè)組合應(yīng)該是目前使用最廣的,至少我個(gè)人使用比較多,而且在Spring Boot中也默認(rèn)使用的該組合,下面就說如何使用Slf4j和Logback組合使用。

jar包說明

logback-core:Logback核心功能包,如果只是想用Logback使用這個(gè)包就可以了。

logback-access:訪問模塊集成和Servlet容器集成,提供Http訪問日志的功能。

logback-classic:如果想與Slf4j集成就需要使用到這個(gè)包,這個(gè)包里面還依賴了Slf4j的包。

一般情況下我們使用Slf4j與Logback只需要在工程中添加下面的依賴即可,不需要我們手動(dòng)添加Slf4j包的依賴。

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

Logback配置說明

如果不需要定制化的日志只需要添加相關(guān)的maven依賴就可以了,如果需要定制化需求就需要自己添加配置。Logback支持通過XML和groovy的方式來配置,但是使用較多的方式還是通過XML這種方式配置。配置時(shí)我們只需要在創(chuàng)建Logback.xml文件放在resource下即可。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="true">
    <!--設(shè)置變量-->
    <property name="APP_NAME" value="logback-demo"/>
    <!--上下文名稱,設(shè)置之后不能再修改-->
    <contextName>${APP_NAME}</contextName>
    <!--配置appender-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
        </encoder>
    </appender>
    <!--配置logger-->
    <logger name="com.buydeem.share.log.logback" level="debug" additivity="true"/>
    <!--配置root-->
    <root level="info">
        <!--引用appender-->
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

configuration

上面就是一個(gè)最簡單的logback配置文件,首先就是configuration節(jié)點(diǎn)的配置,其中scanscanPeriod是用來監(jiān)控配置文件變化的,scan設(shè)置為true時(shí)代表會(huì)掃描配置文件的變化,而scanPeriod是用來指定掃描頻率。而debug是用來打印Logback內(nèi)部的日志,它會(huì)打印如何查找配置文件和實(shí)際應(yīng)用的日志文件等信息。

contextName

設(shè)置應(yīng)用的名稱,該值設(shè)置之后是無法再次修改的。簡單的說就是應(yīng)用啟動(dòng)之后,再次修改該值是不生效的。

property

用來定義變量,在后續(xù)的配置中可是使用該變量。

appender

日志輸出組件,主要用來輸入和格式化日志,而且Logback中提供了多種appender組件,不同的組件有不同的效果。例如可以將日志輸出在控制臺(tái)中,還可以將日志輸出到文件中。例如我們上文中使用的ch.qos.logback.core.ConsoleAppender它就是將日志輸出到控制臺(tái)。

對于appender節(jié)點(diǎn)來說我們還可以配置其他東西,例如我們可以篩選日志的級別,讓該appender中只輸出某種級別的日志。利用這個(gè)設(shè)置,我們可以將不同級別的日志輸出到不同的文件中。

logger和root

logger用來設(shè)置某一個(gè)包或者具體某一個(gè)類的日志打印級別以及指定appender。你可以理解logger和root是同一種東西,不同的在于root是logger的最上級。對于logger節(jié)點(diǎn)中,我們可以設(shè)置的屬性有l(wèi)evel和additivity兩個(gè)屬性。

level代表該appender中輸出的最低日志級別,例如上面的配置文件中我們將其設(shè)置成debug代表低于該級別的日志將不會(huì)再logger中打印出來。如果我們沒有設(shè)置將應(yīng)用父級的level,如果logger沒有父級則會(huì)使用root中的level(root是所有l(wèi)evel最上級)。

additivity用來設(shè)置該logger是否向上級傳遞,如果設(shè)置為true,該logger下打印的日志還會(huì)傳遞到root中。在我們的配置中,我們并沒有在logger下配置appender,但是仍然可以打印出日志。這是因?yàn)槲覀冊趓oot節(jié)點(diǎn)下配置了appender,并且我們還把logger的additivity設(shè)置成了true。如果我們將該值改為false,你可以發(fā)現(xiàn)日志將不會(huì)再控制臺(tái)中輸出了。

配置不同級別的日志內(nèi)容輸出到不同的文件

對于實(shí)際開發(fā)中我們可能需要將不同的日志輸出到不同的文件,下面是一個(gè)示例配置。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
    <!--日志文件前綴,即應(yīng)用名稱 -->
    <property name="logfile.prefix" value="logback-demo"/>
    <!--日志路徑,可寫相對路徑,也可寫絕對路徑 -->
    <property name="log.path" value="logs"/>
    <!-- 日志輸出格式 -->
    <property name="log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5level [%15thread] %40.40logger{40} [%10method,%line] : %msg%n"/>
    <!-- 控制臺(tái)輸出日志 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--        設(shè)置日志輸出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 編碼 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 文件輸出日志, 滾動(dòng)(時(shí)間/文件大?。┹敵霾呗?-->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 過濾器,只記錄debug級別的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 日志文件路徑及文件名 -->
        <File>${log.path}/${logfile.prefix}-debug.log</File>
        <!-- 日志記錄器的滾動(dòng)策略,按日期記錄 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志輸出格式 -->
            <FileNamePattern>${log.path}/${logfile.prefix}-debug.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!-- 日志保留天數(shù) -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        設(shè)置日志輸出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 編碼 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    info級別-->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <File>${log.path}/${logfile.prefix}-info.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/${logfile.prefix}-info.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        設(shè)置日志輸出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 編碼 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    warn級別-->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <File>${log.path}/${logfile.prefix}-warn.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/${logfile.prefix}-warn.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        設(shè)置日志輸出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 編碼 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    error級別-->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <File>${log.path}/${logfile.prefix}-error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.path}/${logfile.prefix}-error.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!--        設(shè)置日志輸出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <!-- 編碼 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <logger name="com.buydeem.share.log.logback" level="debug" additivity="true"/>
    <!-- 日志輸出 -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="DEBUG_FILE"/>
        <appender-ref ref="INFO_FILE"/>
        <appender-ref ref="WARN_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
</configuration>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容