關于slf4j_log4j2源碼學習心得
日志工廠獲取Logger
獲取日志工廠_getILoggerFactory_
執(zhí)行初始化performInitialization
綁定工廠bind
查找可能被綁定的StaticLoggerBinder類路徑findPossibleStaticLoggerBinderPathSet
如果LoggerFactory類加載器為空則使用System類加載器,如果System類加載器為空則使用Bootstrap類加載器加載讀取org/slf4j/impl/StaticLoggerBinder.class類資源路徑
如果LoggerFactory類加載器不為空,使用loggerFactoryClassLoader類加載器讀取org/slf4j/impl/StaticLoggerBinder.class類資源路徑
獲取StaticLoggerBinder單例_SINGLETON_
打印實際綁定的StaticLoggerBInder實例,標識初始化成功
此時日志工廠已經(jīng)完成初始化,創(chuàng)建日志代替者工廠SubstituteLoggerFactory,獲取替代者工廠中的Loggers遍歷其名稱從日志工廠中獲取相應的Logger,將Logger設置為替代者委派對象setDelegate
重新播放替代者工廠事件隊列中的事件
清楚替代者工廠事件隊列,日志列表
使用StaticLoggerBinder獲取日志工廠,log4j2的綁定著實現(xiàn)返回Log4jLoggerFactory
日志工廠中獲取Logger,先獲取日志上下文,從日志上下文中獲取Logger
根據(jù)日志配置屬性log4j2.loggerContextFactory獲取日志上下文工廠
不存在日志上下文工廠配置則使用ProviderUtil提供者工具類讀取SPI配置默認為Log4jContextFactory
日志上下文工廠構(gòu)造器中創(chuàng)建日志上下文選擇器createContextSelector,根據(jù)Log4jContextSelector屬性配置獲取日志上下文選擇器實現(xiàn),假定為異步實現(xiàn):AsyncLoggerContextSelector
日志上下文工廠使用日志上下文選擇器創(chuàng)建日志上下文AsyncLoggerContext,日志上下文構(gòu)造器中創(chuàng)建AsyncLoggerDisruptor
如果日志上下文處于初始化狀態(tài),啟動日志上下文,啟動日志上下文中的loggerDisruptor
啟動Disruptor
計算ringBufferSize,如果啟用了Threadlocals(log4j2.enable.threadlocals)則默認4k,否則256k,讀取AsyncLogger.RingBufferSize屬性配置;如果size小于128則使用128修正
創(chuàng)建等待策略,如果屬性名稱以AsyncLogger.開頭,讀取AsyncLogger.Timeout,否則讀取AsyncLoggerConfig.Timeout配置的超時時間,默認為10毫秒;讀取AsyncLogger.WaitStrategy屬性配置的策略類型,默認為TIMEOUT,根據(jù)類型創(chuàng)建等待策略,默認為超時類型:TimeoutBlockingWaitStrategy
創(chuàng)建單線程線程池_newSingleThreadExecutor_
創(chuàng)建異步隊列滿的處理策略AsyncQueueFullPolicyFactory.create,讀取log4j2.AsyncQueueFullPolicy策略配置,默認策略為同步阻塞DefaultAsyncQueueFullPolicy,Discard類型為DiscardingAsyncQueueFullPolicy,否則讀取用戶自定義策略,獲取類路徑上的AsyncQueueFullPolicy實現(xiàn)類
創(chuàng)建Disruptor實例
為Disruptor實例綁定ExceptionHandler,讀取AsyncLogger.ExceptionHandler屬性配置加載實現(xiàn)類,默認為AsyncLoggerDefaultExceptionHandler
為Disruptor實例綁定事件handler:RingBufferLogEventHandler
啟動Disruptor
提交線程BatchEventProcessor至線程池
向序列屏障提交序列號申請并等待,等待策略默認為Timeout,超時處理后進行線程yield資源釋放
獲取到有效序列號根據(jù)序列號獲取數(shù)據(jù)RingBufferLogEvent
回調(diào)事件handle實例記錄日志RingBufferLogEventHandler.onEvent
執(zhí)行事件RingBufferLogEvent.execute(異步寫日志)
啟動父類日志上下文LoggerContext.start(),重新加載配置reconfigure,設置配置setConfiguration,啟動配置config.start();
map.putIfAbsent("contextName", contextName); config.start(); this.configuration = config;
- 由日志上下文中獲取日志getLogger,不存在則創(chuàng)建日志AsyncLogger
- 返回異步日志實例
日志輸出Logger.info
日志打印logger.info
MessageFactory2將字符串日志封裝為Message實例:ParameterizedMessageFactory.newMessage
從緩存中獲取RingBuffer日志事件轉(zhuǎn)換器:AsyncLogger.logWithThreadLocalTranslator.getCachedTranslator
初始化RingBuffer日志事件轉(zhuǎn)換器:AsyncLogger.initTranslator,RingBufferLogEventTranslator.setBasicValues設置基礎值
發(fā)布日志事件轉(zhuǎn)換器:RingBufferLogEventTranslator
異步日志Disruptor嘗試發(fā)布tryPublish
Disruptor獲取RingBuffer嘗試發(fā)布
轉(zhuǎn)換器將日志數(shù)據(jù)轉(zhuǎn)換為日志事件:RingBufferLogEventTranslator.translateTo
根據(jù)序號sequence獲取對應的事件實例RingBufferLoggerEvent并將轉(zhuǎn)換器中的數(shù)據(jù)寫入RingBufferLoggerEvent.setValues
發(fā)布消息序列號MultiProducerSequencer
等待策略發(fā)送喚醒信號TimeoutBlockingWaitStrategy.signalAllWhenBlocking
異步日志寫入
BatchEventProcessor.eventHandler.onEvent->RingBufferLogEventHandler.onEvent->執(zhí)行事件RingBufferLogEvent.execute
執(zhí)行異步日志寫入當前事件消息actualAsyncLog(this)
從可靠性策略工廠中獲取可靠性策略,根據(jù)參數(shù)配置獲?。簂og4j.ReliabilityStrategy,默認AwaitCompletion:AwaitCompletionReliabilityStrategy,寫入日志strategy.log(AsyncLogger, event);
可靠性策略獲取活躍的LoggerConfig,如果沒取到,遞歸AsyncLogger的next節(jié)點獲取
LoggerConfig寫入日志log,走過濾器鏈過濾事件之后處理事件processLogEvent
調(diào)用Appender:callAppenders
調(diào)用AsyncLoggerConfigDisruptor的tryEnqueue,準備事件,將事件封裝為Log4jLogEvent綁定至Log4jEventWrapper.event然后嘗試發(fā)布
事件handle處理事件Log4jEventWrapperHandler.onEvent
異步調(diào)用Appender:asyncCallAppenders,調(diào)用父類callAppenders
遍歷AppenderControl調(diào)用callAppender,調(diào)用callAppenderPreventRecursion
嘗試appender tryCallAppender
調(diào)用Appender追加方法append追加日志,假定為RollingRandomAccessFileAppender配置實現(xiàn),調(diào)用父類tryAppend
讀取log4j2.enable.direct.encoders配置是否直接編譯,默認為true,直接編譯事件directEncodeEvent
獲取布局Layout編譯事件encode,假定為PatternLayout配置實現(xiàn)
如果布局的事件序列化類型eventSerializer不是Serializer2,調(diào)用父類encode,否則繼續(xù)
從threadlocal中獲取StringBuilder,不存在則創(chuàng)建,如果超出最大值則trim至最大值,最大值配置:log4j.layoutStringBuilder.maxSize,默認為2*1024,stringbuilder設置length為0
將事件序列化后放入StringBuilder:toSerializable
獲取StringBuilder編譯器編譯StringBuilder至ByteBufferDestination
manager刷新flush,刷新至目的地,假定為RollingRandomAccessFileManager配置實現(xiàn),寫入目標文件
至此寫入日志完成
異步日志上下文選擇
通過管理器獲取日志上下文LogManager.getContext(false)
public static LoggerContext getContext(final boolean currentContext) { // TODO: would it be a terrible idea to try and find the caller ClassLoader here? try { return factory.getContext(FQCN, null, null, currentContext, null, null); } catch (final IllegalStateException ex) { LOGGER.warn(ex.getMessage() + " Using SimpleLogger"); return new SimpleLoggerContextFactory().getContext(FQCN, null, null, currentContext, null, null); } }
上下文根據(jù)選擇器選擇獲取,我們假定是AsyncLoggerContextSelector類型,獲取方式是其超類ClassLoaderContextSelector提供getContext實現(xiàn)
可以看到只傳了一個參數(shù)currentContext,如果為true則會根據(jù)ContextAnchor.THREAD_CONTEXT當前線程對應的上下文,如果為空則獲取默認的上下文DEFAULT_CONTEXT,如果默認為空則會創(chuàng)建并緩存
如果currentContext為false則會選擇一個最匹配的恰當?shù)纳舷挛姆祷兀绻鸆lassLoader不為空則按照指定的ClassLoader定位選擇locateContext。否則按照當前的調(diào)用類的classloader進行定位選擇
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext, final URI configLocation) { if (currentContext) { final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get(); if (ctx != null) { return ctx; } return getDefault(); } else if (loader != null) { return locateContext(loader, configLocation); } else { final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn); if (clazz != null) { return locateContext(clazz.getClassLoader(), configLocation); } final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get(); if (lc != null) { return lc; } return getDefault(); } }
locateContext定位選擇日志上下文
根據(jù)classloader的hashcode值獲取對應的日志上下文,異步選擇器實現(xiàn)添加了前綴
@Override protected String toContextMapKey(final ClassLoader loader) { // LOG4J2-666 ensure unique name across separate instances created by webapp classloaders return "AsyncContext@" + Integer.toHexString(System.identityHashCode(loader)); }
如果對應的日志上下文為空則創(chuàng)建并緩存
如果configLocation為空則遍歷classloader的父classloader獲取對應的日志上下文返回,如果不存在則繼續(xù)創(chuàng)建
如果configLocation不為空則直接根據(jù)configLocation創(chuàng)建日志上下文并按照classloader的hashcode值拼接的名稱緩存至CONTEXT_MAP
如果CONTEXT_MAP緩存存在對應的日志上下文,并且不為空,則會判斷configLocation是否一致equals方法,如果不一致則更新后返回
如果CONTEXT_MAP緩存存在對應的日志上下文,并且為空,則創(chuàng)建日志上下文以及日志上下文的弱引用WeakReference并緩存至
總結(jié)
對于異步日志,如果includeLocation沒有指定,默認是關閉的狀態(tài),也就是不會記錄日志調(diào)用時的堆棧位置信息:LoggerConfig.includeLocation。
如果記錄日志時存在Throwable對象,則會記錄至RingBufferLogEventTranslator.thrown。
thrown在日志事件反序列化后為null,thrownProxy可能不為null,thrownProxy是thrown的代理,即使用thrown為入?yún)?gòu)造的代理實例
// Note: for asynchronous loggers, includeLocation default is FALSE, // for synchronous loggers, includeLocation default is TRUE. protected static boolean includeLocation(final String includeLocationConfigValue) { if (includeLocationConfigValue == null) { final boolean sync = !AsyncLoggerContextSelector.isSelected(); return sync; } return Boolean.parseBoolean(includeLocationConfigValue); }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java編程實現(xiàn)判斷網(wǎng)上鄰居文件是否存在的方法
這篇文章主要介紹了Java編程實現(xiàn)判斷網(wǎng)上鄰居文件是否存在的方法,涉及Java針對路徑轉(zhuǎn)換及字符串操作的相關技巧,需要的朋友可以參考下2015-10-10redis setIfAbsent和setnx的區(qū)別與使用說明
這篇文章主要介紹了redis setIfAbsent和setnx的區(qū)別與使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Springboot獲取文件內(nèi)容如何將MultipartFile轉(zhuǎn)File
本文給大家介紹Springboot獲取文件內(nèi)容,將MultipartFile轉(zhuǎn)File方法,本文結(jié)合示例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧2024-01-01