当前位置: 首页 > news >正文

使用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异步输出的原理是通过阻塞队列多线程实现的。


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

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

相关文章:

使用logback异步打印日志

文章目录 一、介绍二、运行环境三、演示项目1. 接口2. 日志配置文件3. 效果演示4. 异步输出验证 四、异步输出原理五、其他参数配置六、源码分析1. 同步输出2. 异步输出 七、总结 一、介绍 对于每一个开发人员来说&#xff0c;在业务代码中添加日志是至关重要的&#xff0c;尤…...

ArcGIS Pro暨基础入门、制图、空间分析、影像分析、三维建模、空间统计分析与建模、python融合、案例应用

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…...

Rabbitmq的消息确认

配置文件 spring:rabbitmq:publisher-confirm-type: correlated #开启确认回调publisher-returns: true #开启返回回调listener:simple:acknowledge-mode: manual #设置手动接受消息消息从生产者到交换机 无论消息是否到交换机ConfirmCallback都会触发。 Resourceprivate Rabb…...

在飞机设计中的仿真技术

仿真技术在飞机设计中发挥着越来越重要的作用&#xff0c;本文阐述了国内外在飞机设计中广泛使用的结构强度计算&#xff0c;多体动力学仿真、多学科多目标结构优化、内外流场分析、非线性有限元分析、疲劳强度分析、电磁仿真分析&#xff0c;机电液联合仿真分析等&#xff0c;…...

(2023Arxiv)Meta-Transformer: A Unified Framework for Multimodal Learning

论文链接&#xff1a;https://arxiv.org/abs/2307.10802 代码链接&#xff1a;https://github.com/invictus717/MetaTransformer 项目主页&#xff1a;https://kxgong.github.io/meta_transformer/ 【注】&#xff1a;根据实验结果来看&#xff0c;每次输入一种数据源进行处…...

解决Python读取图片路径存在转义字符

普遍解决路径中存在转义字符的问题的方法 普遍解决转义字符的问题&#xff0c;无非是以下这三种。 一、在路径前添加r 直接在路径前面加r&#xff0c;这种方法能够使字符保持原始的意思。 比如下面这种&#xff1a; pathr"D:\MindSpore\Dearui\source\ces\0AI.png&qu…...

Windows 安装 pandoc 将 jupyter 导出 pdf 文件

Windows 安装 pandoc 将 jupyter 导出 pdf 文件 1. 下载 pandoc 安装文件2. 安装 pandoc3. 安装 nbconvert4. 使用 pandoc 1. 下载 pandoc 安装文件 访问 https://github.com/jgm/pandoc/releases&#xff0c;下载最新版安装文件&#xff0c;例如&#xff0c;3.1.6.1 版&#…...

混合云环境实现K8S可观测的6大策略

2023年&#xff0c;原生云应用及平台发展迅猛。大量企业都在努力发挥其应用程序的最大潜力&#xff0c;以确保极致的用户体验并推动业务增长。 混合云环境的兴起和容器化技术(如Kubernetes)的采用彻底改变了现代应用程序的开发、部署和扩展方式。 在这个数字舞台上&#xff0c;…...

音视频 FFmpeg命令行搭建

文章目录 一、配置二、测试 一、配置 以FFmpeg4.2.1 win32为例 解压ffmpeg-4.2.1-win32-shared.zip 拷⻉可执⾏⽂件到C:\Windows拷⻉动态链接库到C:\Windows\SysWOW64 注&#xff1a;WoW64 (Windows On Windows64)是⼀个Windows操作系统的⼦系统&#xff0c;被设计⽤来处理许…...

​ORACLE wallet实现无需输入用户名与密码登陆数据库 注意修改目录权限

wallet权限 linux 777 windows 需要修改.lck文件的owner 在ORACLE 10G前&#xff0c;我们在SHELL或JDBC中连接数据库时&#xff0c;都需要输入用户名与密码&#xff0c;并且都是明文。从1OGR2开始&#xff0c;ORACLE提供wallet这个工具&#xff0c;可以实现无需输入用户名与密…...

linux - 用户权限

认知root用户 无论是Windows、Macos、Linux均采用多用户的管理模式进行权限管理 在Linux系统中,拥有最大权限的账户名为: root(超级管理员) root用户拥有最大的系统操作权限&#xff0c;而普通用户在许多地方的权限是受限的。普通用户的权限&#xff0c;一般在其HOME目录内是不…...

计蒜客T1115——字符串判等

水题不解释&#xff0c;考研复习压力偶尔写一道换换心情还不错~ 这里有一个比较有趣的知识点&#xff0c;对于同时输入多个字符串时还要允许空格的输入&#xff0c;那么普通的cin函数就不能满足要求了&#xff0c;这里采用getline函数解决&#xff0c;如下&#xff1a; string …...

Android Framework工具——EA画图

EA 是一个著名的企业架构(Enterprise Architecture)工具,用于绘制和管理企业的架构图和过程模型。该工具提供了多种功能,包括建立业务流程图、数据流图、组织结构图、应用架构图等。EA工具可帮助企业进行战略规划、业务流程改进和系统开发等活动。 一、时序图 时序图(Seq…...

使用MyEclipse如何部署Descriptor (XML)编辑器?

Descriptor (XML) Editor编辑器包含了高级的XML编辑功能&#xff0c;在本文中您将了解到这些编辑功能、Web XML编辑等&#xff0c;此功能包含在MyEclipse中可用。 MyEclipse v2023.1.2离线版下载 1. Web XML 编辑器 MyEclipse Web XML编辑器包括高级XML编辑功能&#xff0c;…...

Codeforces Round 889 (Div. 2)C题题解

文章目录 [Dual (Hard Version)](https://codeforces.com/contest/1855/problem/C2)问题建模问题分析1.按元素值分类讨论&#xff0c;正负不同时存在时2.若正负同时存在时代码 Dual (Hard Version) 问题建模 给定n个数&#xff0c;n不超过20&#xff0c;且每个数ai&#xff0c…...

无涯教程-Perl - Subroutines(子例程)

定义子程序 Perl编程语言中 Subroutine子程序定义的一般形式如下: sub subroutine_name {body of the subroutine } 调用该Perl Subroutine的典型方式如下- subroutine_name( list of arguments ); 在Perl 5.0之前的版本中&#xff0c;调用 Subroutine的语法略有不同&…...

Rpc异步日志模块

Rpc异步日志模块作用 在一个大型分布式系统中&#xff0c;任何部署的分布式节点都可能发生崩溃&#xff0c;试想如果用普通的办法&#xff0c;即先排查哪个节点down掉了&#xff0c;找到down掉的节点后采取调试工具gdb调试该节点&#xff0c;进而排查宕机的原因。这中排查方法…...

python-pip

pip 路径 python 下载后自带pip ,在scripts 下&#xff0c;如 D:\install\python\Scripts numpy pip3 install numpy scipy matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simplepandas D:\install\python\Scripts>pip3 install pandas -i https://pypi.tuna.tsingh…...

无涯教程-Perl - getppid函数

描述 该函数返回父进程的进程ID。 语法 以下是此函数的简单语法- getppid返回值 该函数返回父进程的进程ID。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl$ppidgetppid();print "Parent Process ID $ppid\n";执行上述代码后,将产生以下输出- Paren…...

AUTOSAR规范与ECU软件开发(基础篇)1.2 汽车电子控制系统的基本构成

目录 前言 1、 传感器 2、 电子控制单元(ECU) 3、 执行器 前言 汽车电子控制系统主要由传感器(Sensor) 、 电子控制单元(Electronic Control Unit, ECU) 和执行器(Actuator) 组成(图1.1) ,对被控对象(Controlled Object)...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...