logback异步日志打印阻塞工作线程

前言

最新做项目,发现一些历史遗留问题,典型的是日志打印的配置问题,其实都是些简单问题,但是往往简单问题引起严重的事故,比如日志打印阻塞工作线程,以logback和log4j2为例。logback实际上是springboot的官方默认日志实现框架,承载SLF4J-API,所以基于java开发的云原生项目基本上就是logback打印日志,logback异步appender日志的打印架构

可以看到consoleAppender实际上也是异步(非同步)的范畴

准备demo

springboot的demo,这个可以用Spring官方的脚手架生成

其中启动的console日志就是logback打印的,虽然我们没有配置logback的xml。而现实情况是需要配置文件的,毕竟需要异步打印日志,日志切割,保存时间等都需要配置,关键还要日志格式。

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"><!--定义日志文件的存储地址 可以使用环境变量或者系统变量占位--><property name="LOGBACK_HOME" value="/opt/xxx" /><!--控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!-- 文件 滚动日志 --><appender name="fileLog"  class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 当前日志输出路径、文件名 --><file>${log.path}/app.log</file><!--日志输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset></encoder><!--历史日志归档策略--><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 历史日志: 归档文件名 --><fileNamePattern>${log.path}/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern><!--单个文件的最大大小--><maxFileSize>200MB</maxFileSize><!--日志文件保留天数--><maxHistory>10</maxHistory></rollingPolicy></appender><!-- 文件 异步日志(async) --><appender name="ASYNC_LOG" class="ch.qos.logback.classic.AsyncAppender" ><!-- 不丢失日志.默认的256/5,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold>0</discardingThreshold><!-- 更改默认的队列的长度,默认值为256 --><queueSize>1024</queueSize><neverBlock>true</neverBlock><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref="fileLog" /></appender><!--自定义日志级别 myibatis可以输出SQL--><logger name="com.apache.ibatis" level="TRACE"/><logger name="java.sql.Connection" level="DEBUG"/><logger name="java.sql.Statement" level="DEBUG"/><logger name="java.sql.PreparedStatement" level="DEBUG"/><!-- 日志输出级别 --><root level="INFO"><appender-ref ref="STDOUT" /><appender-ref ref="ASYNC_LOG"/></root>
</configuration>

笔者参考各个网站简单写了一个xml

原理

那么为什么日志打印会阻塞工作线程了,不是异步的嘛吗?异步是没错,但是异步解耦却是依赖队列,不同于分布式MQ,本地队列在某些配置时是阻塞的,所以异步日志实际上是半同步,有点像MySQL的复制原理,架构设计实际上很多地方非常相似。如果不想丢日志,可以提高消费队列日志线程的数量,增加CPU资源消耗。

参考logback官方文档:Chapter 4: Appenders

这个就是前言的代码分析,console也是属于异步范畴。

AsyncAppender配置如下 

这里关键有3个配置

  1. queueSize,队列的大小,默认256
  2. discardingThreshold,队列数量剩余数,达到或者小于,就丢弃TRACE DEBUG INFO日志
  3. neverBlock,从不阻塞,队列满就丢日志

 

最简单的架构图,写文件是异步线程,但是写queue是同步的

源码分析

 ch.qos.logback.classic.AsyncAppender,关键还是父类ch.qos.logback.core.AsyncAppenderBase

/*** In order to optimize performance this appender deems events of level TRACE,* DEBUG and INFO as discardable. See the* <a href="http://logback.qos.ch/manual/appenders.html#AsyncAppender">chapter* on appenders</a> in the manual for further information.*** @author Ceki G&uuml;lc&uuml;* @since 1.0.4*/
public class AsyncAppender extends AsyncAppenderBase<ILoggingEvent> {boolean includeCallerData = false;/*** Events of level TRACE, DEBUG and INFO are deemed to be discardable.* 定义丢弃日志的级别,文档写的就是这里实现的* * @param event* @return true if the event is of level TRACE, DEBUG or INFO false otherwise.*/protected boolean isDiscardable(ILoggingEvent event) {Level level = event.getLevel();return level.toInt() <= Level.INFO_INT;}protected void preprocess(ILoggingEvent eventObject) {eventObject.prepareForDeferredProcessing();if (includeCallerData)eventObject.getCallerData();}

这个类核心还是,按照级别丢日志的定义,比如queue能存储的大小少于1/5时,那些级别日志丢弃 ,再看父类启动的时候分析初始值,发现queue是

ArrayBlockingQueue

 

定义了队列discardingThreshold的值,注意:这个是队列数信号量,不是百分比,发现一些业务配置20,😄

public static final int DEFAULT_QUEUE_SIZE = 256;
int queueSize = DEFAULT_QUEUE_SIZE;

默认队列数256,建议配置大一点,根据内存分配情况,过大会OOM 

 在分析日志入队列的过程

 

分析 

ArrayBlockingQueue
    /*** Inserts the specified element at the tail of this queue if it is* possible to do so immediately without exceeding the queue's capacity,* returning {@code true} upon success and {@code false} if this queue* is full.  This method is generally preferable to method {@link #add},* which can fail to insert an element only by throwing an exception.** @throws NullPointerException if the specified element is null*/public boolean offer(E e) {Objects.requireNonNull(e);final ReentrantLock lock = this.lock;lock.lock();try {if (count == items.length)return false;else {enqueue(e);return true;}} finally {lock.unlock();}}

队列数满,直接丢弃,所以不阻塞,丢失日志。 

log4j2

log4j2实际上根据各方测试说,比logback性能强一些,但是也会出现同样的问题

官方文档:Log4j – Log4j 2 Appenders

apache开源的文档管理要好一些,写的很详细,而且有详细的说明和示例,不过设计原理都差不多 

总结

实际上这个问题是使用问题,非常简单,不过越是简单的使用,却可能出现致命问题,一般公司都会统一脚手架或者统一规范的方式来实现标准的日志配置文件,防止错误配置导致业务问题,不知道未来Java虚拟线程大规模使用会不会缓解日志打印阻塞工作线程的问题,毕竟调度更优,不过如果线程池满载,虚拟线程也是无能为力。还是需要在丢日志和存储消费日志的能力作取舍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/139530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】AppUI自动化—appium自动化开发环境部署、APP测试案例(17)上

文章目录 一.appium简介1.什么是appium2.appium 的工作原理3.APP类型4.APP页面布局 二,appium开发环境部署&#xff08;python环境&#xff09;1.下载安装环境1.1.下载安装所需环境1.2.Appium-desktop&#xff08; Appium-Server-GUI &#xff09;配置1.3.Appium-Inspector 配置…

计算机msvcp140.dll重新安装的四个解决方法,专门解决dll文件丢失问题的方法

在我多年的电脑使用经历中&#xff0c;曾经遇到过一个非常棘手的问题&#xff0c;那就是电脑提示找不到msvcp140.dll文件。这个问题让我苦恼了很久&#xff0c;但最终还是找到了解决方法。今天&#xff0c;我就来分享一下我解决这个问题的四种方法&#xff0c;希望对大家有所帮…

python爬虫怎么翻页

爬虫程序的代码实现如下&#xff1a; #include <iostream> #include <string> #include <curl/curl.h>int main() {CURL *curl;CURLcode res;std::string readBuffer;curl_global_init(CURL_GLOBAL_DEFAULT);curl curl_easy_init();if(curl) {curl_easy_se…

AI 绘画 | Stable Diffusion精确控制ControlNet扩展插件

ControlNet ControlNet是一个用于控制AI图像生成的插件&#xff0c;通过使用Conditional Generative Adversarial Networks&#xff08;条件生成对抗网络&#xff09;的技术来生成图像。它允许用户对生成的图像进行更精细的控制&#xff0c;从而在许多应用场景中非常有用&#…

每次重启完IDEA,application.properties文件里的中文变成?

出现这种情况&#xff0c;在IDEA打开Settings-->Editor-->File Encodings 然后&#xff0c;你需要将问号改为你需要的汉字。 重启IDEA&#xff0c;再次查看你的.properties文件就会发现再没有变成问号了

order by的注入与Insert ,update和delete注入

order by的注入 Insert &#xff0c;update和delete注入

大二第四周总结——用原生js封装一个分页器

用原生js封装一个分页器 起因&#xff1a;这次项目还是用原生的js来写的&#xff0c;我负责的是后台&#xff0c;分页是后台最常见的一个功能了&#xff0c;于是干脆封装一下,废话少说&#xff0c;直接上代码 这里是基本的样式 .pagination {display: flex;width: 600px;hei…

PCB知识补充

系列文章目录 文章目录 系列文章目录参考文献PCB知识互连线电阻过孔/铜箔电流能力铜箔载流能力过孔载流能力 热设计电磁兼容及部分要求 参考文献 [1]牛森,张敏娟,银子燕.高速PCB多板互联的电源完整性分析[J].单片机与嵌入式系统应用,2023,23(09). [2]陈之秀,刘洋,张涵舒等.高…

Django中简单的增删改查

用户列表展示 建立列表 views.py def userlist(request):return render(request,userlist.html) urls.py urlpatterns [path(admin/, admin.site.urls),path(userlist/, views.userlist), ]templates----userlist.html <!DOCTYPE html> <html lang"en">…

【Java】反射

1.什么是反射机制? Java 反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类中的所有属性和方法&#xff0c;对于任意一个对象&#xff0c;都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 的反射机制…

《软件工程与计算》期末考试真题范例及答案

今天分享一套针对《软件工程与计算》这本书的真题案例&#xff0c;有关《软件工程与计算》23章内容的重点知识整理&#xff0c;已经总结在了博客专栏中&#xff0c;有需要的自行阅读&#xff1a; 《软件工程与计算》啃书总结https://blog.csdn.net/jsl123x/category_12468792.…

进程状态和优先级

文章目录 进程状态Linux中具体的进程状态僵尸进程孤儿进程 进程优先级 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站。 进程状态 进程在操…

每天一点python——day66

#每天一点Python——66 #字符串的分隔 #如图&#xff1a; #方法①split()从左开始分隔&#xff0c;默认空格为分割字符&#xff0c;返回值是一个列表 shello world jisuanji#首先创建一个字符串 list1s.split() print(list1)#输出结果是&#xff1a;[hello, world, jisuanji]注…

Git的原理与使用(一)

目录 Git初始 Git安装 Git基本操作 创建git本地仓库 配置git 工作区,暂存区,版本库 添加文件,提交文件 查看.git文件 修改文件 版本回退 小结 Git初始 git是一个非常强大的版本控制工具.可以快速的将我们的文档和代码等进行版本管理. 下面这个实例看理解下为什么需…

Java 简单实现一个 UDP 回显服务器

文章目录 UDP 服务端UDP 客户端实现效果UDP 服务端(实现字典功能)总结 UDP 服务端 package network;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException;public class UdpEchoServer {private Da…

【开源】基于Vue.js的大学兼职教师管理系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、研究内容三、界面展示3.1 登录注册3.2 学生教师管理3.3 课程管理模块3.4 授课管理模块3.5 课程考勤模块3.6 课程评价模块3.7 课程成绩模块3.8 可视化图表 四、免责说明 一、摘要 1.1 项目介绍 大学兼职教师管理系统&#xff0…

Java13新增特性

前言 前面的文章&#xff0c;我们对Java9、Java10、Java11、Java12 的特性进行了介绍&#xff0c;对应的文章如下 Java9新增特性 Java10新增特性 Java11新增特性 Java12新增特性 今天我们来一起看一下Java13这个版本的一些重要信息 版本介绍 Java 13 是在 2019 年 9 月 17 日…

Technology Strategy Patterns 学习笔记9 - bringing it all together

1 Patterns Map 2 Creating the Strategy 2.1 Ansoff Growth Matrix 和owth-share Matrix 区别参见https://fourweekmba.com/bcg-matrix-vs-ansoff-matrix/ 3 Communicating

Docker进阶——再次认识docker的概念 Docker的结构 Docker镜像结构 镜像的构建方式

前言 在微服务大量应用的互联网时代&#xff0c;经常能看到docker的身影。作为docker的爱好者&#xff08;在服务器安装MySQL&#xff0c;Redis。。。我用的都是docker&#xff09;&#xff0c;我也会持续深入学习和认识docker。 本篇博客再次介绍docker的基本概念&#xff0…

FPGA UDP RGMII 千兆以太网(3)ODDR

1 xilinx原语 在 7 系列 FPGA 中实现 RGMII 接口需要借助 5 种原语,分别是:IDDR、ODDR、IDELAYE2、ODELAYE2(A7 中没有)、IDELAYCTRL。其中,IDDR和ODDR分别是输入和输出的双边沿寄存器,位于IOB中。IDELAYE2和ODELAYE2,分别用于控制 IO 口输入和输出延时。同时,IDELAYE2 …