最近在學(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)------------------》org.slf4j.LoggerFactory.getLogger(String)(可直接調(diào)用此處獲取logger)---------------------》org.slf4j.LoggerFactory.getILoggerFactory()image.png
--------------》org.slf4j.LoggerFactory.performInitialization()------》org.slf4j.LoggerFactory.bind()image.pngimage.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();
ch.qos.logback.classic.util.ContextInitializer.findURLOfDefaultConfigurationFile(boolean)在類路徑下查找logback.groovy ,logback.xml ,logback-test.xml查找這三個文件,若能找到這三個文件,則通過這三個配置文件生成Configurator,否則生成默認的ch.qos.logback.classic.BasicConfigurator。image.png- 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文件中
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.pngimage.png
- 1、加載配置文件 -----------》org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LoggingInitializationContext, String, LogFile)-----------》
image.png1.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.pngimage.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.pngimage.png- 1.1.2----------------->ch.qos.logback.core.joran.spi.EventPlayer.play(List<SaxEvent>)
---------------->ch.qos.logback.core.joran.spi.nterpreter.startElement(StartEvent)----------------->ch.qos.logback.core.joran.spi.Interpreter.startElement(String, String, String, Attributes)--------------------->image.pngimage.pngimage.png
主要的幾個action的功能:- ch.qos.logback.classic.joran.action.ConfigurationAction
當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。image.png
ch.qos.logback.core.joran.action.IncludeAction
將logback-spring.xml中include標簽指定的xml配置文件加載并加入WatchList,然后與logback-spring.xml文件一樣處理成recordEvents,并加入到加載logback-spring.xml時生成的record(recorder.saxEventList)中。image.pngch.qos.logback.core.joran.action.ConversionRuleAction
image.png用于初始化converterimage.png
conversionWord的值就是配置pattern時需要匹配成對應(yīng)值的key,如下image.png當converter匹配到對應(yīng)的key便轉(zhuǎn)換成需要打印的日志內(nèi)容,因此,可自定義converter及pattern關(guān)鍵字實現(xiàn)更靈活的日志打印image.pngch.qos.logback.core.joran.action.PropertyAction
用于存儲properties,默認scope為local,存在InterpretationContext中,當scope為context時,存在logContext中,為system時存為系統(tǒng)變量image.pngch.qos.logback.core.joran.action.AppenderAction<E>
image.png完成appender的初始化,ch.qos.logback.core.rolling.RollingFileAppender<E>,該appender設(shè)置rollingPolicy,調(diào)用start()方法時會檢測rollingPolicy是否生效以及需要的日志文件是否存在,若不存在便根據(jù)在rollingpolicy設(shè)置的pattern新建一個。image.png
在打印日志調(diào)用ch.qos.logback.core.rolling.RollingFileAppender.subAppend(E)時,會先調(diào)用判斷是否需要分割文件(打印日志步驟)當有子標簽時需要其他的action完成配置和引用,如:ch.qos.logback.core.joran.action.NestedComplexPropertyIA完成對rollingPolicy,encoder,等的加載,當仍有子標簽時,按同樣的方式遍歷event加載。bodyevent直接設(shè)置值。image.png
NestedComplexPropertyIA加載時,當有配置class則根據(jù)class加載實例(rollingPolicy),若沒有配置class,則按標簽名為規(guī)則去查找(encoder)在之前的步驟中,配置了默認的encoder,layout,和evalator,因此也可以根據(jù)需要自定義需要的encoder等,并通過配置class的方式配置,image.png
通過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.pngimage.png- 1、ch.qos.logback.core.pattern.PatternLayoutBase.getEffectiveConverterMap()
image.pngp.compile();方法會將與patter的關(guān)鍵字匹配的converter保留,其余的過濾掉
- 2、start方法主要完成converter的一些初始化工作,設(shè)置context,key,以便調(diào)用convert方法的時候進行內(nèi)容轉(zhuǎn)換。
當appender的類型是AsyncAppender時,在ch.qos.logback.core.joran.action.AppenderAction.end(InterpretationContext, String)時,ch.qos.logback.core.AsyncAppenderBase.start()會啟動一個異步worker,對日志event進行異步打印處理。image.pngimage.pngch.qos.logback.classic.joran.action.LoggerAction
為指定的包和類定義logger對象,并設(shè)置日志級別當logger帶有appender-ref子標簽時,繼續(xù)startEvent并執(zhí)行到ch.qos.logback.core.joran.action.AppenderRefAction<E>完成appender的引用image.pngorg.springframework.boot.logging.logback.SpringPropertyAction
配置需要的變量,當spring配置文件中有對應(yīng)的值時取spring中配置文件的內(nèi)容,否則使用defaultValue的值image.png
(application.properties的加載在加載logback-spring.xml之前,其實在LogFactory初始化的時候有過一次對logback配置文件的加載(如果存在的話,只不過在加載logback-spring.xml時重置了))ch.qos.logback.core.joran.action.NewRuleAction
自定義標簽,需要通過改action解析自定義標簽對應(yīng)action的class。因此,該標簽的配置,需要在使用該自定義標簽之前配置。查找action時,key是拼接上所有的父節(jié)點如[configuration][myrule]、[configuration][appender][encorder]image.png
因此自定義action時,需要用“/”將父節(jié)點分隔開,向rulestore存儲action時會將pattern configuration/myrule 解析成[configuration][myrule]還有其他action由于尚未使用,未曾了解。image.png
當所有的event執(zhí)行完之后,便完成了對logback配置文件的加載
三、logger的執(zhí)行與日志的打印
在完成LogFactory的加載后,就可以通過factory獲取logger,當logger執(zhí)行info(),debug(),warn(),error(),方法,將會執(zhí)行到
------------------->ch.qos.logback.classic.Logger.callAppenders(ILoggingEvent)
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)
image.png------------------>ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(E)image.pngAsyncAppender主要完成將日志event放入ch.qos.logback.core.AsyncAppenderBase<E>的blockingQueue中,供二中開啟的worker消費。image.pngConsoleAppender主要完成將日志event在控制臺輸出image.png
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)最終,這三個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.pngimage.png
- 1、layout.doLayout(event);-------------》ch.qos.logback.classic.PatternLayout.doLayout(ILoggingEvent)-----------》ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(E)
如:ch.qos.logback.classic.pattern.MDCConverterimage.pngimage.png
總的來說,logback提供了相當大的自定義空間,很容易進行拓展
demo地址:https://gitee.com/eshin/logbackDemo
更多文字性描述和用法,推薦參考:














































