Springboot 学习 之 logback-spring.xml 日志压缩 .tmp 临时文件问题

文章目录

  • 前言
  • 功能简述
    • [1. 自定义日志文件名](#1. 自定义日志文件名)
    • [2. 归档规则 && 压缩](#2. 归档规则 && 压缩)
      • [2.1. 归档配置](#2.1. 归档配置)
      • [2.2. 归档压缩](#2.2. 归档压缩)
      • [2.3. 日志格式 && 编码](#2.3. 日志格式 && 编码)
  • 现象
  • 原因
  • 解决办法

前言

在 Springboot 应用中,默认使用 logback-spring.xml 配置日志相关

功能简述

1. 自定义日志文件名

xml 复制代码
	<file>${log.path}/sys-info.log</file>

2. 归档规则 && 压缩

2.1. 归档配置

xml 复制代码
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!-- 日志文件名格式 -->
        <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!-- 单个文件最大大小 -->
        <maxFileSize>50MB</maxFileSize>
        <!-- 日志最大的历史 60天 -->
        <maxHistory>60</maxHistory>
    </rollingPolicy>

2.2. 归档压缩

通过 <fileNamePattern> 文件后缀判断是否压缩,支持 GZ、ZIP

xml 复制代码
     <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
         <!-- 日志文件名格式 -->
         <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
         <!-- 单个文件最大大小 -->
         <maxFileSize>50MB</maxFileSize>
         <!-- 日志最大的历史 60天 -->
         <maxHistory>60</maxHistory>
     </rollingPolicy>

源码伪代码

java 复制代码
    switch (compressionMode) {
	    case GZ:
	        if (fileNamePatternStr.endsWith(".gz"))
	            return fileNamePatternStr.substring(0, len - 3);
	        else
	            return fileNamePatternStr;
	    case ZIP:
	        if (fileNamePatternStr.endsWith(".zip"))
	            return fileNamePatternStr.substring(0, len - 4);
	        else
	            return fileNamePatternStr;
	    case NONE:
	        return fileNamePatternStr;
    }

2.3. 日志格式 && 编码

xml 复制代码
     <encoder>
     	 <!-- 编码 -->
         <charset>UTF-8</charset>
     	 <!-- 日志格式 -->
         <pattern>${log.pattern}</pattern>
     </encoder>

现象

多个进程或者实例 的日志同时打印在 同一个文件 中,且日志文件归档使用 压缩,日志目录下出现了大量的 .tmp 临时文件,占用内存

原因

归档伪代码

java 复制代码
	 // 无压缩
     if (compressionMode == CompressionMode.NONE) {
     	 // 配置 file 标签
         if (getParentsRawFileProperty() != null) {
             // 直接重命名归档
             renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);
         } 
     } else { // 压缩
     	 // 未配置 file 标签
         if (getParentsRawFileProperty() == null) {
         	 // 直接压缩归档
             compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elapsedPeriodStem);
         } else {
             // 先重命名为 .tmp 文件,然后再压缩归档;
             compressionFuture = renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem);
         }
     }
java 复制代码
    private void gzCompress(String nameOfFile2gz, String nameOfgzedFile) {
    	// 源文件(可能是 .log 也可能是 .log.tmp 文件)
        File file2gz = new File(nameOfFile2gz);
        if (!file2gz.exists()) {
            return;
        }

		// 目标压缩文件
        if (!nameOfgzedFile.endsWith(".gz")) {
            nameOfgzedFile = nameOfgzedFile + ".gz";
        }
        File gzedFile = new File(nameOfgzedFile);
        // 如果目标文件已存在,则直接返回;不同版本写法略有不同,但大同小异;
        // 此处多进程或实例同时操作时,可能出现后边执行归档操作的进程直接 return,未执行删除逻辑
        if (gzedFile.exists()) {
            return;
        }

        ......    

        BufferedInputStream bis = null;
        GZIPOutputStream gzos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(nameOfFile2gz));
            gzos = new GZIPOutputStream(new FileOutputStream(nameOfgzedFile));
			......
			// 如果源文件存在,则删除
            if (!file2gz.delete()) {
                addStatus(new WarnStatus("Could not delete [" + nameOfFile2gz + "].", this));
            }
        } catch (Exception e) {
        	......    
        } finally {
        	......    
        }
    }

解决办法

  • 不压缩:直接归档原始日志文件,不存在此问题
  • 压缩:不使用 <file> 标签,可以跳过 .tmp 文件创建逻辑
相关推荐
流烟默1 小时前
MCP第 5 篇:用 Spring AI 写 MCP:注解、自动配置、ChatClient 集成
人工智能·spring·mcp封装
我是人✓1 小时前
SpringBoot项目的五种创建方式
java·spring boot·后端
来福猿3 小时前
SpringCloud Config 源码分析:配置拉取、刷新与多环境管理
后端·spring·spring cloud
星恒随风3 小时前
Linux线程概念详解:从分页存储、页表、MMU与TLB,深入理解线程和进程的区别与联系
linux·运维·笔记·学习·状态模式
qq_284274053 小时前
数控机床编程基础与坐标系:编程六步流程、程序号/程序段/程序字格式,与右手定则下车床铣床XYZ轴判定(含考点)
笔记·学习·自动化·制造
德林恩宝4 小时前
On-Policy Distillation(OPD)学习文档
人工智能·学习·机器学习·大模型·llm·大模型微调
xxwl5854 小时前
区域赛分类题解
c++·笔记·学习·算法
古法老登瞎折腾4 小时前
大数据智能运维实战(一):机器和任务数据都有了,如何用 MCP 搭建运维助手?
大数据·运维·spring boot·spring ai·mcp
企业数字化笔记5 小时前
多租户配额怎么控制?用量口径、并发预留与超额处理的实现方法
java·数据库·spring boot
雷一言说7 小时前
Spring AI 2.0 RAG:用 Metadata Filter 做多租户检索隔离
java·spring