關(guān)于slf4j_log4j2源碼學(xué)習(xí)心得
日志工廠獲取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_
打印實(shí)際綁定的StaticLoggerBInder實(shí)例,標(biāo)識(shí)初始化成功
此時(shí)日志工廠已經(jīng)完成初始化,創(chuàng)建日志代替者工廠SubstituteLoggerFactory,獲取替代者工廠中的Loggers遍歷其名稱從日志工廠中獲取相應(yīng)的Logger,將Logger設(shè)置為替代者委派對(duì)象setDelegate
重新播放替代者工廠事件隊(duì)列中的事件
清楚替代者工廠事件隊(duì)列,日志列表
使用StaticLoggerBinder獲取日志工廠,log4j2的綁定著實(shí)現(xiàn)返回Log4jLoggerFactory
日志工廠中獲取Logger,先獲取日志上下文,從日志上下文中獲取Logger
根據(jù)日志配置屬性log4j2.loggerContextFactory獲取日志上下文工廠
不存在日志上下文工廠配置則使用ProviderUtil提供者工具類讀取SPI配置默認(rèn)為L(zhǎng)og4jContextFactory
日志上下文工廠構(gòu)造器中創(chuàng)建日志上下文選擇器createContextSelector,根據(jù)Log4jContextSelector屬性配置獲取日志上下文選擇器實(shí)現(xiàn),假定為異步實(shí)現(xiàn):AsyncLoggerContextSelector
日志上下文工廠使用日志上下文選擇器創(chuàng)建日志上下文AsyncLoggerContext,日志上下文構(gòu)造器中創(chuàng)建AsyncLoggerDisruptor
如果日志上下文處于初始化狀態(tài),啟動(dòng)日志上下文,啟動(dòng)日志上下文中的loggerDisruptor
啟動(dòng)Disruptor
計(jì)算ringBufferSize,如果啟用了Threadlocals(log4j2.enable.threadlocals)則默認(rèn)4k,否則256k,讀取AsyncLogger.RingBufferSize屬性配置;如果size小于128則使用128修正
創(chuàng)建等待策略,如果屬性名稱以AsyncLogger.開(kāi)頭,讀取AsyncLogger.Timeout,否則讀取AsyncLoggerConfig.Timeout配置的超時(shí)時(shí)間,默認(rèn)為10毫秒;讀取AsyncLogger.WaitStrategy屬性配置的策略類型,默認(rèn)為TIMEOUT,根據(jù)類型創(chuàng)建等待策略,默認(rèn)為超時(shí)類型:TimeoutBlockingWaitStrategy
創(chuàng)建單線程線程池_newSingleThreadExecutor_
創(chuàng)建異步隊(duì)列滿的處理策略AsyncQueueFullPolicyFactory.create,讀取log4j2.AsyncQueueFullPolicy策略配置,默認(rèn)策略為同步阻塞DefaultAsyncQueueFullPolicy,Discard類型為DiscardingAsyncQueueFullPolicy,否則讀取用戶自定義策略,獲取類路徑上的AsyncQueueFullPolicy實(shí)現(xiàn)類
創(chuàng)建Disruptor實(shí)例
為Disruptor實(shí)例綁定ExceptionHandler,讀取AsyncLogger.ExceptionHandler屬性配置加載實(shí)現(xiàn)類,默認(rèn)為AsyncLoggerDefaultExceptionHandler
為Disruptor實(shí)例綁定事件handler:RingBufferLogEventHandler
啟動(dòng)Disruptor
提交線程BatchEventProcessor至線程池
向序列屏障提交序列號(hào)申請(qǐng)并等待,等待策略默認(rèn)為Timeout,超時(shí)處理后進(jìn)行線程yield資源釋放
獲取到有效序列號(hào)根據(jù)序列號(hào)獲取數(shù)據(jù)RingBufferLogEvent
回調(diào)事件handle實(shí)例記錄日志RingBufferLogEventHandler.onEvent
執(zhí)行事件RingBufferLogEvent.execute(異步寫(xiě)日志)
啟動(dòng)父類日志上下文LoggerContext.start(),重新加載配置reconfigure,設(shè)置配置setConfiguration,啟動(dòng)配置config.start();
map.putIfAbsent("contextName", contextName);
config.start();
this.configuration = config;
- 由日志上下文中獲取日志getLogger,不存在則創(chuàng)建日志AsyncLogger
- 返回異步日志實(shí)例
日志輸出Logger.info

日志打印logger.info
MessageFactory2將字符串日志封裝為Message實(shí)例:ParameterizedMessageFactory.newMessage
從緩存中獲取RingBuffer日志事件轉(zhuǎn)換器:AsyncLogger.logWithThreadLocalTranslator.getCachedTranslator
初始化RingBuffer日志事件轉(zhuǎn)換器:AsyncLogger.initTranslator,RingBufferLogEventTranslator.setBasicValues設(shè)置基礎(chǔ)值
發(fā)布日志事件轉(zhuǎn)換器:RingBufferLogEventTranslator
異步日志Disruptor嘗試發(fā)布tryPublish
Disruptor獲取RingBuffer嘗試發(fā)布
轉(zhuǎn)換器將日志數(shù)據(jù)轉(zhuǎn)換為日志事件:RingBufferLogEventTranslator.translateTo
根據(jù)序號(hào)sequence獲取對(duì)應(yīng)的事件實(shí)例RingBufferLoggerEvent并將轉(zhuǎn)換器中的數(shù)據(jù)寫(xiě)入RingBufferLoggerEvent.setValues
發(fā)布消息序列號(hào)MultiProducerSequencer
等待策略發(fā)送喚醒信號(hào)TimeoutBlockingWaitStrategy.signalAllWhenBlocking
異步日志寫(xiě)入

BatchEventProcessor.eventHandler.onEvent->RingBufferLogEventHandler.onEvent->執(zhí)行事件RingBufferLogEvent.execute
執(zhí)行異步日志寫(xiě)入當(dāng)前事件消息actualAsyncLog(this)
從可靠性策略工廠中獲取可靠性策略,根據(jù)參數(shù)配置獲取:log4j.ReliabilityStrategy,默認(rèn)AwaitCompletion:AwaitCompletionReliabilityStrategy,寫(xiě)入日志strategy.log(AsyncLogger, event);
可靠性策略獲取活躍的LoggerConfig,如果沒(méi)取到,遞歸AsyncLogger的next節(jié)點(diǎn)獲取
LoggerConfig寫(xiě)入日志log,走過(guò)濾器鏈過(guò)濾事件之后處理事件processLogEvent
調(diào)用Appender:callAppenders
調(diào)用AsyncLoggerConfigDisruptor的tryEnqueue,準(zhǔn)備事件,將事件封裝為L(zhǎng)og4jLogEvent綁定至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配置實(shí)現(xiàn),調(diào)用父類tryAppend
讀取log4j2.enable.direct.encoders配置是否直接編譯,默認(rèn)為true,直接編譯事件directEncodeEvent
獲取布局Layout編譯事件encode,假定為PatternLayout配置實(shí)現(xiàn)
如果布局的事件序列化類型eventSerializer不是Serializer2,調(diào)用父類encode,否則繼續(xù)
從threadlocal中獲取StringBuilder,不存在則創(chuàng)建,如果超出最大值則trim至最大值,最大值配置:log4j.layoutStringBuilder.maxSize,默認(rèn)為2*1024,stringbuilder設(shè)置length為0
將事件序列化后放入StringBuilder:toSerializable
獲取StringBuilder編譯器編譯StringBuilder至ByteBufferDestination
manager刷新flush,刷新至目的地,假定為RollingRandomAccessFileManager配置實(shí)現(xiàn),寫(xiě)入目標(biāo)文件
至此寫(xiě)入日志完成
異步日志上下文選擇
通過(guò)管理器獲取日志上下文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實(shí)現(xiàn)
可以看到只傳了一個(gè)參數(shù)currentContext,如果為true則會(huì)根據(jù)ContextAnchor.THREAD_CONTEXT當(dāng)前線程對(duì)應(yīng)的上下文,如果為空則獲取默認(rèn)的上下文DEFAULT_CONTEXT,如果默認(rèn)為空則會(huì)創(chuàng)建并緩存
如果currentContext為false則會(huì)選擇一個(gè)最匹配的恰當(dāng)?shù)纳舷挛姆祷?,如果ClassLoader不為空則按照指定的ClassLoader定位選擇locateContext。否則按照當(dāng)前的調(diào)用類的classloader進(jìn)行定位選擇
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值獲取對(duì)應(yīng)的日志上下文,異步選擇器實(shí)現(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));
}
如果對(duì)應(yīng)的日志上下文為空則創(chuàng)建并緩存
如果configLocation為空則遍歷classloader的父classloader獲取對(duì)應(yīng)的日志上下文返回,如果不存在則繼續(xù)創(chuàng)建
如果configLocation不為空則直接根據(jù)configLocation創(chuàng)建日志上下文并按照classloader的hashcode值拼接的名稱緩存至CONTEXT_MAP
如果CONTEXT_MAP緩存存在對(duì)應(yīng)的日志上下文,并且不為空,則會(huì)判斷configLocation是否一致equals方法,如果不一致則更新后返回
如果CONTEXT_MAP緩存存在對(duì)應(yīng)的日志上下文,并且為空,則創(chuàng)建日志上下文以及日志上下文的弱引用WeakReference并緩存至
總結(jié)
對(duì)于異步日志,如果includeLocation沒(méi)有指定,默認(rèn)是關(guān)閉的狀態(tài),也就是不會(huì)記錄日志調(diào)用時(shí)的堆棧位置信息:LoggerConfig.includeLocation。
如果記錄日志時(shí)存在Throwable對(duì)象,則會(huì)記錄至RingBufferLogEventTranslator.thrown。
thrown在日志事件反序列化后為null,thrownProxy可能不為null,thrownProxy是thrown的代理,即使用thrown為入?yún)?gòu)造的代理實(shí)例
// 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);
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
BufferedReader中read()方法和readLine()方法的使用
這篇文章主要介紹了BufferedReader中read()方法和readLine()方法的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
查看本地啟動(dòng)SpringBoot的本地端口號(hào)的幾種方式
這篇文章主要介紹了查看本地啟動(dòng)SpringBoot的本地端口號(hào)的幾種方式,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-10-10
常用的Java數(shù)據(jù)結(jié)構(gòu)知識(shí)點(diǎn)匯總
這篇文章主要介紹了常用的Java數(shù)據(jù)結(jié)構(gòu)知識(shí)點(diǎn)匯總,數(shù)據(jù)結(jié)構(gòu)分線性數(shù)據(jù)結(jié)構(gòu)和非線性數(shù)據(jù)結(jié)構(gòu),下面對(duì)此作詳細(xì)介紹,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)或工作有所幫助2022-03-03
SpringMVC中的@RequestMapping注解解析
這篇文章主要介紹了SpringMVC中的@RequestMapping注解解析,SpringMVC使用@RequestMapping注解為控制器指定可以處理哪些?URL?請(qǐng)求,在控制器的類定義及方法定義處都可標(biāo)注@RequestMapping,需要的朋友可以參考下2023-12-12
IDEA+Maven搭建Spring環(huán)境的詳細(xì)教程
這篇文章主要介紹了IDEA+Maven搭建Spring環(huán)境的詳細(xì)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11

