返回顶部
首页 > 资讯 > 后端开发 > Python >关于slf4j_log4j2源码学习心得
  • 670
分享到

关于slf4j_log4j2源码学习心得

2024-04-02 19:04:59 670人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录日志工厂获取Logger日志输出Logger.info异步日志写入异步日志上下文选择locateContext定位选择日志上下文总结日志工厂获取Logger 获取日志工厂_ge

日志工厂获取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);
    }

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

--结束END--

本文标题: 关于slf4j_log4j2源码学习心得

本文链接: https://lsjlt.com/news/160542.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 关于slf4j_log4j2源码学习心得
    目录日志工厂获取Logger日志输出Logger.info异步日志写入异步日志上下文选择locateContext定位选择日志上下文总结日志工厂获取Logger 获取日志工厂_ge...
    99+
    2024-04-02
  • 基于android startActivityForResult的学习心得总结
    从昨晚到现在终于调试通了一个startActivityForResult的例子,网上要么有些说的太复杂了,要么说的含糊,搞的我走了很多弯路,所以写篇心得。在一个主界面(主Act...
    99+
    2022-06-06
    学习心得 学习 Android
  • ITIL V3学习心得
    在深入研究V3之后,逐步认识到该理论对实际运维工作的指导意义,从认识流程,到掌握流程,最后是灵活运用流程,了解到流程对IT服务质量管理的改善,同时也对流程设计和实施产生困惑。虽然,我们从网络上能够搜索到很多方面的介绍,但是看过之...
    99+
    2023-01-31
    学习心得 ITIL
  • 15、OSPF学习心得3
    1、DR和BDR的选举双方同时进入2-Way状态开始选举,选举时间=死亡时间(40S)(1) 首先检查DR和BDR字段是否为空,如果都为空(0.0.0.0)表明没有DR也没有BDR。(2) 先选举出BDR(DR、BDR都为空)① 先比较优先...
    99+
    2023-01-31
    学习心得 OSPF
  • 关于Javascript中值得学习的特性总结
    目录可选链操作符(Optional Chaining Operator)空值合并运算符(Nullish Coalescing Operator)Promise.allSettled(...
    99+
    2023-05-19
    Javascript特性
  • [心得] 关于 JSON 中文问题
      忽然感觉很久没上博了,今日上来一看居然将近一个月没写新文章了,再不“生产”两篇,感觉也太对不起自己的博了。最近正好项目里有用到一些 JSON 的部分,于是今天就写一点使用心得来与大家分享一下吧。  说道 JSON 还真是一个好东西,一个...
    99+
    2023-06-03
  • python学习心得-第一天-作业
    python学习第一天作业作业1用户输入帐号密码进行登陆用户信息保存在文件内用户密码输入错误三次后锁定用户代码:#__author__ = 'leslie' #-*-coding:utf-8-*- # #1. 用户输入帐号密码进行登陆 #2...
    99+
    2023-01-31
    作业 学习心得 python
  • 阿里云数据库学习心得
    阿里云数据库学习心得 阿里云数据库是阿里集团自研的分布式关系型数据库,它采用了多种技术,包括分布式计算、内存计算、GPU加速等,可以满足大规模数据的存储和处理需求。在学习阿里云数据库的过程中,我收获了很多宝贵的经验和知识,也发现了一些值得...
    99+
    2023-10-30
    阿里 学习心得 数据库
  • Java异常的学习心得是什么
    这期内容当中小编将会给大家带来有关Java异常的学习心得是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一. 异常机制异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全...
    99+
    2023-06-17
  • java8新特性-Stream入门学习心得
    目录Stream是什么?创建StreamStream常见的操作总结上一篇介绍了Lambda的学习,如果对Lambda表达式还不清晰的同学可以戳一下这个链接:java8新特性-lamb...
    99+
    2023-03-13
    java8新特性 Stream入门学习 java8 Stream
  • 学习EJB CMP/CMR 的心得体会 (转)
    学习EJB CMP/CMR 的心得体会 (转)[@more@]  本人学习EJB也有一年多的时间了,前段时间公司接到一个大的项目才真正的用到,客户使用的webLOGIC 7.0,数据库用的是sql SERVER 2000,所以我只...
    99+
    2023-06-03
  • Vue源码中值得学习的方法有哪些
    本篇内容介绍了“Vue源码中值得学习的方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 数据类...
    99+
    2024-04-02
  • Android编程心得分享——JSON学习过程
    让我说说Android编程心得,在我们初步学习JSON时我们都知道JSON作为现在比较流行的数据交换格式,有着它的许多优点,Android的WEB应用中更是广泛用到了它,这里将...
    99+
    2022-06-06
    JSON Android
  • ReactRouter中Link和NavLink的学习心得总结
    目录React Router Link和NavLink的学习LinkNavLink总结React Router Link和NavLink的学习 Link 现在,我们应用需要在各个页面...
    99+
    2022-12-08
    React Router中Link React Router中NavLink Link和NavLink学习
  • Android中Intent组件的入门学习心得
    目录什么是 Intent ?Intent 的类型?Intent 的组成显式 Intent 的使用隐式 Intent 的使用总结什么是 Intent ? Intent是Android开...
    99+
    2024-04-02
  • 关于JAVA8的 Stream学习
    目录一、Stream的使用1 创建2 步骤二、Stream的特性三、中间操作1 filter()2 limit()3 skip()4 map()3.1 flatM...
    99+
    2024-04-02
  • python 关于epoll的学习
          在linux中,默认情况下所有的socket都是blocking;当 用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比...
    99+
    2023-01-31
    python epoll
  • php积极心理学交流学习网站 毕业设计源码100623
    摘要 心理测试在我国兴起还是近几年的事,由于对心理健康认识不足,观念陈旧,一些人虽然有心理问题或有心理疾病症状,但却想不到或不敢去心理测试。因为他们中有些人是不知道自己的这些问题是应该找心理医生呢还是找生理医生,而另一些人认为看心理...
    99+
    2023-09-01
    php spring boot css java vue Powered by 金山文档
  • 关于Python数据结构中字典的心得
    本篇主要介绍:常见的字典方法、如何处理查不到的键、标准库中 dict 类型的变种、散列表的工作原理等。一下是全部内容: 泛映射类型 collections.abc 模块中有 Mapping 和 Mutabl...
    99+
    2022-06-04
    数据结构 字典 心得
  • 关于react-router中的Prompt组件使用心得
    目录Prompt组件作用引入Prompt组件在最近的react项目中, 遇到了一个需求, 点击图片的时候, 会出现一个大图预览, 其实就是一个遮罩层, 专门用来显示大图的, 但因为是...
    99+
    2023-01-17
    react router中Prompt组件 react router组件 Prompt组件
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作