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虚拟线程大规模使用会不会缓解日志打印阻塞工作线程的问题,毕竟调度更优,不过如果线程池满载,虚拟线程也是无能为力。还是需要在丢日志和存储消费日志的能力作取舍。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Smart Link 和 Monitor Link应用

定义 Smart Link常用于双上行链路组网&#xff0c;提高接入的可靠性。 Monitor Link通过监视上行接口&#xff0c;使下行接口同步上行接口状态&#xff0c;起到传递故障信息的作用。 Smart Link&#xff0c;又叫做备份链路。一个Smart Link由两个接口组成&#xff0c;其中一个…

2016年408计网

这一年&#xff0c;计算机网络部分的全部考题都围绕该网络拓扑图进行。 第33题 在 OSI 参考模型中, R1、Switch、Hub 实现的最高功能层分别是() A. 2、2、1 B. 2、2、2 C. 3、2、1 D. 3、2、2 本题考察路由器、以太网交换机、集线器各自实现的最高功能层是什么题目给定R1是…

链表OJ题【环形链表】(3)

目录 环形问题的思考 ❓Q1 ❓Q2 &#x1f642;Q2 ❓Q3 ❓Q4 8.环形链表 9.环形链表Ⅱ 今天接着链表的经典问题环形问题。大家一定要自己动手多写写。&#x1f642; 快慢指针&#xff08;保持相对距离/保持相对速度&#xff09;野指针考虑为NULL的情况带环链表&#x…

Java14新增特性

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

No180.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

【图像处理:OpenCV-Python基础操作】

【图像处理&#xff1a;OpenCV-Python基础操作】 1 读取图像2 显示图像3 保存图像4 图像二值化、灰度图、彩色图&#xff0c;像素替换5 通道处理&#xff08;通道拆分、合并&#xff09;6 调整尺寸大小7 提取感兴趣区域、掩膜8 乘法、逻辑运算9 HSV色彩空间&#xff0c;获取特定…

【算法每日一练]-单调队列,滑动窗口(保姆级教程 篇1) #滑动窗口 #求m区间的最小值 #理想的正方形 #切蛋糕

今天讲单调队列 目录 题目&#xff1a;滑动窗口 思路&#xff1a; 题目&#xff1a;求m区间的最小值​ 思路&#xff1a; 题目&#xff1a;理想的正方形 思路&#xff1a; 题目&#xff1a;切蛋糕 思路&#xff1a; 一共两种类型&#xff1a;一种是区间中的最值&…

游戏制作:猜数字(1~100),不会也可以先试着玩玩

目录 1 2代码如下&#xff1a;可以试着先玩玩 3运行结果&#xff1a;嘿嘿嘿 4程序分析&#xff1a;想学的看 5总结&#xff1a; 1 猜数范围为1~100&#xff0c;猜大输出猜大了&#xff0c;猜小输出猜小了&#xff0c;游戏可以无限玩。 首先先做一个简单的菜单界面&#xf…

RK3588平台 WIFI的基本概念

一.安卓WIFI框架 Android WIFI系统引入了wpa_supplicant&#xff0c;它的整个WIFI系统以wpa_supplicant为核心来定义上层接口和下层驱动接口。Android WIFI主要分为六大层&#xff0c;分别是WiFi Settings层&#xff0c;Wifi Framework层&#xff0c;Wifi JNI 层&#xff0c; W…

WorkPlus Meet:局域网内部使用的高效视频会议系统

随着全球化和远程办公的趋势&#xff0c;视频会议已成为现代企业和机构不可或缺的沟通工具。而现在&#xff0c;大多数政企单位或者涉密强的企业&#xff0c;都会使用局域网部署的音视频会议系统&#xff0c;提供更高的安全性和隐私保护。因为音视频会议中可能涉及到公司机密和…

Torch Hub 系列#2:VGG 和 ResNet

一、说明 在上一篇教程中,我们了解了 Torch Hub 背后的本质及其概念。然后,我们使用 Torch Hub 的复杂性发布了我们的模型,并通过相同的方式访问它。但是,当我们的工作要求我们利用 Torch Hub 上提供的众多全能模型之一时,会发生什么? 在本教程中,我们将学习如何利用称为…

自动泊车轨迹规划学习

1.基于6次多项式的自动泊车轨迹算法研究 针对常见的自动泊车系统无法躲避障碍物&#xff0c;以及轨迹的曲率不连续等问题进行了泊车轨迹算法的研究以及跟踪算法的设计。 针对低速自动泊车场景进行分析&#xff0c;建立符合对应场景下的车辆运动学模型以及能够泊车的最小车位大…

华为dns mapping配置案例

解决内网PC用公网的dns用域名方法访问公司内网的web服务器&#xff1a; 原理是用DNS mapping方式解决 配置过程&#xff1a;域名——出口公网IP地址——公网端口——协议类型 公司内网client用户填公网dns&#xff0c; 公网上的dns上面已注册有公司对外映射的web服务器的公网…

山西电力市场日前价格预测【2023-11-13】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-11-13&#xff09;山西电力市场全天平均日前电价为428.16元/MWh。其中&#xff0c;最高日前电价为751.89元/MWh&#xff0c;预计出现在18: 30。最低日前电价为289.03元/MWh&#xff0c;预计…

【MySQL系列】 第一章 · MySQL概述

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

王道 | 数据结构第一章

目录结构 章节总览 1.0 开篇_数据结构在学什么 1.1_1 数据结构的基本概念 1.1_2 数据结构的三要素 1.2_1 算法的基本概念 1.2_2 算法的时间复杂度 1.2_3 算法的空间复杂度 章节总览 1.0 开篇_数据结构在学什么 1.1_1 数据结构的基本概念 数据&#xff1a; 数据是信息的载…

数据库加密的常用方法 安当加密

数据库加密的方法主要有以下几种&#xff1a; 前置代理及加密网关技术&#xff1a;在数据库之前增加一道安全代理服务&#xff0c;对数据库访问的用户都必须经过该安全代理服务&#xff0c;在此服务中实现如数据加解密、存取控制等安全策略。加密数据存储在安全代理服务中。但…

OpenCV:图像旋转与缩放

人工智能的学习之路非常漫长&#xff0c;不少人因为学习路线不对或者学习内容不够专业而举步难行。不过别担心&#xff0c;我为大家整理了一份600多G的学习资源&#xff0c;基本上涵盖了人工智能学习的所有内容。点击下方链接,0元进群领取学习资源,让你的学习之路更加顺畅!记得…

人工智能基础——图像认知与OpenCV

人工智能的学习之路非常漫长&#xff0c;不少人因为学习路线不对或者学习内容不够专业而举步难行。不过别担心&#xff0c;我为大家整理了一份600多G的学习资源&#xff0c;基本上涵盖了人工智能学习的所有内容。点击下方链接,0元进群领取学习资源,让你的学习之路更加顺畅!记得…

Adversarial Training Methods for Deep Learning: A Systematic Review

Adversarial Training Methods for Deep Learning: A Systematic Review----《面向深度学习的对抗训练方法:系统回顾》 摘要 通过快速梯度符号法(FGSM)、投影梯度下降法(PGD)和其他攻击算法&#xff0c;深度神经网络暴露在对抗攻击的风险下。对抗性训练是用来防御对抗性攻击威…