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

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 日志打印导致程序崩溃的实战分析

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

新加坡 Numen Cyber 与香港光环云数据有限公司达成战略合作

新加坡本土网络安全公司 Numen Cyber 宣布与香港光环云数据有限公司&#xff08;简称“光环云香港”&#xff09;建立战略合作伙伴关系。此次合作将重点放在云服务器和云服务业务场景的安全领域。 Numen Cyber&#xff0c;作为一家致力于为客户提供专业网络安全服务和一体化安…...

Laravel魔术方法:框架的隐秘力量

Laravel魔术方法&#xff1a;框架的隐秘力量 引言 Laravel是一个充满魔力的PHP框架&#xff0c;它通过许多巧妙的设计让Web开发变得简洁而优雅。在Laravel中&#xff0c;魔术方法&#xff08;Magic Methods&#xff09;是这些魔力的体现之一。魔术方法是PHP预定义的、可以在类…...

系统复习Java日志体系

一&#xff0c;我们采用硬编码体验一下几个使用比较多的日志 分别导入几种日志的 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系统中&#xff0c;有许多常用的网络命令用于检查网络配置、诊断网络问题以及管理网络连接。以下是一些常用的网络命令及其简要说明&#xff1a; ifconfig 显示或配置网络接口。 ifconfigip 用于显示和操作路由、设备、策略路由和隧道。 ip addr show ip link show ip …...

PowerDNS架构解析与安装部署指南

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

Ubuntu 20.04.6 安装 Elasticsearch

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

Python for循环迭代原理(迭代器 Iterator)

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

通信原理-思科实验四:静态路由项配置实验

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

ngzero使用外部的svg图标

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

逆矩阵、秩

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

pc端小程序抓包修改数据相关记录

看了很多关于小程序抓包的 废话不多说直接演示 一、小程序抓包 1.所需要的工具 官网下载即可&#xff1a; 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作为一门功能强大且易于学习的编程语言&#xff0c;其基础语法简单直观&#xff0c;非常适合初学者入门。这一节将带你了解Python的基本语法规则&#xff0c;为后续制作动画和视频打下坚实的基础。 1. 变量与数据类型 Python的变量不需要提前声…...

Golang高效合并(拼接)多个gzip压缩文件

有时我们可能会遇到需要把多个 gzip 文件合并成单个 gzip 文件的场景&#xff0c;最简单最容易的方式是把每个gzip文件都先解压&#xff0c;然后合并成一个文件后再次进行压缩&#xff0c;最终得到我们想要的结果&#xff0c;但这种先解压后压缩的方式显然效率不高&#xff0c;…...

MySQL数据库-基本概念

数据 描述事物的符号记录包括属组、文字、图形、图像、声音、档案记录等以“记录”形式按统一的格式进行存储 表 将不同的记录组织在一起用来存储具体数据 数据库 表的集合&#xff0c;是以一定的组织方式存储的相互有关的数据集合 数据库管理系统&#xff08;DBMS&#…...

【无标题】web+http协议+nginx搭建+nginx反向代理(环境准备)

一&#xff0e;Web 为用户提供互联网上浏览信息的服务&#xff0c;web服务是动态的&#xff0c;可交互的。 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()关于流控制软件流控制&#xff08;XON/XOFF&#xff09;硬件流控制&#xff08;RTS/CTS&#xff09;选择流控制方法 serial_…...

Matlab arrayfun 与 bsxfun——提高编程效率的利器!

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

【Unity编辑器拓展】GraphView自定义可视化节点

1、创建节点区域脚本 其中的new class UxmlFactory&#xff0c;可以让该元素显示在UI Builder中&#xff0c;我们就可以在Library-Project中看到我们新建的这两个UI元素&#xff0c;就可以拖入我们的UI窗口编辑了 public class NodeTreeViewer : GraphView {public new class…...

教程系列4 | 趋动云『社区项目』极速体验 LivePortrait 人脸表情“移花接木”大法

LivePortrait LivePortrait 由快手可灵大模型团队开源&#xff0c;只需 1 张原图就能生成动态视频。 LivePortrait 的核心优势在于其卓越的表情"迁移"技术&#xff0c;能够令静态图像中的人物瞬间焕发活力&#xff0c;无论是眨眼、微笑还是转头&#xff0c;皆栩栩如…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...