Log4j2源码解析:同步写、异步写原理、中间技术思考-天天看热讯
Log4j2中的组件 从配置开始API基本使用小细节 写日志的原理主要流程 同步写异步写 顺便说一下ArrayBlockingQueue notFull 与 notEmpty 异步写是怎么玩的巧妙的异步写设计 ByteBuffer与RandomAccessFileGarbage-free避免创建多余对象异步Logger性能 写在最后
(资料图片仅供参考)
本文主要记录我对Log4j2源码阅读后的一些个人理解,包括的内容有:Log4j2的组件、同步写、异步写原理、以及中间技术的个人思考。而log4j2的使用与配置稍显简易,可在很多地方找到说明,本文则不重点讨论这部分的内容。
Log4j2中的组件
Log4j2以插件的方式来配置各个组件,在配置中可自由的插拔组件,并支持自动生效(配置monitorInterval)。
从配置开始
Log4j2支持多种格式配置文件,xml、json、yaml、properties。相应地,初始化时会逐个查找并加载这类文件。以最常用的xml配置为例,Log4j2默认的文件名称为log4j2.xml,主要配置如下:
target/rolling.log
如上配置的对象有: 1. Configuration: 表示日志环境的一份配置描述,用于构建一份运行时的LoggerContext2. Logger表示日志记录器,可有多个。示例中有2个普通记录器,1个根记录器。一个记录器需要指定Appender,可指定多个,表示日志记录到哪里。 3. Appenders表示日志的输出源,可有多个。示例中有3个,Console表示通过标准输出流输出到控制台,RollingRandomAccessFile表示可滚动的记录日志,日志文件到达一定的大小后,会启用压缩。 Async表示一种异步的输出端,通过异步线程与队列的方式写入日志到其关联的实际Appender中。
API基本使用
// 获取Logger private static final Logger logger = LogManager.getLogger("HelloWorld"); private static final Logger logger = LogManager.getLogger(Class.getName()); logger.info("Hello, World!");logger.debug("Logging in user {} with birthday {}", user.getName(), user.getBirthdayCalendar());logger.debug("Logging in user %s with birthday %s", user.getName(), user.getBirthdayCalendar());
小细节
Log4j2在对外的info,warn等API上在内置了logIfEnabled的判断,而不用在程序中显示的写上:
if (logger.isInfoEnabled()) { logger.info();}if (logger.isWarnEnabled()) { logger.warn();}
所以在写日志时,放心的直接用logger.info,logger.warn吧。
写日志的原理
主要流程
同步写
如上流程是典型的同步写的过程,多线程并发写是通过临界区来实现。简要过程如下:
相关代码: AbstractOutputStreamAppender#directEncodeEvent 以常用的基于PatternLayout的日志格式为例:
private void encodeSynchronized(final CharsetEncoder charsetEncoder, final CharBuffer charBuffer, final StringBuilder source, final ByteBufferDestination destination) { // 临界区阻塞式Encode synchronized (destination) { try { TextEncoderHelper.encodeText(charsetEncoder, charBuffer, destination.getByteBuffer(), source, destination); } catch (final Exception ex) { .. } } }// 根据不同的Appender用OutputStream.writeBytespublic synchronized void flush() { flushBuffer(byteBuffer); flushDestination();}
异步写
如上配置的Async表示一种异步输出器,对应的实现为AsyncAppender。 异步Appender使用异步线程加阻塞队列(BlockingQueue)来实现异步写的功能。默认情况下通过BlockingQueueFactory创建缺省的队列类型为:ArrayBlockingQueue,队列大小为128.
顺便说一下ArrayBlockingQueue
notFull 与 notEmpty
put和take阻塞调用线程是借用notFull和notEmpty两个条件对象来实现的。
所以在理解这两个词的意思时,看看Doug Lea给的注释:
等待take的条件 private final Condition notEmpty; 等待put的条件 private final Condition notFull;
换言之,此处的notEmpty表示为:只有队列在notEmpty的条件下才能take, 如果队列为empty,那么当前take的线程则需要等待。类似的,当队列在notFull时,才能put, 如果队列为full, 那么当前put的线程则需要等待。
异步写是怎么玩的
业务线程并发调用同一个Logger写日志时,Log4j2内部把内容解析成LogEvent,然后投递到队列中,由异步的线程来负责消费。
相关代码: 投递到队列:
public void append(final LogEvent logEvent) { ... 获取不可变的副本对象 final Log4jLogEvent memento = Log4jLogEvent.createMemento(logEvent, includeLocation); if (!transfer(memento)) { // 投递到队列失败,降级策略 if (blocking) { // 新版本默认为true // 根据策略不同,可丢弃、可等待、可由当前线程执行 final EventRoute route = asyncQueueFullPolicy.getRoute(thread.getId(), memento.getLevel()); route.logMessage(this, memento); } else { // 交由error-appender处理 logToErrorAppenderIfNecessary(false, memento); } } }// 投递LogEvent到队列,新版本可指定队列为LinkedTransferQueueprivate boolean transfer(final LogEvent memento) { return queue instanceof TransferQueue ? ((TransferQueue) queue).tryTransfer(memento) : queue.offer(memento);}
消费写LogEvent
消费while (!queue.isEmpty()) { try { final LogEvent event = queue.take(); if (event instanceof Log4jLogEvent) { final Log4jLogEvent logEvent = (Log4jLogEvent) event; logEvent.setEndOfBatch(queue.isEmpty()); // 在异步线程中调用Appender写日志 callAppenders(logEvent); } else { ... } } catch (final InterruptedException ex) { 不处理异常,继续写日志 } }
巧妙的异步写设计
实际环境中,很多业务线程在并发的写日志到队列中,并由一条异步消费者线程负责消费写。高并发的场景下,很容易造成生成的速度大于消费的速度。 为了不阻塞生成,投递LogEvent时选择的是队列的offer方法,如果成功入队,则执行马上返回给应用。如果队列满了,则默认降级为在同步写,这种设计能极大的提供性能 。而消费时选择的队列的take方法,如果生产的日志较少,则park消费者线程,让出CPU。 注意到offer方法在入队成功时,也会调用notEmpty.signal()方法,进而唤醒消费者,从而让它继续工作。
ByteBuffer与RandomAccessFile
项目中常常会用到RollingRandomAccessFile这种Appender,而在Log4j2中都是基于ByteBuffer来管理字节缓冲,并用于处理字节流与字符流的高效转换。最后采用RandomAccessFile来写Bytes到文件。Log4j2官方给出的性能测试报告提到相较于BufferedOutputStream,ByteBuffer + RandomAccessFile有20-200%的性能改善。
private ByteBuffer getByteBuffer() { ByteBuffer result = byteBufferThreadLocal.get(); if (result == null) { result = ByteBuffer.wrap(new byte[byteBufferSize]); byteBufferThreadLocal.set(result); } return result;}
在输出ByteBuffer时,根据Appender的不同选择不同的输出策略。基于RandomAccessFile的输出为: randomAccessFile.write(byteBuffer.toArray(), 0, byteBuffer.limit());
Garbage-free(避免创建多余对象)
Log4j2提倡创建最少的对象做更多事,尽量避免创建多余的对象。在内部有很多细节代码,这里分析2个例子。 1. API设计上避免创建变长数组 用void info(String message, Object p0, Object p1, Object p2, Object p3)这类参数明确的api替换带变长数组的api.
提供工具来避免基础类型参数的自动装箱. 自动装箱会创建大量的对象,Log4j2提供Unbox工具类来转换为StringBuilder.
异步Logger
事实上log4j2的异步logger才是性能改善最卓越的部分。异步Logger内部采用 Disruptor来实现,它是一个用于代替队列的无锁化线程间的通信的工具库,可以明显的提高吞吐量并降低延迟。 自己后续会对Disruptor做一个学习与分析。感谢关注。
性能
Log4j2的性能改善是明显的,官方文档:http://logging.apache.org/log4j/2.x/performance.html 提供了大量的场景对比结果,有兴趣的可以去了解一下。
写在最后
The log4j1.x amost has became End of Life。So upgrade it and learn about it.
哦,对了,升级的时候可以用SLF4J来做统一的外观,然后引入log4j-api,log4j-core, slf4j-api,还有slf4j到log4j2的桥接器log4j-slf4j-impl就行。但是需要注意的是: 咱们内部中间件大多默认依赖了slf4j-log4j12,因此需要把这类依赖全部排出, 可以用如下的tip:
org.slf4jslf4j-log4j12999-not-exist
Good luck. Tks.
标签:
相关推荐:
最新新闻:
- 当前简讯:计算机语言元素周期表——3D版化学元素列表
- 如何查询每个岗位的中位数位置?如何分析中位数?_环球热讯
- 决策树算法是什么?决策树的定义与核心思想 视焦点讯
- 【环球热闻】Kitten-少儿编程的首选工具 Kitten-少儿编程详情介绍
- 今日报丨AI论文中的novelty如何评价?详情介绍
- 【WOTD】remittance 释义/词源/示例 词源词根演化
- 为什么用Thread.Sleep函数把线程挂起时间?关于Thread.sleep的两个问题_世界时讯
- Statement、PreparedStatement、CallableStatement有什么区别?三者区别的详情介绍
- iPhone 14Plus遇冷:不降价就买安卓!
- 天天新消息丨如何搭建VIE架构?VIE架构如何实现海外上市?
- struts的框架介绍 Struts2框架的大致处理流程-世界新要闻
- 马斯克:10万亿美元“改造地球” 专家说不靠谱
- 环球观天下!PMA的应用是什么?基于PMA-qPCR的生物学检测方法
- 基于jsp+servlet+pojo+mysql的贴吧系统 java项目源码介绍_全球观天下
- NTC热敏电阻的采集方法有哪些?NTC热敏电阻温度采集方法介绍
- 什么是扁平化设计?扁平化时代之后会怎样?
- Log4j2源码解析:同步写、异步写原理、中间技术思考-天天看热讯
- 电介质和导体的区别是什么?电介质和导体的区别介绍
- 中兴通讯智慧家庭发布路由器新品小方糖 助推家庭网络千兆带宽普及 世界时讯
- Applewatch瑟瑟发抖!卡西欧推出G-SQUAD GBD-H2000智能手表 当前讯息
- iPhone中国换购价普降 iPhone 13 Pro折换价低了400元
- 每日快讯!大学老师撞脸《狂飙》高启盛 这压迫感谁不听课?
- 观天下!卡普空3月10日播出直播节目 将发布怪物猎人生化危机4等游戏新情报
- 《蜘蛛侠:平行宇宙2》新剧照曝光 蜘蛛侠2099亮相 格温紧随其后|环球新视野
- 《生化危机4:重制版》新演示和截图 里昂拯救黑丝碍事梨|当前滚动
- Mobileye荣获两大行业研究机构“自动驾驶领导者”评级-天天速看料
- 今日热讯:《堡垒之夜》将推出《生化危机》里昂、克莱尔联动皮肤
- iPhone 14 Pro同款“灵动岛”?realme C55手机官宣将于3月7日发布
- ALIENWARE外星人发布新款外设,扩展其不断增长的生态系统_前沿资讯
- 英特尔持续深耕数据中心渠道生态,聚合力赢未来 当前视点
- 屏下Face ID!iPhone 16 Pro大升级 报资讯
- 世界消息!《生化危机4》重制vs原版刀战对比:玩家实操机会更多!
- 中国载人航天总师:航天员见到外星人算星际合作|世界焦点
- 卖不动了!iPhone销量1月下跌11% 世界快播报
- 全球即时看!戴尔科技集团公布2023财年第四财季及全年财报
- 《黎明杀机》真人电影制作中 温子仁加盟|环球快看点
- 观天下!光荣版怪猎《狂野之心》分享赞誉宣传片
- 消息称动视收购案或将得到欧盟监管机构批准
- 《地狱之刃2》开发团队前后两次到冰岛实地取景
- 猫爬架也不安全 你做好这些准备了吗?
- 世界消息!抄底价!512GB M.2固态184元到手
- 女神节特惠!罗技爆款鼠标限时特惠229元
- 【当前独家】OpenAI允许App内嵌调用ChatGPT
- 人工智能加持?曝微软正在准备推出Windows 12|全球热闻
- 开放世界冒险《Tchia》PC配置公布 3月21日登陆Epic-当前快播
- 《FF16》宣发解禁!日本游戏店内摆满PS5实体盘盒
- 【独家焦点】《生化危机4:重制版》7分钟演示 刀战Jack Krauser
- 《卧龙:苍天陨落》M站评分解禁 均分81:微资讯
- 柯洁丢掉中国围棋等级分榜首 曾霸榜7年多|环球动态
- 最懂三国的游戏公司,并不在中国:当前快播
- 俞敏洪称不太喜欢《狂飙》:民营企业家没一个好人
- 焦点快看:《卧龙》联动《永劫无间》 宁红夜、季沧海参战新DLC
- 卧龙制作人:联动永劫无间!期待吸引中国动作游戏玩家
- 歹徒劫走汽车和小孩 大众称定位车辆要先续费被谴责|环球今亮点
- 特斯拉最新宏图第三章:新工厂、新能源计划、新机器人 快资讯
- Garmin佳明新一代Forerunner 265和Forerunner 965 GPS运动智能腕表闪亮登场
- 悦刻电子烟被罚20万:其广告涉嫌违反广告法!
- 69岁成龙再跳摩天轮!《龙马精神》片场花絮曝光:环球信息
- 《卧龙:苍天陨落》IGN 8分 战斗最好的魂类游戏
- 新人结婚朋友随100张刮刮乐 新娘:1000元开出500多
- 《黑豹2》《蚁人3》翻车:超级英雄电影为什么在中国遇冷?:天天快报
- 每日热闻!B站公布四季度财报:日活9千万 净亏损14.95亿元
- 环球报道:一加首款折叠屏手机官宣:将于今年晚些时候面世
- 购映众显卡送《穿越火线》限定好礼活动再次延长
- 2023款联想小新14轻薄本,13代i5处理器4199元
- 新学期新目标 三星GalaxyWatch5系列助力领跑开学季:实时焦点
- 固态硬盘价格新低,1TB只要249 元_当前时讯
- 热点在线丨《三体》电视剧豆瓣涨至8.6分 于和伟:像涨工资一样
- 3月15日7款游戏离开XGP 《漫威银护》《传说之下》等
- 今日视点:《最终幻想14》6.35版确定3月7日上线 新地下城任务等更新
- 儿子篮球班倒闭家长花1000万买下 网友:这就是钞能力:当前视点
- 叙事RPG《奇唤士》中文实机演示发布 发售日期待定|环球热门
- 广州2023大中专学生医保待遇标准一览(门诊+住院)
- 硬核配置强悍性能,三星玄龙骑士电竞显示器细节打造超感体验_全球看点
- 天天实时:特斯拉人形机器人自己造自己,马斯克承认其数量将超人类
- 是德科技收购Cliosoft,进一步壮大EDA软件产品线 每日资讯
- 3.8女王节送礼指南|汉印小粉饼标签机让生活更精彩
- 全球速讯:日立水墅适VF全变频新品:引领墅适大宅智慧风向
- 乔尔大危机?《最后生还者》第二季有望年内开拍:当前消息
- 《死侍》出租司机加盟《蜘蛛侠:平行宇宙2》 饰演印度蜘蛛侠
- 首次个人演唱会高桥李依给出小提醒:宅男记得洗澡!_当前关注
- 人大代表新建议 应该尽快出台《反网络暴力法》
- 天天微动态丨特斯拉确认将于墨西哥建立下一个超级工厂
- 假面骑士系列登陆B站 《假面骑士极狐》3月5日首播|全球观察
- 特斯拉人形机器人自己造自己 能自我繁衍了?_环球观速讯