我的编程空间,编程开发者的网络收藏夹
学习永远不晚

关于slf4j_log4j2源码学习心得

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

关于slf4j_log4j2源码学习心得

日志工厂获取Logger

slf4j_getLogger.jpg

获取日志工厂_getILoggerFactory_

执行初始化performInitialization

绑定工厂bind

查找可能被绑定的StaticLoggerBinder类路径findPossibleStaticLoggerBinderPathSet

如果LoggerFactory类加载器为空则使用System类加载器,如果System类加载器为空则使用Bootstrap类加载器加载读取org/slf4j/impl/StaticLoggerBinder.class类资源路径

如果LoggerFactory类加载器不为空,使用loggerFactoryClassLoader类加载器读取org/slf4j/impl/StaticLoggerBinder.class类资源路径

获取StaticLoggerBinder单例_SINGLETON_

打印实际绑定的StaticLoggerBInder实例,标识初始化成功

此时日志工厂已经完成初始化,创建日志代替者工厂SubstituteLoggerFactory,获取替代者工厂中的Loggers遍历其名称从日志工厂中获取相应的Logger,将Logger设置为替代者委派对象setDelegate

重新播放替代者工厂事件队列中的事件

清楚替代者工厂事件队列,日志列表

使用StaticLoggerBinder获取日志工厂,log4j2的绑定着实现返回Log4jLoggerFactory

日志工厂中获取Logger,先获取日志上下文,从日志上下文中获取Logger

根据日志配置属性log4j2.loggerContextFactory获取日志上下文工厂

不存在日志上下文工厂配置则使用ProviderUtil提供者工具类读取SPI配置默认为Log4jContextFactory

日志上下文工厂构造器中创建日志上下文选择器createContextSelector,根据Log4jContextSelector属性配置获取日志上下文选择器实现,假定为异步实现:AsyncLoggerContextSelector

日志上下文工厂使用日志上下文选择器创建日志上下文AsyncLoggerContext,日志上下文构造器中创建AsyncLoggerDisruptor

如果日志上下文处于初始化状态,启动日志上下文,启动日志上下文中的loggerDisruptor

启动Disruptor

计算ringBufferSize,如果启用了Threadlocals(log4j2.enable.threadlocals)则默认4k,否则256k,读取AsyncLogger.RingBufferSize属性配置;如果size小于128则使用128修正

创建等待策略,如果属性名称以AsyncLogger.开头,读取AsyncLogger.Timeout,否则读取AsyncLoggerConfig.Timeout配置的超时时间,默认为10毫秒;读取AsyncLogger.WaitStrategy属性配置的策略类型,默认为TIMEOUT,根据类型创建等待策略,默认为超时类型:TimeoutBlockingWaitStrategy

创建单线程线程池_newSingleThreadExecutor_

创建异步队列满的处理策略AsyncQueueFullPolicyFactory.create,读取log4j2.AsyncQueueFullPolicy策略配置,默认策略为同步阻塞DefaultAsyncQueueFullPolicy,Discard类型为DiscardingAsyncQueueFullPolicy,否则读取用户自定义策略,获取类路径上的AsyncQueueFullPolicy实现类

创建Disruptor实例

为Disruptor实例绑定ExceptionHandler,读取AsyncLogger.ExceptionHandler属性配置加载实现类,默认为AsyncLoggerDefaultExceptionHandler

为Disruptor实例绑定事件handler:RingBufferLogEventHandler

启动Disruptor

提交线程BatchEventProcessor至线程池

向序列屏障提交序列号申请并等待,等待策略默认为Timeout,超时处理后进行线程yield资源释放

获取到有效序列号根据序列号获取数据RingBufferLogEvent

回调事件handle实例记录日志RingBufferLogEventHandler.onEvent

执行事件RingBufferLogEvent.execute(异步写日志)

启动父类日志上下文LoggerContext.start(),重新加载配置reconfigure,设置配置setConfiguration,启动配置config.start();


map.putIfAbsent("contextName", contextName);
config.start();
this.configuration = config;
  • 由日志上下文中获取日志getLogger,不存在则创建日志AsyncLogger
  • 返回异步日志实例

日志输出Logger.info

slf4j_AsyncLogger.jpg

日志打印logger.info

MessageFactory2将字符串日志封装为Message实例:ParameterizedMessageFactory.newMessage

从缓存中获取RingBuffer日志事件转换器:AsyncLogger.logWithThreadLocalTranslator.getCachedTranslator

初始化RingBuffer日志事件转换器:AsyncLogger.initTranslator,RingBufferLogEventTranslator.setBasicValues设置基础值

发布日志事件转换器:RingBufferLogEventTranslator

异步日志Disruptor尝试发布tryPublish

Disruptor获取RingBuffer尝试发布

转换器将日志数据转换为日志事件:RingBufferLogEventTranslator.translateTo

根据序号sequence获取对应的事件实例RingBufferLoggerEvent并将转换器中的数据写入RingBufferLoggerEvent.setValues

发布消息序列号MultiProducerSequencer

等待策略发送唤醒信号TimeoutBlockingWaitStrategy.signalAllWhenBlocking

异步日志写入

slf4j_writeLogger.jpg

BatchEventProcessor.eventHandler.onEvent->RingBufferLogEventHandler.onEvent->执行事件RingBufferLogEvent.execute

执行异步日志写入当前事件消息actualAsyncLog(this)

从可靠性策略工厂中获取可靠性策略,根据参数配置获取:log4j.ReliabilityStrategy,默认AwaitCompletion:AwaitCompletionReliabilityStrategy,写入日志strategy.log(AsyncLogger, event);

可靠性策略获取活跃的LoggerConfig,如果没取到,递归AsyncLogger的next节点获取

LoggerConfig写入日志log,走过滤器链过滤事件之后处理事件processLogEvent

调用Appender:callAppenders

调用AsyncLoggerConfigDisruptor的tryEnqueue,准备事件,将事件封装为Log4jLogEvent绑定至Log4jEventWrapper.event然后尝试发布

事件handle处理事件Log4jEventWrapperHandler.onEvent

异步调用Appender:asyncCallAppenders,调用父类callAppenders

遍历AppenderControl调用callAppender,调用callAppenderPreventRecursion

尝试appender tryCallAppender

调用Appender追加方法append追加日志,假定为RollingRandomAccessFileAppender配置实现,调用父类tryAppend

读取log4j2.enable.direct.encoders配置是否直接编译,默认为true,直接编译事件directEncodeEvent

获取布局Layout编译事件encode,假定为PatternLayout配置实现

如果布局的事件序列化类型eventSerializer不是Serializer2,调用父类encode,否则继续

从threadlocal中获取StringBuilder,不存在则创建,如果超出最大值则trim至最大值,最大值配置:log4j.layoutStringBuilder.maxSize,默认为2*1024,stringbuilder设置length为0

将事件序列化后放入StringBuilder:toSerializable

获取StringBuilder编译器编译StringBuilder至ByteBufferDestination

manager刷新flush,刷新至目的地,假定为RollingRandomAccessFileManager配置实现,写入目标文件

至此写入日志完成

异步日志上下文选择

通过管理器获取日志上下文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);
    }
}

上下文根据选择器选择获取,我们假定是AsyncLoggerContextSelector类型,获取方式是其超类ClassLoaderContextSelector提供getContext实现

可以看到只传了一个参数currentContext,如果为true则会根据ContextAnchor.THREAD_CONTEXT当前线程对应的上下文,如果为空则获取默认的上下文DEFAULT_CONTEXT,如果默认为空则会创建并缓存

如果currentContext为false则会选择一个最匹配的恰当的上下文返回,如果ClassLoader不为空则按照指定的ClassLoader定位选择locateContext。否则按照当前的调用类的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定位选择日志上下文

根据classloader的hashcode值获取对应的日志上下文,异步选择器实现添加了前缀


@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));
    }

如果对应的日志上下文为空则创建并缓存

如果configLocation为空则遍历classloader的父classloader获取对应的日志上下文返回,如果不存在则继续创建

如果configLocation不为空则直接根据configLocation创建日志上下文并按照classloader的hashcode值拼接的名称缓存至CONTEXT_MAP

如果CONTEXT_MAP缓存存在对应的日志上下文,并且不为空,则会判断configLocation是否一致equals方法,如果不一致则更新后返回

如果CONTEXT_MAP缓存存在对应的日志上下文,并且为空,则创建日志上下文以及日志上下文的弱引用WeakReference并缓存至

总结

对于异步日志,如果includeLocation没有指定,默认是关闭的状态,也就是不会记录日志调用时的堆栈位置信息:LoggerConfig.includeLocation。

如果记录日志时存在Throwable对象,则会记录至RingBufferLogEventTranslator.thrown。

thrown在日志事件反序列化后为null,thrownProxy可能不为null,thrownProxy是thrown的代理,即使用thrown为入参构造的代理实例


    // 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);
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

关于slf4j_log4j2源码学习心得

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

基于android startActivityForResult的学习心得总结

从昨晚到现在终于调试通了一个startActivityForResult的例子,网上要么有些说的太复杂了,要么说的含糊,搞的我走了很多弯路,所以写篇心得。在一个主界面(主Activity)上能连接往许多不同子功能模块(子Activity上去
2022-06-06

关于Javascript中值得学习的特性总结

本文主要介绍了一些Javascript中值得学习的特性,可选链操作符,空值合并运算符,Promise.allSettled(),BigInt类型等特性,文中有详细的代码示例介绍这些特性,感兴趣的可以参考下
2023-05-19

关于学习canvas,初学者应该掌握哪些方法和资源?

初学者必备的学习canvas的方法和资源有哪些?随着互联网的发展,前端技术在不断更新和演进,canvas作为HTML5标准的重要组成部分之一,开发者对canvas的需求也越来越多。canvas提供了一种通过脚本来绘制图形、动画以及图像的方
关于学习canvas,初学者应该掌握哪些方法和资源?
2024-01-17

学习数据库,你不得不知道的关于索引的小知识

有了图,接下来,就是对于我今天看的内容觉得比较好的分享,文末有福利 1、select * 对效率的影响 在我们平时的代码编写或面试题中,很多人都会疑惑:select * 到底合理吗? 如果说不合理,为什么?如果说合理,原因又是什么? 1)、阿里规范 在阿里j
学习数据库,你不得不知道的关于索引的小知识
2018-01-19

字字谏言!Python入门学习教程:关于Python不得不说的事儿

作为程序员,你肯定遇到过这样的领导:“别人还没走,你先走了不合适吧。”“不能确定功劳,总得有苦劳吧!你看别人9点谁走了?”说到996,这很有可能是诱因之一。所以很多程序员会在学习了Java、C的情况下,依然转头Python的怀抱。提升工作效
2023-06-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录