前言
在 Springboot
应用中,默认使用 logback-spring.xml
配置日志相关
功能简述
1. 自定义日志文件名
<file>${log.path}/sys-info.log</file>
2. 归档规则 && 压缩
2.1. 归档配置
<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
<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>
源码伪代码
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. 日志格式 && 编码
<encoder>
<!-- 编码 -->
<charset>UTF-8</charset>
<!-- 日志格式 -->
<pattern>${log.pattern}</pattern>
</encoder>
现象
多个进程或者实例
的日志同时打印在 同一个文件
中,且日志文件归档使用 压缩
,日志目录下出现了大量的 .tmp
临时文件,占用内存
原因
归档伪代码
// 无压缩
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);
}
}
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
文件创建逻辑