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

磁盘满了对日志打印(Logback)的影响

背景

我们生产环境有一个服务半夜报警:磁盘剩余空间不足10%,请及时处理。排查后发现是新上线的一个功能,日志打太多导致的,解决方法有很多,就不赘述了。领导担心报警不及时、或者报警遗漏,担心磁盘满了对线上业务有负面影响,甚至不可用,令我研究一下该情况下对服务的影响。

正文

注:我们线上的应用使用Logback日志框架进行打印,所以log4j、log4j2或其它日志框架不在本次研究的范围。

实验研究

实验步骤

  1. 启动服务,观察各项指标正常(模拟正常工作的服务以及环境状态)
  2. 将磁盘写满
  3. 观察应用服务、宿主机各项指标,查看服务响应是否正常(包括功能以及性能)
  4. 将大文件删除(恢复)
  5. 重复第3步

实验过程

第1、3、5步重在观测,不赘述。

第2步的模拟磁盘写满,使用linux的fallocate命令。

将磁盘写满的方式如下有几种:

  1. 自己写文件,写满为止,速度较慢,有开发(写脚本)成本
  2. 使用dd命令,但是比较慢,取决于磁盘的速度
  3. 使用truncate命令,但是该命令操作的结果并不占用实际的磁盘空间
  4. 使用fallocate命令,fallocate -l {size} {fileName},如fallocate -l 20G text1

我选择了方式4,很快就写满了磁盘

验证方式:vim {xxx},进入文件编辑,写入任意内容,保存失败并提示:Can’t open file for writing(No space left on device)

实验结果

  1. 磁盘写满前、后,应用服务各项指标均正常(功能、性能)
  2. 磁盘写满后,服务器磁盘空间报警(无可用空间),删除文件后报警恢复
  3. 磁盘写满后,应用日志停止打印,删除文件后应用日志恢复打印

即,磁盘满了对于使用logback日志框架的应用,并不会造成影响。

实验结果令人诧异,按正常理解,磁盘满了之后再写入,会报No space left on device异常,进而影响到应用程序的功能。

原理分析

logback配置文件里的文件appender如下(RollingFileAppender同理):

<appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>${LOG_HOME}/${APP_NAME}.log</file><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder>
</appender>

把关注点放到appender即可,因为最终的日志输出是由appender控制的:它控制写在哪个文件里,格式是怎样的,滚动策略又是怎样的。

通过IDEA查看RollingFileAppender的继承关系如下下示:

在这里插入图片描述

将视线聚焦到ch.qos.logback.core.OutputStreamAppender#writeBytes,代码如下:

private void writeBytes(byte[] byteArray) throws IOException {if(byteArray == null || byteArray.length == 0)return;lock.lock();try {this.outputStream.write(byteArray);if (immediateFlush) {this.outputStream.flush();}} finally {lock.unlock();}
}

由上可知,写日志时会加锁,避免资源竞争,然后通过outputStream写出,接着马上刷盘(默认情况)。

这里的outputStream可了不得,是个ResilientFileOutputStream实例,顾名思议,【弹性的】FileOutputStream。它是个子类,核心逻辑在父类ResilientOutputStreamBase上,看看write方法:

public void write(byte b[], int off, int len) {if (isPresumedInError()) {if (!recoveryCoordinator.isTooSoon()) {attemptRecovery();}return; // return regardless of the success of the recovery attempt}try {os.write(b, off, len);postSuccessfulWrite();} catch (IOException e) {postIOFailure(e);}
}

这个write方法隐藏着【即使磁盘满了也不影响业务】的秘密!它的核心逻辑是:如果能正常写文件,就写;如果写入失败(出现了IO异常),就抓住异常并切换至失败状态,接下来的一段时间都不再继续写文件,直接返回。

而此处所有的IO异常,就包含了No space left on device,因此,此处便是磁盘满了之后继续日志写入,却不会抛出No space left on device异常进而影响业务的原因。

还有一个问题,待磁盘有剩余空间后,如何恢复日志写入?上面实验结果提到,磁盘写满后,应用日志停止打印,删除文件后应用日志恢复打印。因此必然会有一个策略,能够在磁盘空间恢复后,继续进行日志写入。

再仔细查看看write方法,方法首先判断当前状态是否为“失败”。若是,接着检查自上次失败至今的时间。如果已经经过了很长时间,就【尝试恢复】。

void attemptRecovery() {try {close();} catch (IOException e) {}addStatusIfCountNotOverLimit(new InfoStatus("Attempting to recover from IO failure on " + getDescription(), this));// subsequent writes must always be in append modetry {os = openNewOutputStream();presumedClean = true;} catch (IOException e) {addStatusIfCountNotOverLimit(new ErrorStatus("Failed to open " + getDescription(), this, e));}
}

尝试恢复的逻辑是:关闭旧文件流并重新打开文件流,将【失败】状态切换至【半恢复】状态,此处并未完全进入【正常】状态,而是立即返回,即是说当次的日志记录请求并不会写文件。

下次的日志写入,才会尝试将日志写入文件

  • 若日志成功写入文件,就将状态切换至【正常】状态。
  • 若日志写入文件失败,就将状态切换至【失败】状态。

此处的失败以及恢复逻辑非常像Hystrix断路器,即经历一个正常→失败→半通路状态,通过下次请求的结果来决定是恢复正常还是保持失败。

上面提到若经过很长时间,就尝试恢复,这里的【很长时间】其实是一个泛指,它有自己的时间计算逻辑,此处采用了类似于RocketMQ在消费失败时的重试策略,即采用指数退避逻辑来控制重试时间间隔。

每次【半恢复】状态下写文件失败时,会保持【失败】状态,且每一次的失败写入都会指数级延长【失败】状态保持的时间。

public boolean isTooSoon() {long now = getCurrentTime();if (now > next) {next = now + getBackoffCoefficient();return false;} else {return true;}
}
private long getBackoffCoefficient() {long currentCoeff = backOffCoefficient;if (backOffCoefficient < BACKOFF_COEFFICIENT_MAX) {backOffCoefficient *= BACKOFF_MULTIPLIER;}return currentCoeff;
}

backOffCoefficient初始值:20

BACKOFF_COEFFICIENT_MAX:327680

BACKOFF_MULTIPLIER:4

总结

使用Logback日志框架,当磁盘满了后不必惊慌,它不会对应用程序产生太负面影响。仔细考虑,做为一个日志框架本该如此,不能因为写不了日志就抛出异常进而影响应用本身,毕竟,日志终究是个辅助的旁路逻辑,没有它应用也应该work well。

底层知识的掌握有助于迅速理解上层应用。在阅读源码的过程中,我发现了非常熟悉的设计逻辑:即类似于Hystrix的断路器和RocketMQ消费失败重试的策略。由于之前有相关经验,因此我很快就能够理解作者的设计意图。

注:事后我测试了一下Log4j2日志框架,表现也差不多,磁盘满了也不会对应用程序本身产生影响。

相关文章:

磁盘满了对日志打印(Logback)的影响

背景 我们生产环境有一个服务半夜报警&#xff1a;磁盘剩余空间不足10%&#xff0c;请及时处理。排查后发现是新上线的一个功能&#xff0c;日志打太多导致的&#xff0c;解决方法有很多&#xff0c;就不赘述了。领导担心报警不及时、或者报警遗漏&#xff0c;担心磁盘满了对线…...

【算法与数据结构】--算法基础--数据结构概述

一、什么是数据结构 数据结构是一种组织和存储数据的方式&#xff0c;它定义了数据之间的关系、操作和存储方式&#xff0c;以便有效地访问和修改数据。数据结构是计算机科学中的一个重要概念&#xff0c;它为处理和管理数据提供了基本框架。数据结构通常包括以下几个重要方面…...

QECon大会亮相产品,全栈测试平台推荐:RunnerGo

最近在gitee上看见一款获得GVP&#xff08;最有价值开源项目&#xff09;的测试平台RunnerGo&#xff0c;看他们官网介绍包含了接口测试、性能测试、自动化测试。知道他们有saas版可以试用&#xff0c;果断使用了一下&#xff0c;对其中场景管理和性能测试印象深刻&#xff0c;…...

前端小案例-图片存放在远端服务器

前端小案例-图片存放在远端服务器 项目背景&#xff1a; 一个智能产业园的小程序由于可以控制很多种设备&#xff0c;可能有灯、空调、窗帘等智能设备。 现在面临以下问题&#xff1a; 需要存放很多设备的图标。设备的图标可能会进行修改。 为了解决上面的问题&#xff0c…...

【鼠标右键菜单添加用VSCode打开文件或文件夹】

鼠标右键菜单添加用VSCode打开文件或文件夹 演示效果如下&#xff1a; 右击文件 或右击文件夹 或在文件夹内空白处右击 方法一&#xff1a;重装软件 重装软件&#xff0c;安装时勾选如图所示方框&#xff08;如果登录的有账号保存有配置信息可以选择重装软件&#xff0c…...

【jvm--堆】

文章目录 1. 堆&#xff08;Heap&#xff09;的核心概述2. 图解对象分配过程2.1 Minor GC&#xff0c;MajorGC、Full GC2.1 堆空间分代思想2.3 内存分配策略2.4 TLAB&#xff08;Thread Local Allocation Buffer&#xff09;2.5 堆空间的参数设置2.6 逃逸分析2.7 逃逸分析&…...

【数据库——MySQL(实战项目1)】(1)图书借阅系统

目录 1. 简述2. 功能3. 数据库结构设计3.1 绘制 E-R 图3.2 创建数据库3.3 创建表3.4 插入表数据 1. 简述 经过前期的学习&#xff0c;我们已经掌握数据库基础操作&#xff0c;因此是时候来做一个实战项目了——图书借阅系统。对于图书借阅系统&#xff0c;相信大家不难想到至少…...

[GXYCTF 2019]Ping Ping Ping题目解析

本题考察的内容是rce绕过&#xff0c;本事过滤的东西不算多也算是比较好绕过 基础看到这种先ping一下试试看 输入127.0.0.1看看有啥东西 有回显说明可以接着往下做 借用RCE漏洞详解及绕过总结(全面)-CSDN博客这个大佬整理的rce绕过 ;A;B无论真假&#xff0c;A与B都执行&…...

HTTP协议的请求协议和响应协议的组成,HTTP常见的状态信息

HTTP协议 什么是协议 协议实际上是某些人或组织提前制定好的一套规范,大家只要都按照这个规范来就可以做到沟通无障碍 HTTP协议是W3C(万维网联盟组织)制定的一种超文本传输通信协议(发送消息的模板和数据的格式),除了传送字符串,还有声音、视频、图片等流媒体等超文本信息 …...

【LeetCode】剑指 Offer Ⅱ 第6章:栈(6道题) -- Java Version

题库链接&#xff1a;https://leetcode.cn/problem-list/e8X3pBZi/ 类型题目解决方案栈的应用剑指 Offer II 036. 后缀表达式模拟 栈 ⭐剑指 Offer II 037. 小行星碰撞分类讨论 栈 ⭐单调栈剑指 Offer II 038. 每日温度单调栈 ⭐剑指 Offer II 039. 直方图最大矩形面积单调栈…...

vue3的element-plus的el-dialog的样式修改无效问题

问题描述 想要修改element-plus的对话框el-dialog中的样式&#xff0c;发现在页面style的scoped属性下&#xff0c;使用:deep深入选择器进行修改是无效的。&#xff08;vue2下深度选择器是有效的&#xff09; //无效 :deep(.el-dialog){background-color: transparent; }解决…...

归纳所猜半结论推出完整结论:CF1592F1

https://www.luogu.com.cn/problem/CF1592F1 场上猜了个结论&#xff0c;感觉只会操作1。然后被样例1hack了。然后就猜如果 ( n , m ) (n,m) (n,m) 为1则翻转4操作&#xff0c;被#14hack了。然后就猜4操作只会进行一次&#xff0c;然后就不知道怎么做下去了。 上面猜的结论都…...

WPFdatagrid结合comboBox

在WPF的DataGrid中希望结合使用ComboBox下拉框&#xff0c;达到下拉选择绑定的效果&#xff0c;在实现的过程中&#xff0c;遇到了一些奇怪的问题&#xff0c;因此记录下来。 网上能够查询到的解决方案&#xff1a; 总共有三种ItemSource常见绑定实现方式&#xff1a; 1.ItemS…...

Markdown类图之继承、实现、关联、依赖、组合、聚合总结(十五)

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

@MultipartConfig注解

前言&#xff1a; 在学习Javaweb的Servlet文件上传和下载的过程中&#xff0c;我们会遇到一个特殊的注解---MultipartConfig。 MultipartConfig的适用情况&#xff1a; 1.文件上传: 当您的应用程序需要接收用户上传的文件时&#xff0c;可以在相应的 Servlet 上使用 Multipart…...

Python并发编程简介

1、Python对并发编程的支持 多线程: threading, 利用CPU和IO可以同时执行的原理,让CPU不会干巴巴等待IO完成多进程: multiprocessing, 利用多核CPU的能力&#xff0c;真正的并行执行任务异步IO: asyncio,在单线程利用CPU和IO同时执行的原理&#xff0c;实现函数异步执行使用Lo…...

WebSocket介绍及部署

WebSocket是一种在单个TCP连接上进行全双工通信的协议&#xff0c;其设计的目的是在Web浏览器和Web服务器之间进行实时通信&#xff08;实时Web&#xff09;。 WebSocket协议的优点包括&#xff1a; 1. 更高效的网络利用率&#xff1a;与HTTP相比&#xff0c;WebSocket的握手…...

自动求导,计算图示意图及pytorch实现

pytorch实现 x1 torch.tensor(3.0, requires_gradTrue) y1 torch.tensor(2.0, requires_gradTrue) a x1 ** 2 b 3 * a c b * y1 c.backward() print(x1.grad) print(y1.grad) print(x1.grad 6 * x1 * y1) print(y1.grad 3 * (x1 ** 2))输出为&#xff1a; tensor(36.) …...

睿伴科创上线了

Robotutor睿伴&#xff0c;一个专业的青少儿编程科创教育品牌和科创服务平台。 Robotutor睿伴拥有一个超过5年的青少儿编程科创教育团队&#xff0c;积累了丰富的课程研发&#xff0c;教学服务和赛事辅导经验。并和上海多所知名高校、上海市计算机学会、上海青少年科学社等开展…...

域名抢注和域名注册

随着互联网的发展&#xff0c;域名已经成为了企业和个人在网络上展示自己的重要标志。如何获得一段好记、易拼写、有意义的域名&#xff0c;是很多人都面临的问题。本文将介绍域名抢注和域名注册的相关内容&#xff0c;并推荐ym.qqmu.com这个可靠的域名注册平台。 一、什么是域…...

【20】c++设计模式——>组合模式

组合模式定义 C组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;他允许将对象组合成树形结构来表示“部分-整体”的层次结构&#xff1b;在组合模式中有两种基本类型的对象&#xff1a;叶子对象和组合对象&#xff0c;叶子对象时没有子对象…...

Jetpack:004-如何使用文本组件

文章目录 1. 概念介绍2. 使用方法2.1 通用参数2.2 专用参数 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack组件在布局中的对齐方式&#xff0c;本章回中主要介绍文 本组件的使用方法。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧 1. 概念介绍 我们在本章…...

JVM(八股文)

目录 一、JVM简介 二、JVM中的内存区域划分 三、JVM加载 1.类加载 1.1 加载 1.2 验证 1.3 准备 1.4 解析 1.5 初始 1.6 总结 2.双亲委派模型 四、JVM 垃圾回收&#xff08;GC&#xff09; 1.确认垃圾 1.1 引用计数 1.2 可达性分析&#xff08;Java 采用的方案&a…...

C#WPF标记扩展应用实例

本文介绍C#WPF标记扩展应用实例 一、标记扩展 标记扩展是一个 XAML 语言概念。 用于提供特性语法的值时,大括号({ 和 })表示标记扩展用法。 此用法指示 XAML 处理不要像通常那样将特性值视为文本字符串或者可转换为字符串的值。就是类似于值用变量的意思。 WPF 应用编程中…...

四维曲面如何画?matlab

clc; clear all [theta,phi]meshgrid(linspace(0,pi,50),linspace(0,2*pi,50)); zcos(theta); xsin(theta).*cos(phi); ysin(theta).*sin(phi); f-1*((x.*y).2(y.*z).2(z.*x).^2); surf(sin(theta).*cos(phi).*f,sin(theta).*sin(phi).*f,cos(theta).*f,f) 结果...

软件培训测试高级工程师多测师肖sir__html之作业11

html之作业 案例1&#xff1a; 截图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>表单</title></head><body><table style"background-color:red" bo…...

详解一典型的反激式开关电源方案

理解一个单端反激式开关电源方案&#xff1a; 1、抛出问题&#xff1a; 如图&#xff0c;在某系统方案上看到下图所示的单端反激式开关电源方案。 2、解析问题&#xff1a; 2.1、乍一看&#xff1a; 典型的AC-DC电路&#xff0c;考虑了安规及过压过流保护&#xff0c;如&am…...

AI 大框架基于python来实现基带处理之TensorFlow(信道估计和预测模型,信号解调和解码模型)

AI 大框架基于python来实现基带处理之TensorFlow(信道估计和预测模型,信号解调和解码模型) 基带处理&#xff08;Baseband Processing&#xff09;是一种信号处理技术&#xff0c;用于在通信系统中处理和调制基带信号。基带信号是指未经过调制的信号&#xff0c;通常包含原始数…...

阿里云上了新闻联播

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 阿里新任的CEO吴泳铭上央视新闻联播了! 在昨天的新闻联播里&#xff0c;出席科技座谈会&#xff0c;有一个特别镜头&#xff0c;出现了阿里新任CEO吴泳铭的镜头。 这个信号意义明显&#xff0c;我…...

算法练习12——跳跃游戏

LeetCode 55 跳跃游戏 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 贪…...