使用logback异步打印日志

文章目录

  • 一、介绍
  • 二、运行环境
  • 三、演示项目
    • 1. 接口
    • 2. 日志配置文件
    • 3. 效果演示
    • 4. 异步输出验证
  • 四、异步输出原理
  • 五、其他参数配置
  • 六、源码分析
    • 1. 同步输出
    • 2. 异步输出
  • 七、总结

一、介绍

对于每一个开发人员来说,在业务代码中添加日志是至关重要的,尤其是后端开发,如果不打印日志,在接口出现bug的时候将无法定位bug;有了日志,即使接口出现bug,也可以通过查询日志很快的定位到bug的位置。

使用springboot开发的朋友使用最多的日志框架想必都是logback了吧,毕竟它是springboot官方推荐的日志框架,与springboot天然整合。再结合第三方工具lombok,当我们需要打印日志时只需要通过log.info("第一条日志");log.error("日志信息:{}", arg1);简单的代码即可完成打印。


如果接口中出现大量日志,且日志是直接输出到日志文件通过网络发送到其他服务器呢?这种场景下,将会出现较为耗时的磁盘IO网络IO,从而导致接口响应时间变长。

有没有办法通过异步的形式将日志打印出来呢?答案是肯定的,logback早就想到这一点了。请往下看。

二、运行环境

  • springboot:2.4.3
  • logback:1.2.3。logback是springboot自带的默认日志框架

该版本的springboot中自带该版本的logback,因此不必单独引入logback的依赖。

三、演示项目

本项目仅用于logback日志的异步打印,因此较为简单,仅包含一个接口和logback日志配置文件。

1. 接口

@Slf4j
@RestController
@RequestMapping("/student")
public class StudentController {

    @GetMapping("/insert")
    public String insert() {
        log.info("第一条日志");
        log.info("第二条日志");
        log.info("第三条日志");
        log.info("第四条日志");
        log.info("第五条日志");
        return "成功";
    }
}

2. 日志配置文件

logback提供了大量的Appender将日志输出到指定的位置。如ConsoleAppender用于同步地将日志输出到控制台FileAppender用于同步地将日志输出到指定日志文件SocketAppender用于同步地将日志通过套接字发送到指定服务器。他们都有一个共同点:同步

那么如何修改日志配置文件将日志输出改为异步呢?logback提供了一个Appender的异步实现类:AsyncAppender。使用方法也非常简单,同样也是通过在配置文件中添加<appender>标签声明AsyncAppender,然后通过引用属性引用一个同步Appender即可,如下所示

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <!-- 日志输出格式 -->
    <property name="log.pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)"/>

    <!-- 同步输出到控制台 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="async-console" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 引用同步,将其包装为异步 -->
        <appender-ref ref="console" />
    </appender>

    <!-- 采用异步输出 -->
    <root level="INFO">
        <appender-ref ref="async-console" />
    </root>
    
</configuration>

3. 效果演示

我们使用postman调用接口,得到返回结果,如下图所示

在这里插入图片描述

查看控制台打印的日志,如下图所示

在这里插入图片描述

可这如何看出该日志打印和接口业务代码为异步的呢?

4. 异步输出验证

我们在AppenderAttachableImpl类的appendLoopOnAppenders()方法上添加断点,且要将该端点设置为多线程模式,如下图所示

在这里插入图片描述

此时再次调用接口,得到相同的结果,如下图所示

在这里插入图片描述

虽然接口已经正常响应了,但是我们发现代码却进入了断点,从该方法参数可以看出,在将要打印第一条日志时进入断点。如下图所示

在这里插入图片描述

且控制台上没有输出任何日志

在这里插入图片描述

当我们逐次放开断点使代码继续执行时,日志才逐行打印出来。因此我们可以断定接口业务代码与日志的打印为异步执行。

四、异步输出原理

为什么给同步Appender包装一层AsyncAppender就可以实现异步输出呢?

AsyncAppender内部维护了一个阻塞队列ArrayBlockingQueue,logback将我们打印日志的代码log.info("第一条日志")封装为一个事件Event,每当我们需要打印一行日志时,logback会将该事件放在阻塞队列中,然后再通过多线程的形式从该阻塞队列中获取一个事件执行。

阻塞队列可以保证日志的输出是有序的,多线程保证日志的输出是异步的。

五、其他参数配置

AsyncAppender能实现异步输出日志的关键因素是阻塞队列多线程,既然说到阻塞队列,那么必然少不了相关的参数。

参数类型说明
queueSizeint阻塞队列的最大容量,默认为256
discardingThresholdint默认地,当阻塞队列剩余容量达到20%时,logback将丢弃trace, debug和info三个级别的日志,而只输出warn和error级别的日志。如果需要全部输出,则将该值设置为0。
includeCallerDataboolean如果为true,logback对日志封装的事件中将包含更多调用者和父线程的信息,将降低性能。默认为false
maxFlushTimeint当服务停止时,logback将会等待一段时间,在这段时间内将正在输出的日志完成。
neverBlockboolean当阻塞队列已满时,如果为true,logback将丢失日志时间。默认为false

配置文件如下所示

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <!-- 日志输出格式 -->
    <property name="log.pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)"/>

    <!-- 同步输出到控制台 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="async-console" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 阻塞队列的参数配置 -->
        <queueSize>256</queueSize>
        <discardingThreshold>20</discardingThreshold>
        <includeCallerData>false</includeCallerData>
        <maxFlushTime>1000</maxFlushTime>
        <neverBlock>false</neverBlock>
        
        <!-- 引用同步,将其包装为异步 -->
        <appender-ref ref="console" />
    </appender>

    <!-- 采用异步输出 -->
    <root level="INFO">
        <appender-ref ref="async-console" />
    </root>
    
</configuration>

六、源码分析

在logback中,任何日志的输出去都是通过start()方法完成的,接下来我们以ConsoleAppender同步输出 和 AsyncAppender异步输出为例,从源码层面分析。

1. 同步输出

进入ConsoleAppender源码,查看其start()方法。

在这里插入图片描述

可以看到其中第一行代码中通过target.getStream()获取输出目标的流,而target属性的声明如下所示,从该声明中我们看到通过target.getStream()获取输出目标的流就是System.out.print()了。这下我们就明白ConsoleAppender是如何将日志输出到控制台了。

在这里插入图片描述

再进入setOutputStream()方法查看其具体实现,从中可以看到是通过lock.lock()lock.unlock()来保证其同步执行的,其中lock所是一个非公平的可重入同步锁,这是多线程中同步锁AQS体系的概念,这里不详细展开,我们只需要知道通过该锁实现同步即可。

在这里插入图片描述

2. 异步输出

进入AsyncAppender源码,查看其start()方法,该方法定义在其父类AsyncAppenderBase中。

在这里插入图片描述

在该方法中我们看到第102行代码使用queueSize参数声明一个指定容量的阻塞队列,默认的discardingThreshold参数为阻塞队列容量的1/5,即20%,然后就是对worker属性进行设置,然后调用其start()方法。worker实际上是Thread类的子类Worker对象,调用其start()方法就是通过一个新线程调用该对象的run()方法。

在这里插入图片描述

从该方法中我们发现,无论是直接调用take()方法获取阻塞队列中的元素,还是通过遍历阻塞队列获取其中的元素,都会调用appendLoopOnAppenders()方法,而该方法就是logback真正输出日志的方法,也是我们在验证其异步输出是打断点的地方。

七、总结

  • logback通过AyncAppender实现日志的异步输出。
  • AyncAppender异步输出的原理是通过阻塞队列多线程实现的。


纸上得来终觉浅,绝知此事要躬行。

————————我是万万岁,我们下期再见————————

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

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

相关文章

docker基本使用方法

docker使用 1. Docker 介绍 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。Docker 使您能够将应用程序与基础架构分开&#xff0c;从而可以快速交付软件。通过利用 …

Oracle 开发篇+Java通过DRCP访问Oracle数据库

标签&#xff1a;DRCP、Database Resident Connection Pooling、数据库驻留连接池释义&#xff1a;DRCP&#xff08;全称Database Resident Connection Pooling&#xff09;数据库驻留连接池&#xff08;Oracle自己的数据库连接池技术&#xff09; ★ Oracle开启并配置DRCP sq…

神码ai伪原创文章生成器软件【php源码】

大家好&#xff0c;本文将围绕python二级用哪个版本的软件展开说明&#xff0c;二级python 值不值得考是一个很多人都想弄明白的事情&#xff0c;想搞清楚python二级用什么软件需要先了解以下几个事情。 火车头采集ai伪原创插件截图&#xff1a; 问题一&#xff1a;安装python…

java+springboot+mysql智能社区管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的社区住户综合管理系统&#xff0c;系统包含超级管理员、管理员、住户角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;住户管理&#xff1b;房屋管理&#xff08;楼栋、房屋&#xff…

代码详解——Transformer

文章目录 整体架构Modules.pyScaledDotProductAttention SubLayers.pyMultiHeadAttentionPositionwiseFeedForward Layers.pyEncoderLayerDecoderLayer Models.pyget_pad_maskget_subsequent_maskPositionalEncodingEncoderDecoderTransformer 整体架构 源码地址&#xff08;py…

电商3D产品渲染简明教程

3D 渲染让动作电影看起来更酷&#xff0c;让建筑设计变得栩栩如生&#xff0c;现在还可以帮助营销人员推广他们的产品。 从最新的《阿凡达》电影到 Spotify 的上一次营销活动&#xff0c;3D 的应用让一切变得更加美好。 在营销领域&#xff0c;3D 产品渲染可帮助品牌创建产品的…

Docker+rancher部署SkyWalking8.5并应用在springboot服务中

1.Skywalking介绍 Skywalking是一个国产的开源框架&#xff0c;2015年有吴晟个人开源&#xff0c;2017年加入Apache孵化器&#xff0c;国人开源的产品&#xff0c;主要开发人员来自于华为&#xff0c;2019年4月17日Apache董事会批准SkyWalking成为顶级项目&#xff0c;支持Jav…

Kafka消息队列学习(一)

文章目录 概述核心概念生产者示例同步 / 异步发送消息生产者参数配置ack-确认机制retries - 重试次数compression_type - 消息压缩类型 分区机制分区策略 消费者消息有序性提交和偏移量偏移量提交方式手动提交 高可用设计 SpringBoot集成Kafka基本使用传递对象消息 概述 核心概…

2023企业微信0day漏洞复现以及处理意见

2023企业微信0day漏洞复现以及处理意见 一、 漏洞概述二、 影响版本三、 漏洞复现小龙POC检测脚本: 四、 整改意见 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#x…

Arduino+esp32学习笔记

学习目标&#xff1a; 使用Arduino配置好蓝牙或者wifi模块 学习使用python配置好蓝牙或者wifi模块 学习内容&#xff08;笔记&#xff09;&#xff1a; 一、 Arduino语法基础 Arduino语法是基于C的语法,C又是c基础上增加了面向对象思想等进阶语言。那就只记录没见过的。 单多…

Mysql in 查询的奇怪方向

Mysql in 查询的奇怪方向 关于表字段存储的数据为 num1,num2,num3时, 还要通过多个num1,num2入参针对该字段进行查询 建表语句 CREATE TABLE test (test_ids varchar(100) DEFAULT NULL COMMENT 保存ids 以逗号分隔 ) ENGINEInnoDB;数据项 查询语句 SELECT test_ids FROM t…

Android之版本号、版本别名、API等级对应关系(全)(一百六十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

数据结构:选择排序

简单选择排序 选择排序是一种简单直观的排序算法。首先在未排序序列中找到最大&#xff08;最小&#xff09;的元素&#xff0c;存放到排序学列的其实位置&#xff0c;然后在剩余的未排序的元素中寻找最小&#xff08;最大&#xff09;元素&#xff0c;存放在已排序序列的后面…

NLP文本分类

NLP文本分类 落地实战五大利器&#xff01;_kaiyuan_sjtu的博客-CSDN博客https://zhuanlan.zhihu.com/p/432619164 https://github.com/alibaba/EasyNLP/blob/master/README.cn.md

【5G 核心网】5G 多PDU会话锚点技术介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

Docker部署rabbitmq遇到的问题 Stats in management UI are disabled on this node

1. Stats in management UI are disabled on this node #进入rabbitmq容器 docker exec -it {rabbitmq容器名称或者id} /bin/bash#进入容器后&#xff0c;cd到以下路径 cd /etc/rabbitmq/conf.d/#修改 management_agent.disable_metrics_collector false echo management_age…

什么是gRPC?

1. GRPC是google开源的rpc框架 2. 核心是一个.proto的服务描述文件 3. 添加依赖的grpc相关的包&#xff0c;配置IDEA的grpc插件&#xff0c;就可以很方便的生成调用代码 4. 通过在IDEA的protobuf插件上分别执行以下两个服务&#xff0c;就可以生成需要的调用代码 1&#xff…

2023深圳杯A题完整代码模型

已更新深圳杯A题全部版本&#xff0c;文末获取&#xff01; 摘要 现代社会&#xff0c;随着生活方式的变化和工作压力的增大&#xff0c;慢性非传染性疾病日益成为威胁公众健康的主要问题。心脑血管疾病、糖尿病、恶性肿瘤及慢性阻塞性肺病等慢性病的发病率呈现出上升趋势。为…

通过将信号频谱与噪声频谱进行比较,自动检测适当的带通滤波器转折频率研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

普通人怎样拥抱AI时代?这几点最为重要!

一、拒绝还是接受&#xff1f; 当纽约公立学校严禁学生用ChatGPT写论文之后&#xff0c;沃顿商学院的教授Ethan Mollick却开始鼓励自己的学生用ChatGPT来写论文。 图源于网络 试想一下&#xff0c;当所有学生都可以用ChatGPT写论文&#xff0c;大家的分数会有明显差别吗?一定…