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> 文件后缀判断是否压缩,支持 GZZIP

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 文件创建逻辑
相关推荐
a1117761 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao2 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
你想知道什么?3 小时前
线性回归-学习笔记
笔记·学习·线性回归
鱼毓屿御3 小时前
从「只会聊」到「边想边做」的跃迁
前端·学习·react.js
AOwhisky5 小时前
Python 学习笔记(第十五期)——运维自动化(下·后篇):堡垒机实战——paramiko高阶篇
运维·python·学习·云原生·自动化·运维开发
Java内核笔记5 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端
minglie16 小时前
zynq中linux系统的自动登录和免密登录
学习
我是唐青枫6 小时前
Java Spring Security 实战详解:从登录认证到 JWT 权限控制
java·spring
茯苓gao7 小时前
嵌入式开发笔记:Qt信号槽机制深度解析——从原理到实战的全方位指南
开发语言·笔记·嵌入式硬件·qt·学习
海棠Flower未眠7 小时前
SpringBoot 消息死信队列(荣耀典藏版)
java·数据库·spring boot