logback介紹和配置詳解

logback是java的日志開(kāi)源組件,是log4j創(chuàng)始人寫(xiě)的,性能比log4j要好际插,目前主要分為3個(gè)模塊

  1. logback-core:核心代碼模塊
  2. logback-classic:log4j的一個(gè)改良版本哥艇,同時(shí)實(shí)現(xiàn)了slf4j的接口,這樣你如果之后要切換其他日志組件也是一件很容易的事
  3. logback-access:訪問(wèn)模塊與Servlet容器集成提供通過(guò)Http來(lái)訪問(wèn)日志的功能

本篇博客會(huì)講解logback的使用胁黑、配置詳解废封、以及l(fā)ogback簡(jiǎn)單的一個(gè)原理。

一丧蘸、logback的使用

引入maven依賴

<!--這個(gè)依賴直接包含了 logback-core 以及 slf4j-api的依賴-->
<dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-classic</artifactId>
     <version>1.2.3</version>
</dependency>

然后就可以直接在代碼中使用slf4j的接口獲取Logger輸出日志了漂洋。(配置在下面的章節(jié)介紹)

//這是slf4j的接口,由于我們引入了logback-classic依賴力喷,所以底層實(shí)現(xiàn)是logback
private static final Logger LOGGER = LoggerFactory.getLogger(Test.class);

public static void main(String[] args) throws InterruptedException {
    LOGGER.info("hello world");
}

二刽漂、logback的配置

配置獲取順序

logback在啟動(dòng)的時(shí)候,會(huì)按照下面的順序加載配置文件

  1. 如果java程序啟動(dòng)時(shí)指定了logback.configurationFile屬性弟孟,就用該屬性指定的配置文件贝咙。如java -Dlogback.configurationFile=/path/to/mylogback.xml Test ,這樣執(zhí)行Test類的時(shí)候就會(huì)加載/path/to/mylogback.xml配置
  2. 在classpath中查找 logback.groovy 文件
  3. 在classpath中查找 logback-test.xml 文件
  4. 在classpath中查找 logback.xml 文件
  5. 如果是 jdk6+,那么會(huì)調(diào)用ServiceLoader 查找 com.qos.logback.classic.spi.Configurator接口的第一個(gè)實(shí)現(xiàn)類
  6. 自動(dòng)使用ch.qos.logback.classic.BasicConfigurator,在控制臺(tái)輸出日志

上面的順序表示優(yōu)先級(jí)拂募,使用java -D配置的優(yōu)先級(jí)最高庭猩,只要獲取到配置后就不會(huì)再執(zhí)行下面的流程。相關(guān)代碼可以看ContextInitializer#autoConfig()方法陈症。

關(guān)于SLF4j的日志輸出級(jí)別

在slf4j中蔼水,從小到大的日志級(jí)別依舊是trace、debug录肯、info趴腋、warn、error

logback.xml 配置樣例

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">

    <contextName>logback</contextName>
    <!--定義參數(shù),后面可以通過(guò)${app.name}使用-->
    <property name="app.name" value="logback_test"/>
    <!--ConsoleAppender 用于在屏幕上輸出日志-->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--定義了一個(gè)過(guò)濾器,在LEVEL之下的日志輸出不會(huì)被打印出來(lái)-->
        <!--這里定義了DEBUG优炬,也就是控制臺(tái)不會(huì)輸出比ERROR級(jí)別小的日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
        <!-- encoder 默認(rèn)配置為PatternLayoutEncoder -->
        <!--定義控制臺(tái)輸出格式-->
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--定義日志輸出的路徑-->
        <!--這里的scheduler.manager.server.home 沒(méi)有在上面的配置中設(shè)定疏叨,所以會(huì)使用java啟動(dòng)時(shí)配置的值-->
        <!--比如通過(guò) java -Dscheduler.manager.server.home=/path/to XXXX 配置該屬性-->
        <file>${scheduler.manager.server.home}/logs/${app.name}.log</file>
        <!--定義日志滾動(dòng)的策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--定義文件滾動(dòng)時(shí)的文件名的格式-->
            <fileNamePattern>${scheduler.manager.server.home}/logs/${app.name}.%d{yyyy-MM-dd.HH}.log.gz
            </fileNamePattern>
            <!--60天的時(shí)間周期,日志量最大20GB-->
            <maxHistory>60</maxHistory>
            <!-- 該屬性在 1.1.6版本后 才開(kāi)始支持-->
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!--每個(gè)日志文件最大100MB-->
            <maxFileSize>100MB</maxFileSize>
        </triggeringPolicy>
        <!--定義輸出格式-->
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>

    <!--root是默認(rèn)的logger 這里設(shè)定輸出級(jí)別是debug-->
    <root level="trace">
        <!--定義了兩個(gè)appender穿剖,日志會(huì)通過(guò)往這兩個(gè)appender里面寫(xiě)-->
        <appender-ref ref="stdout"/>
        <appender-ref ref="file"/>
    </root>

    <!--對(duì)于類路徑以 com.example.logback 開(kāi)頭的Logger,輸出級(jí)別設(shè)置為warn,并且只輸出到控制臺(tái)-->
    <!--這個(gè)logger沒(méi)有指定appender蚤蔓,它會(huì)繼承root節(jié)點(diǎn)中定義的那些appender-->
    <logger name="com.example.logback" level="warn"/>

    <!--通過(guò) LoggerFactory.getLogger("mytest") 可以獲取到這個(gè)logger-->
    <!--由于這個(gè)logger自動(dòng)繼承了root的appender,root中已經(jīng)有stdout的appender了糊余,自己這邊又引入了stdout的appender-->
    <!--如果沒(méi)有設(shè)置 additivity="false" ,就會(huì)導(dǎo)致一條日志在控制臺(tái)輸出兩次的情況-->
    <!--additivity表示要不要使用rootLogger配置的appender進(jìn)行輸出-->
    <logger name="mytest" level="info" additivity="false">
        <appender-ref ref="stdout"/>
    </logger>
    
    <!--由于設(shè)置了 additivity="false" 秀又,所以輸出時(shí)不會(huì)使用rootLogger的appender-->
    <!--但是這個(gè)logger本身又沒(méi)有配置appender,所以使用這個(gè)logger輸出日志的話就不會(huì)輸出到任何地方-->
    <logger name="mytest2" level="info" additivity="false"/>
</configuration>

配置詳解

configuration節(jié)點(diǎn)相關(guān)屬性

屬性名稱 默認(rèn)值 介紹
debug false 要不要打印 logback內(nèi)部日志信息贬芥,true則表示要打印吐辙。建議開(kāi)啟
scan true 配置發(fā)送改變時(shí),要不要重新加載
scanPeriod 1 seconds 檢測(cè)配置發(fā)生變化的時(shí)間間隔蘸劈。如果沒(méi)給出時(shí)間單位昏苏,默認(rèn)時(shí)間單位是毫秒

configuration子節(jié)點(diǎn)介紹

1. contextName節(jié)點(diǎn)

設(shè)置日志上下文名稱,后面輸出格式中可以通過(guò)定義 %contextName 來(lái)打印日志上下文名稱

2.property節(jié)點(diǎn)

用來(lái)設(shè)置相關(guān)變量,通過(guò)key-value的方式配置威沫,然后在后面的配置文件中通過(guò) ${key}來(lái)訪問(wèn)

3.appender 節(jié)點(diǎn)

日志輸出組件贤惯,主要負(fù)責(zé)日志的輸出以及格式化日志。常用的屬性有name和class

屬性名稱 默認(rèn)值 介紹
name 無(wú)默認(rèn)值 appender組件的名稱棒掠,后面給logger指定appender使用
class 無(wú)默認(rèn)值 appender的具體實(shí)現(xiàn)類孵构。常用的有 ConsoleAppender、FileAppender烟很、RollingFileAppender

ConsoleAppender:向控制臺(tái)輸出日志內(nèi)容的組件颈墅,只要定義好encoder節(jié)點(diǎn)就可以使用。

FileAppender:向文件輸出日志內(nèi)容的組件雾袱,用法也很簡(jiǎn)單恤筛,不過(guò)由于沒(méi)有日志滾動(dòng)策略,一般很少使用

RollingFileAppender:向文件輸出日志內(nèi)容的組件芹橡,同時(shí)可以配置日志文件滾動(dòng)策略毒坛,在日志達(dá)到一定條件后生成一個(gè)新的日志文件。

appender節(jié)點(diǎn)中有一個(gè)子節(jié)點(diǎn)filter僻族,配置具體的過(guò)濾器粘驰,比如上面的例子配置了一個(gè)內(nèi)置的過(guò)濾器ThresholdFilter屡谐,然后設(shè)置了level的值為DEBUG述么。這樣用這個(gè)appender輸出日志的時(shí)候都會(huì)經(jīng)過(guò)這個(gè)過(guò)濾器,日志級(jí)別低于DEBUG的都不會(huì)輸出來(lái)愕掏。

在RollingFileAppender中度秘,可以配置相關(guān)的滾動(dòng)策略,具體可以看配置樣例的注釋。

4.logger以及root節(jié)點(diǎn)

root節(jié)點(diǎn)和logger節(jié)點(diǎn)其實(shí)都是表示Logger組件剑梳。個(gè)人覺(jué)的可以把他們之間的關(guān)系可以理解為父子關(guān)系唆貌,root是最頂層的logger,正常情況getLogger("name/class")沒(méi)有找到對(duì)應(yīng)logger的情況下垢乙,都是使用root節(jié)點(diǎn)配置的logger锨咙。

如果配置了logger,并且通過(guò)getLogger("name/class")獲取到這個(gè)logger追逮,輸出日志的時(shí)候酪刀,就會(huì)使用這個(gè)logger配置的appender輸出,同時(shí)還會(huì)使用rootLogger配置的appender钮孵。我們可以使用logger節(jié)點(diǎn)的additivity="false"屬性來(lái)屏蔽rootLogger的appender骂倘。這樣就可以不使用rootLogger的appender輸出日志了。

關(guān)于logger的獲取巴席,一般logger是配置name的历涝。我們?cè)俅a中經(jīng)常通過(guò)指定的CLass來(lái)獲取Logger,比如這樣LoggerFactory.getLogger(Test.class);,其實(shí)這個(gè)最后也是轉(zhuǎn)成對(duì)應(yīng)的包名+類名的字符串com.kongtrio.Test.class漾唉。假設(shè)有一個(gè)logger配置的那么是com.kongtrio荧库,那么通過(guò)LoggerFactory.getLogger(Test.class)獲取到的logger就是這個(gè)logger。

也就是說(shuō)赵刑,name可以配置包名电爹,也可以配置自定義名稱。

上面說(shuō)的logger和root節(jié)點(diǎn)的父子關(guān)系只是為了方便理解料睛,具體的底層實(shí)現(xiàn)本人并沒(méi)有看丐箩,他們之間真正的關(guān)系讀者有興趣的話可以去看logback的源碼

一些特性的支持

在看logback的啟動(dòng)日志時(shí),看到下面這句話恤煞。

no applicable action for [totalSizeCap], current ElementPath is [[configuration][appender][rollingPolicy][totalSizeCap]]

no applicable action for [maxFileSize], current ElementPath is [[configuration][appender][rollingPolicy][maxFileSize]]

大概意思解析logbck配置時(shí)不支持totalSizeCap屎勘、maxFileSize的配置。后來(lái)查了下居扒,果然概漱,totalSizeCap是在1.1.6之后的版本才開(kāi)始支持的,切換到1.1.7之后就不會(huì)出現(xiàn)這句話了喜喂。

maxFileSize比較奇怪瓤摧,試了目前所有的版本都不支持rollingPolicy—maxFileSize的配置方案,如果配置到triggeringPolicy節(jié)點(diǎn)下玉吁,又是可以生效的照弥。但是官網(wǎng)給的文檔上又有出現(xiàn)rollingPolicy下面的。

Ps:啟動(dòng)的時(shí)候建議多看看日志进副,可以及早發(fā)現(xiàn)一些問(wèn)題这揣。比如這些配置沒(méi)生效,看到這些日志就可以馬上調(diào)整,而不會(huì)因?yàn)闆](méi)達(dá)到預(yù)期的效果而造成一些損失给赞。

三机打、實(shí)現(xiàn)原理

slf4j是什么

slf4j只是一套標(biāo)準(zhǔn),通俗來(lái)講片迅,就是定義了一系列接口残邀,它并不提供任何的具體實(shí)現(xiàn)。所以柑蛇,我們使用這套接口進(jìn)行開(kāi)發(fā)罐旗,可以任意的切換底層的實(shí)現(xiàn)框架。

比如唯蝶,一開(kāi)始項(xiàng)目用的是log4j的實(shí)現(xiàn)九秀,后來(lái)發(fā)現(xiàn)log4j的性能太差了,想換成logback粘我,由于我們代碼中都是面向slf4j接口的鼓蜒,這樣我們只要吧log4j的依賴換成logback就可以了。

logback-classic啟動(dòng)原理

我們?cè)谡{(diào)用LoggerFactory.getLogger(Test.class)時(shí)征字,這些接口或者類都是slf4j的都弹,那么,它是怎么切換到logback的實(shí)現(xiàn)的呢匙姜?

為了解決這個(gè)問(wèn)題畅厢,我追蹤了一下代碼,發(fā)現(xiàn)logback-classic底下氮昧,有一個(gè)slf4j的包.

logback-slf4j實(shí)現(xiàn).png

slf4j在初始化時(shí)會(huì)調(diào)用org.slf4j.StaticLoggerBinder進(jìn)行初始化框杜。因此,每個(gè)要實(shí)現(xiàn)slf4j的日志組件項(xiàng)目袖肥,底下都要有org.slf4j.StaticLoggerBinder的具體實(shí)現(xiàn)咪辱。這樣slf4j才會(huì)在初始化的關(guān)聯(lián)到具體的實(shí)現(xiàn)。

比如logback在自己定義的StaticLoggerBinder做了自己組件的初始化工作椎组。下面是網(wǎng)上找的一個(gè)時(shí)序圖:


slf4j加載時(shí)序圖.png

多個(gè)依賴包都實(shí)現(xiàn)了slf4j

如果引入了多個(gè)slf4j的實(shí)現(xiàn)依賴包油狂,那么各個(gè)包底下都有org.slf4j.StaticLoggerBinder的實(shí)現(xiàn),這時(shí)候slf4j會(huì)調(diào)用哪個(gè)包的StaticLoggerBinder實(shí)現(xiàn)呢寸癌?

這個(gè)問(wèn)題和java的類加載機(jī)制有關(guān)系专筷,在雙親委派機(jī)制的模型中,這些引入的依賴包通常都是由Application ClassLoader來(lái)加載的蒸苇。Application ClassLoader會(huì)加載用戶路徑(classpath)上指定的類庫(kù)磷蛹,如果多個(gè)org.slf4j.StaticLoggerBinder的jar包實(shí)現(xiàn),類加載器先掃到哪個(gè)jar包填渠,就會(huì)使用jar包提供的實(shí)現(xiàn)弦聂。

舉個(gè)例子鸟辅,我們通過(guò) java -classpath a.jar:b.jar Test運(yùn)行Test類氛什,a.jar和b.jar都定義了org.slf4j.StaticLoggerBinder的實(shí)現(xiàn)莺葫,那么執(zhí)行Test時(shí)加載StaticLoggerBinder類就會(huì)加載a.jar中的那個(gè)定義類。因?yàn)閍.jar在classpath中排在比較前面枪眉。

四捺檬、總結(jié)

日志組件的使用一般都非常簡(jiǎn)單,幾乎所有的項(xiàng)目中都會(huì)用到各種各樣的日志組件贸铜。但是可能就是由于太簡(jiǎn)單了堡纬,比較少的人會(huì)愿意深入系統(tǒng)的去了解。本人也只是對(duì)logback的配置以及一些簡(jiǎn)單的原理做了一些了解蒿秦,并沒(méi)有很深入的去看logback的具體實(shí)現(xiàn)烤镐。

因此,本文的內(nèi)容大部分都是基于官網(wǎng)的文檔以及網(wǎng)上一些其他關(guān)于logback的博客棍鳖,雖然也做了一些簡(jiǎn)單的測(cè)試炮叶,但并不保證全部都是正確的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渡处,一起剝皮案震驚了整個(gè)濱河市镜悉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌医瘫,老刑警劉巖侣肄,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異醇份,居然都是意外死亡稼锅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)僚纷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缰贝,“玉大人,你說(shuō)我怎么就攤上這事畔濒∈G纾” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵侵状,是天一觀的道長(zhǎng)赞弥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)趣兄,這世上最難降的妖魔是什么绽左? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮艇潭,結(jié)果婚禮上拼窥,老公的妹妹穿的比我還像新娘戏蔑。我一直安慰自己,他們只是感情好鲁纠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布总棵。 她就那樣靜靜地躺著,像睡著了一般改含。 火紅的嫁衣襯著肌膚如雪情龄。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天捍壤,我揣著相機(jī)與錄音骤视,去河邊找鬼。 笑死鹃觉,一個(gè)胖子當(dāng)著我的面吹牛专酗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盗扇,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼祷肯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了粱玲?” 一聲冷哼從身側(cè)響起躬柬,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抽减,沒(méi)想到半個(gè)月后允青,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卵沉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年颠锉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片史汗。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡琼掠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出停撞,到底是詐尸還是另有隱情瓷蛙,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布戈毒,位于F島的核電站艰猬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏埋市。R本人自食惡果不足惜冠桃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望道宅。 院中可真熱鬧食听,春花似錦胸蛛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肃弟,卻和暖如春玷室,著一層夾襖步出監(jiān)牢的瞬間零蓉,已是汗流浹背笤受。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敌蜂,地道東北人箩兽。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像章喉,于是被迫代替她去往敵國(guó)和親汗贫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容