logback源碼解讀筆記(springboot)

最近在學(xué)習(xí)使用同事開發(fā)的日志框架,感覺對于logback除了基本的使用并沒有很深刻的了解,遂對debug過程做大概記錄以備忘,debug所用demo如下:https://gitee.com/eshin/logbackDemo

一、Logfactory初始化

LogFactory在初次getLogger的時候完成初始化,之后的每次調(diào)用都是從緩存中讀取
eg:private static final Log logger = LogFactory.getLog(SpringApplication.class); springboot 在main方法沒有g(shù)etlogger的情況下在org.springframework.boot.SpringApplication初次調(diào)用org.apache.commons.logging.LogFactory.getLog(Class):默認初始化的Factory為org.apache.commons.logging.impl.SLF4JLogFactory.SLF4JLogFactory()-------------》org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(String)

image.png
------------------》org.slf4j.LoggerFactory.getLogger(String)(可直接調(diào)用此處獲取logger)---------------------》org.slf4j.LoggerFactory.getILoggerFactory()
image.png
--------------》org.slf4j.LoggerFactory.performInitialization()------》org.slf4j.LoggerFactory.bind()
image.png

完成StaticLoggerBinder的初始化后,將標志位INITIALLZATION_STATE置為3,在下次調(diào)用是直接獲取單例不用再進行初始化操作,INITIALLZATION_STATE為volatile修飾的靜態(tài)變量,保證了多線程環(huán)境的可見性,但無法保證binder的單例只有一次加載,binder的單例由StaticLoggerBinder設(shè)計完成。

StaticLoggerBinder的初始化
image.png

1、LoggerContext defaultLoggerContext = new LoggerContext();
image.png

2、org.slf4j.impl.StaticLoggerBinder.init()

  • 2.1 new ContextInitializer(defaultLoggerContext).autoConfig();
    image.png
    ch.qos.logback.classic.util.ContextInitializer.findURLOfDefaultConfigurationFile(boolean)在類路徑下查找logback.groovy ,logback.xml ,logback-test.xml查找這三個文件,若能找到這三個文件,則通過這三個配置文件生成Configurator,否則生成默認的ch.qos.logback.classic.BasicConfigurator。
  • 2.2 contextSelectorBinder.init(defaultLoggerContext, KEY);
    image.png

    至此,LogFactory初始化完成、org.slf4j.LoggerFactory.getLogger(String)便可獲取logger。


二、springboot與logback整合

spring-boot.x.x.x.jar的spring.factories文件中

image.png
org.springframework.boot.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent)------------》org.springframework.boot.logging.LoggingApplicationListener.initialize(ConfigurableEnvironment, ClassLoader)在監(jiān)聽到ApplicationEnvironmentPreparedEvent事件后進入initialize方法。-------------》org.springframework.boot.logging.LoggingApplicationListener.initializeSystem(ConfigurableEnvironment, LoggingSystem, LogFile)
image.png
----------------------》org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LoggingInitializationContext, String, LogFile) ** 此時configLocation為空 **
image.png
---------------------》由于configLocation為空 -----------》org.springframework.boot.logging.AbstractLoggingSystem.initialize(LoggingInitializationContext, String, LogFile)------------》org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(LoggingInitializationContext, LogFile)-----------》
image.png

  • 1、加載配置文件 -----------》org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LoggingInitializationContext, String, LogFile)-----------》
    image.png
  • 1.1、根據(jù)logback配置文件加載context(logback-spring.xml)

    org.springframework.boot.logging.logback.LogbackLoggingSystem.configureByResourceUrl(LoggingInitializationContext, LoggerContext, URL)-------------->ch.qos.logback.core.joran.GenericConfigurator.doConfigure(URL)-------------->ch.qos.logback.core.joran.GenericConfigurator.doConfigure(InputStream, String)--------->ch.qos.logback.core.joran.GenericConfigurator.doConfigure(InputSource)這個過程將配置文件解析成recorder.recordEvents(inputSource) recordEvents是xml的每個標簽,分startEvent、bodyEvent和endEvent對應(yīng)標簽頭,內(nèi)容標簽,和標簽尾
    image.png
    image.png
  • 1.1.1buildInterpreter();該方法主要完成設(shè)置RuleStore
    ch.qos.logback.core.joran.GenericConfigurator.buildInterpreter()-------------->
    主要的RuleStore在如下兩個類中設(shè)置分別設(shè)置不同包下的action
    ch.qos.logback.classic.joran.JoranConfigurator(ch.qos.logback.classic.joran.action
    ch.qos.logback.core.joran.JoranConfiguratorBase<E>(ch.qos.logback.core.joran.action
    設(shè)置的rule供解析event對應(yīng)的標簽action使用
    image.png
    image.png
  • 1.1.2----------------->ch.qos.logback.core.joran.spi.EventPlayer.play(List<SaxEvent>)
    image.png

    ---------------->ch.qos.logback.core.joran.spi.nterpreter.startElement(StartEvent)----------------->ch.qos.logback.core.joran.spi.Interpreter.startElement(String, String, String, Attributes)--------------------->
    image.png
    image.png

    主要的幾個action的功能:
  • ch.qos.logback.classic.joran.action.ConfigurationAction
    image.png
    當debug=true(默認)時創(chuàng)建OnConsoleStatusListener實例并啟動,用于控制臺打印status(logcontext中的BasicStatusManager中保存的ch.qos.logback.core.status.Status對象)啟動之后,當有調(diào)用ch.qos.logback.core.BasicStatusManager.add(Status)時,控制臺便打印當前add的status。-------------》ch.qos.logback.core.BasicStatusManager.fireStatusAddEvent(Status)此處遍歷Listener,之中包括該Listener。
  • ch.qos.logback.core.joran.action.IncludeAction

    image.png
    將logback-spring.xml中include標簽指定的xml配置文件加載并加入WatchList,然后與logback-spring.xml文件一樣處理成recordEvents,并加入到加載logback-spring.xml時生成的record(recorder.saxEventList)中。

  • ch.qos.logback.core.joran.action.ConversionRuleAction

    image.png
    image.png
    用于初始化converter
    conversionWord的值就是配置pattern時需要匹配成對應(yīng)值的key,如下
    image.png
    image.png
    當converter匹配到對應(yīng)的key便轉(zhuǎn)換成需要打印的日志內(nèi)容,因此,可自定義converter及pattern關(guān)鍵字實現(xiàn)更靈活的日志打印

  • ch.qos.logback.core.joran.action.PropertyAction

    image.png
    用于存儲properties,默認scope為local,存在InterpretationContext中,當scope為context時,存在logContext中,為system時存為系統(tǒng)變量

  • ch.qos.logback.core.joran.action.AppenderAction<E>

    image.png
    image.png
    完成appender的初始化,ch.qos.logback.core.rolling.RollingFileAppender<E>,該appender設(shè)置rollingPolicy,調(diào)用start()方法時會檢測rollingPolicy是否生效以及需要的日志文件是否存在,若不存在便根據(jù)在rollingpolicy設(shè)置的pattern新建一個。
    在打印日志調(diào)用ch.qos.logback.core.rolling.RollingFileAppender.subAppend(E)時,會先調(diào)用判斷是否需要分割文件(打印日志步驟)
    image.png
    當有子標簽時需要其他的action完成配置和引用,如:ch.qos.logback.core.joran.action.NestedComplexPropertyIA完成對rollingPolicy,encoder,等的加載,當仍有子標簽時,按同樣的方式遍歷event加載。bodyevent直接設(shè)置值。
    NestedComplexPropertyIA加載時,當有配置class則根據(jù)class加載實例(rollingPolicy),若沒有配置class,則按標簽名為規(guī)則去查找(encoder)在之前的步驟中,
    image.png
    配置了默認的encoder,layout,和evalator,因此也可以根據(jù)需要自定義需要的encoder等,并通過配置class的方式配置,
    通過NestedComplexPropertyIA加載的startEvent,其對應(yīng)的endevent將進入ch.qos.logback.core.joran.action.NestedComplexPropertyIA.end(InterpretationContext, String)
    rollingPolicy,調(diào)用ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy<E>.start()方法完成rollingPolicy的加載。
    完成對文件名pattern的設(shè)置解析。
    encoder,主要是調(diào)用ch.qos.logback.classic.encoder.PatternLayoutEncoder.start()方法完成encoder的加載。
    image.png
    image.png

  • 1、ch.qos.logback.core.pattern.PatternLayoutBase.getEffectiveConverterMap()
    image.png

    p.compile();方法會將與patter的關(guān)鍵字匹配的converter保留,其余的過濾掉

  • 2、start方法主要完成converter的一些初始化工作,設(shè)置context,key,以便調(diào)用convert方法的時候進行內(nèi)容轉(zhuǎn)換。
    image.png

    當appender的類型是AsyncAppender時,在ch.qos.logback.core.joran.action.AppenderAction.end(InterpretationContext, String)時,ch.qos.logback.core.AsyncAppenderBase.start()會啟動一個異步worker,對日志event進行異步打印處理。
    image.png
  • ch.qos.logback.classic.joran.action.LoggerAction

    image.png
    為指定的包和類定義logger對象,并設(shè)置日志級別當logger帶有appender-ref子標簽時,繼續(xù)startEvent并執(zhí)行到ch.qos.logback.core.joran.action.AppenderRefAction<E>完成appender的引用

  • org.springframework.boot.logging.logback.SpringPropertyAction

    image.png
    配置需要的變量,當spring配置文件中有對應(yīng)的值時取spring中配置文件的內(nèi)容,否則使用defaultValue的值
    (application.properties的加載在加載logback-spring.xml之前,其實在LogFactory初始化的時候有過一次對logback配置文件的加載(如果存在的話,只不過在加載logback-spring.xml時重置了))

  • ch.qos.logback.core.joran.action.NewRuleAction
    自定義標簽,需要通過改action解析自定義標簽對應(yīng)action的class。因此,該標簽的配置,需要在使用該自定義標簽之前配置。

    image.png
    查找action時,key是拼接上所有的父節(jié)點如[configuration][myrule]、[configuration][appender][encorder]
    因此自定義action時,需要用“/”將父節(jié)點分隔開,向rulestore存儲action時會將pattern configuration/myrule 解析成[configuration][myrule]
    image.png
    還有其他action由于尚未使用,未曾了解。
    當所有的event執(zhí)行完之后,便完成了對logback配置文件的加載

三、logger的執(zhí)行與日志的打印

在完成LogFactory的加載后,就可以通過factory獲取logger,當logger執(zhí)行info(),debug(),warn(),error(),方法,將會執(zhí)行到
ch.qos.logback.classic.Logger.filterAndLog_1(String, Marker, Level, String, Object, Throwable)或者
ch.qos.logback.classic.Logger.filterAndLog_2(String, Marker, Level, String, Object, Object, Throwable)或者
ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(String, Marker, Level, String, Object[], Throwable)
三者的不同之處在于通過“{}”占位符可填充的參數(shù)個數(shù)不同。三者最終調(diào)用的都是
ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(String, Marker, Level, String, Object[], Throwable)

------------------->ch.qos.logback.classic.Logger.callAppenders(ILoggingEvent)
image.png
image.png
------------------>ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(E)
image.png
AsyncAppender主要完成將日志event放入ch.qos.logback.core.AsyncAppenderBase<E>的blockingQueue中,供二中開啟的worker消費。
image.png
ConsoleAppender主要完成將日志event在控制臺輸出
RollingAppender的實例在進入ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(E)之后調(diào)用this.append(eventObject);------------》ch.qos.logback.core.OutputStreamAppender.append(E)-------------》ch.qos.logback.core.rolling.RollingFileAppender.subAppend(E)此時調(diào)用rollingPOlicy----》ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP.isTriggeringEvent(File, E)
image.png
最終,這三個appender調(diào)用的super.subAppend(event)都指向----------------》ch.qos.logback.core.OutputStreamAppender.subAppend(E)--------------》ch.qos.logback.core.OutputStreamAppender.writeOut(E)-------------》調(diào)用ch.qos.logback.classic.encoder.PatternLayoutEncoder的doEncode方法,實際調(diào)用---》ch.qos.logback.core.encoder.LayoutWrappingEncoder.doEncode(E)
image.png
  • 1、layout.doLayout(event);-------------》ch.qos.logback.classic.PatternLayout.doLayout(ILoggingEvent)-----------》ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(E)
    image.png

    如:ch.qos.logback.classic.pattern.MDCConverter
    image.png

總的來說,logback提供了相當大的自定義空間,很容易進行拓展

demo地址:https://gitee.com/eshin/logbackDemo

更多文字性描述和用法,推薦參考:

https://logback.qos.ch/manual/

https://www.cnblogs.com/lixuwu/p/5804793.html (系列)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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