Logback 日志打印导致程序崩溃的实战分析
在软件开发和运维中,日志记录是必不可少的一环,帮我们追踪程序的行为,定位问题所在。然而,有时日志本身却可能成为问题的根源。本文将通过一个真实的案例来探讨 Logback 日志系统中的一个常见问题,当并发量大,日志打印突增时,导致程序崩溃。
一、Logback集成
下面来看下在 SpringBoot 中是如何集成 Logback 的,这虽然是个 demo,但在真实使用中也是这样的,无非是扩展一些功能而已,后面讲到 Logback 是程序崩溃也是基于这样的实现。
首先是添加依赖,关于 SpringBoot 相关的依赖就省略了,这里重点关注 Logback 的依赖,注意使用的版本,不同的版本有不同的表现。
<dependency><groupId>net.logstash.logback</groupId><artifactId>logstash-logback-encoder</artifactId><version>6.3</version>
</dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.1.8</version>
</dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.8</version>
</dependency>
Logback 配置文件添加,添加 logback-spring.xml 文件。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 日志输出内容,根据实际情况进行调整 --><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 日志文件名及路径,根据实际情况修改 --><file>./logs/app.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>./logs/app.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern></encoder></appender><root level="info"><appender-ref ref="STDOUT" /><appender-ref ref="FILE" /></root>
</configuration>
然后项目中在需要打印日志的地方,直接使用 log.info 或 log.error 等方式打印日志就好了,是不是很简单。
二、程序崩溃案例
程序在运行过程中,会收到很多服务请求超时的预警,然后过一会就会收到服务重启的预警。而且服务重启是无规律可循的,不是固定在某一个服务的某些 pod 上,而是所有的服务都有重启的现象,然后马上查看了相关监控,发现了一下问题,以下是相关监控内容
open files数量突增
直接内存的使用会突增
CPU 使用率突增
线程数量也会突增
三、原因分析
通过监控发现,在程序重启过程中,CPU、HTTP线程、直接内存使用、Open Files 这些指标都会突增。
首先看 HTTP 线程突增,感觉像是程序哪里阻塞住了,然后导致 HTTP 的堆积,最终导致健康检查的请求进不来,最终导致了服务的重启,而服务重启后一切又都回归了正常,下次在出现这种情况,可能又出现在了别的 Pod 上。到底是哪里出了问题呢?
因为是 HTTP 线程突增,所以第一想到的是程序内部有阻塞点,然后再请求三方接口、数据库查询等依赖的地方都加上了耗时统计,然后再出现重启时在看一下到底是哪里阻塞住了,奇怪的是这些地方都很正常,响应时间也没什么变化。
没有办法只能生成重启时的线程堆栈情况了,联系运维,编写了相应的脚本,当服务发生重启时生成相应的线程堆栈,具体是使用 jstack 命令。通过线程堆栈信息最终才发现了问题所在。以下是详细的线程堆栈分析。
阻塞的线程统计情况如下:
尽然有857个 HTTP 线程被阻塞住了,具体在看下阻塞在哪里了呢。
通过线程堆栈信息可以清楚的看到,是在打印日志时发生了阻塞,大量的 HTTP 线程被阻塞住,无法接受新的请求,最终导致健康检查失败而发生了重启,重启后这些又回归了正常。
为什么会发生阻塞呢?
我们使用的 Logback 版本是1.1.8,根据堆栈信息定位到具体的阻塞点,源码如下:
写日志时采用的是同步写,同步写这里是有加锁操作的,目的是为了防止一个线程将写日志的流关闭导致其他线程无法写入日志,而且采用得还是公平锁。这就是问题的根源所在,在高并发场景下,大量的日志写入都要到这里获取锁才能进行日志写入,这就造成了阻塞,将 HTTP 线程耗尽。
公平锁就是多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能获取到锁。这样虽然可以方式线程饥饿,但是吞吐量会下降很多,队列里除了第一个线程,其他的线程都会被阻塞。
四、解决方案
为了解决上述问题,可以采用如下策略进行修改。
首先,优化日志输出策略,平时在我们的系统重只输出必要的 info 日志和 error 日志,其他的 debug 级别日志和 warn 级别的日志就不用输出了。接口只打印入参,不要打印出参,一般情况下出参都会比较大,还有一些 SQL 语句的日志,这些都会加大日志的输出量。要想解决上述问题,第一条就是减少日志的输出,这也是这次问题解决的第一条方案:对项目中的日志进行梳理并且规范化,debug 级别和 warn 级别的日志统统下掉,简化系统日志输出量,将接口返回值和 SQL 语句的日志省略掉。当然了如果生产环境为了排查问题方便,可以开发一个开关功能,可以在排查问题时打印相应的日志。
其次,升级 Logback 日志版本,目前使用的版本是 1.1.8,将版本升级为 1.2.3,升级以后将公平锁修改为了非公平锁。
使用非公平锁后,在并发场景下可以减少线程切换次数,从而提高整体性能。同时也能减少线程的等待时间。
然后,可以将日志输出由同步改为异步输出,在同步输出模式下,日志记录操作会阻塞调用线程,这意味着每当调用logger.info()
等方法时,应用程序必须等待日志消息被写入磁盘或其它目的地后才能继续执行。
同步模式下日志记录逻辑简单,易于理解和调试,日志输出的顺序与记录顺序一致,这对于日志分析非常有利。但是容易出现性能瓶颈,上述的案例就是同步模式造成的。
在异步输出模式下,日志记录操作不会阻塞调用线程,而是将日志消息放入一个队列中,由一个或多个后台线程负责从队列中取出消息并写入目的地。
异步模式可以显著提高性能,减少对主线程的影响。由于消息可能被不同的时间被处理,所以输出的日志顺序可能与记录顺序不一致,如果队列满了,还可能造成日志消息的丢失。
在Logback中,可以使用ch.qos.logback.classic.AsyncAppender
来实现异步日志输出。
五、总结
日志记录是必不可少的一环,帮我们追踪程序的行为,定位问题所在。然而,有时日志本身却可能成为问题的根源。所以要根据实际情况选择合适的日志输出方式,避免造成程序阻塞而导致业务收到影响。
往期经典推荐:
从理论到实践:零拷贝技术的全面解读_零拷贝详解-CSDN博客
Sentinel与Nacos强强联合,构建微服务稳定性基石的重要实践_nacos sentinel-CSDN博客
从0开始理解云原生架构-CSDN博客
TiDB高手进阶:揭秘自增ID热点现象与高级调优技巧_tidb 分布式自增id-CSDN博客
深入浅出 Drools 规则引擎-CSDN博客
相关文章:

Logback 日志打印导致程序崩溃的实战分析
在软件开发和运维中,日志记录是必不可少的一环,帮我们追踪程序的行为,定位问题所在。然而,有时日志本身却可能成为问题的根源。本文将通过一个真实的案例来探讨 Logback 日志系统中的一个常见问题,当并发量大ÿ…...

新加坡 Numen Cyber 与香港光环云数据有限公司达成战略合作
新加坡本土网络安全公司 Numen Cyber 宣布与香港光环云数据有限公司(简称“光环云香港”)建立战略合作伙伴关系。此次合作将重点放在云服务器和云服务业务场景的安全领域。 Numen Cyber,作为一家致力于为客户提供专业网络安全服务和一体化安…...
Laravel魔术方法:框架的隐秘力量
Laravel魔术方法:框架的隐秘力量 引言 Laravel是一个充满魔力的PHP框架,它通过许多巧妙的设计让Web开发变得简洁而优雅。在Laravel中,魔术方法(Magic Methods)是这些魔力的体现之一。魔术方法是PHP预定义的、可以在类…...

系统复习Java日志体系
一,我们采用硬编码体验一下几个使用比较多的日志 分别导入几种日志的 jar 包 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSch…...
网络管理linux命令
在Linux系统中,有许多常用的网络命令用于检查网络配置、诊断网络问题以及管理网络连接。以下是一些常用的网络命令及其简要说明: ifconfig 显示或配置网络接口。 ifconfigip 用于显示和操作路由、设备、策略路由和隧道。 ip addr show ip link show ip …...

PowerDNS架构解析与安装部署指南
1、背景介绍 目前公司使用PowerDNS进行DNS管理,但由于采用的是单节点架构,存在不可用的风险。为提升系统的稳定性和可靠性,我们计划对现有架构进行重构。通过引入高可用性设计,我们将优化系统架构,使其能够在故障情况…...

Ubuntu 20.04.6 安装 Elasticsearch
1.准备 -- 系统更新 sudo apt update sudo apt upgrade -- 安装vim 文本编辑器 sudo apt install vim-- jdk 版本确认 java -versionjdk 安装可以参照:https://blog.csdn.net/CsethCRM/article/details/140768670 2.官方下载Elasticsearch 官方地址:h…...

Python for循环迭代原理(迭代器 Iterator)
在使用Python时,我们经常会使用for循环来访问容器对象(列表、字符、字典等)中的元素。其幕后实际是通过迭代协议来完成的,迭代是一种依次访问对象中元素的方式,for循环在对象上调用iter()函数生成一个迭代器࿰…...

通信原理-思科实验四:静态路由项配置实验
实验四 静态路由项配置实验 一:实验内容 二:实验目的 三、实验原理 四、实验步骤 选择三个2811型号的路由器 R1、R2、R3 路由器默认只有两个快速以太网接口,为路由器R1和R3增加快速以太网接口模块NM-1FE-TX,安装后检查路由器的接…...

ngzero使用外部的svg图标
1.将图标svg下下来,放到项目中,路径如下所示 之后 <span nz-icon [nzIconfont]“‘icon-zhibiao’”>使用 2.直接使用阿里的图标 先将你要用的图标放入购物车,再将购物车的图标添加到你主页的我的项目中 之后代码中在startupService…...

逆矩阵、秩
在数学的广阔天地中,线性代数扮演着至关重要的角色。它不仅是现代科学和工程学的基石,也是理解复杂数据结构的关键。本文将深入探讨线性代数中的几个核心概念:逆矩阵、秩、列空间和零空间,通过详细的解释和丰富的实例,…...

pc端小程序抓包修改数据相关记录
看了很多关于小程序抓包的 废话不多说直接演示 一、小程序抓包 1.所需要的工具 官网下载即可: https://www.charlesproxy.com/latest-release/download.do 我这里用的 Charles-proxy-4.6.6-win64 需要中文破解参考 https://www.jianshu.com/p/4d67dbbf2f6a 2、破…...

用Python打造精彩动画与视频.2.1 Python基础语法概述
2.1 Python基础语法概述 Python作为一门功能强大且易于学习的编程语言,其基础语法简单直观,非常适合初学者入门。这一节将带你了解Python的基本语法规则,为后续制作动画和视频打下坚实的基础。 1. 变量与数据类型 Python的变量不需要提前声…...

Golang高效合并(拼接)多个gzip压缩文件
有时我们可能会遇到需要把多个 gzip 文件合并成单个 gzip 文件的场景,最简单最容易的方式是把每个gzip文件都先解压,然后合并成一个文件后再次进行压缩,最终得到我们想要的结果,但这种先解压后压缩的方式显然效率不高,…...
MySQL数据库-基本概念
数据 描述事物的符号记录包括属组、文字、图形、图像、声音、档案记录等以“记录”形式按统一的格式进行存储 表 将不同的记录组织在一起用来存储具体数据 数据库 表的集合,是以一定的组织方式存储的相互有关的数据集合 数据库管理系统(DBMS&#…...

【无标题】web+http协议+nginx搭建+nginx反向代理(环境准备)
一.Web 为用户提供互联网上浏览信息的服务,web服务是动态的,可交互的。 1.安装httpd yum -y install httpd 2.启动 systemctl start httpd 3.关闭防火墙 systemctl stop firewalld [rootrs html]# echo "我手机号是" > …...

c-periphery RS485串口库文档serial.md(serial.h)(非阻塞读)(VMIN、VTIME)
c-peripheryhttps://github.com/vsergeev/c-periphery 文章目录 NAMESYNOPSISENUMERATIONS关于奇偶校验枚举类型 DESCRIPTIONserial_new()serial_open()关于流控制软件流控制(XON/XOFF)硬件流控制(RTS/CTS)选择流控制方法 serial_…...

Matlab arrayfun 与 bsxfun——提高编程效率的利器!
许多人知道 MATLAB 向量化编程,少用 for 循环 可以提高代码运行效率,但关于代码紧凑化编程, arrayfun 与 bsxfun 两个重要函数却鲜有人能够用好,今天针对这两个函数举例说明其威力。 Matlab arrayfun 概述 arrayfun 是 Matlab …...

【Unity编辑器拓展】GraphView自定义可视化节点
1、创建节点区域脚本 其中的new class UxmlFactory,可以让该元素显示在UI Builder中,我们就可以在Library-Project中看到我们新建的这两个UI元素,就可以拖入我们的UI窗口编辑了 public class NodeTreeViewer : GraphView {public new class…...

教程系列4 | 趋动云『社区项目』极速体验 LivePortrait 人脸表情“移花接木”大法
LivePortrait LivePortrait 由快手可灵大模型团队开源,只需 1 张原图就能生成动态视频。 LivePortrait 的核心优势在于其卓越的表情"迁移"技术,能够令静态图像中的人物瞬间焕发活力,无论是眨眼、微笑还是转头,皆栩栩如…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...