场景:项目每天都会产生大量的日志信息并占据服务器大量空间,旧的日志并没有利用价值,所以我们需要定时清除旧日志,保证磁盘不会占满影响日志正常输出。
程序代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author Miku
* @Date 2024/09/04 14:54
* @Description 定时自动清理磁盘
* @Version 1.0
*/
@Component
public class FileCleanerScheduled {
private static final Logger log = LoggerFactory.getLogger(FileCleanerScheduled.class);
// todo 如果可以的话,路径放配置文件更好
private static final String CLEAN_FILE_CONFIG_PATH = "/home/miku/config/clean_file_config.txt";
// 磁盘利用率超过80%则清理
private double thresholdDisk;
// 监控的磁盘路径
private String diskPath;
// 删除指定日期之前的日志数据
private String targetDate;
// 日期格式
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
// 每周日的午夜执行
@Scheduled(cron = "0 0 0 * * SUN")
public void runTask() {
try {
loadConfig();
checkConfig();
File disk = new File(diskPath);
long totalSpace = disk.getTotalSpace();
long freeSpace = disk.getFreeSpace();
// 磁盘利用率
double diskUsageRatio = (double) (totalSpace - freeSpace) / totalSpace;
if (diskUsageRatio > thresholdDisk) {
Date thresholdDate;
try {
thresholdDate = DATE_FORMAT.parse(targetDate);
} catch (ParseException e) {
throw new IOException("日期格式不正确", e);
}
cleanOldFiles(disk, thresholdDate);
}
} catch (Exception e) {
log.error("执行自动清理磁盘定时任务出错:{}", e.getMessage());
}
}
/**
* 检查配置文件是否有误
*/
private void checkConfig() {
File file = new File(diskPath);
if (!file.isDirectory()) {
log.error("配置的磁盘路径无效:{}", diskPath);
throw new RuntimeException("无效的磁盘路径");
}
if (thresholdDisk < 0 || thresholdDisk > 1) {
log.error("磁盘利用率阈值无效:{}", thresholdDisk);
throw new RuntimeException("无效的磁盘利用率阈值");
}
try {
DATE_FORMAT.parse(targetDate);
} catch (ParseException e) {
log.error("日期格式无效:{}", targetDate);
throw new RuntimeException("无效的日期格式");
}
}
/**
* 加载配置文件
*/
private void loadConfig() {
File file = new File(CLEAN_FILE_CONFIG_PATH);
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
// 忽略以 # 开头的注释行
if (line.trim().startsWith("#") || line.trim().isEmpty()) {
continue;
}
String[] parts = line.split("=", 2);
if (parts.length == 2) {
switch (parts[0].trim()) {
case "thresholdDisk":
thresholdDisk = Double.parseDouble(parts[1].trim());
break;
case "diskPath":
diskPath = parts[1].trim();
break;
case "targetDate":
targetDate = parts[1].trim();
break;
default:
log.error("未知配置或错误配置:{}", parts[0]);
}
}
}
} catch (Exception e) {
log.error("配置文件读取失败:{}", e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 清理旧文件
*
* @param disk 清理的目录文件
* @param thresholdDate 门槛日期,在此日期之前全部清理
*/
private void cleanOldFiles(File disk, Date thresholdDate) {
File[] files = disk.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isFile()) {
String fileName = file.getName();
String filePath = file.getParent() + File.separator + fileName;
// 从文件名中取出日期
String dateString = getDateByFileName(fileName);
try {
Date fileDate = DATE_FORMAT.parse(dateString);
if (fileDate.before(thresholdDate)) {
Optional.of(file.delete()).ifPresentOrElse(delete -> log.info("已删除文件:{}", filePath), () -> log.error("删除文件失败:{}", filePath));
}
} catch (ParseException e) {
log.info("该文件名不存在日期:{}", filePath);
}
} else if (file.isDirectory()) {
cleanOldFiles(file, thresholdDate);
}
}
}
/**
* 从文件名中通过正则获取日期
*
* @param fileName 文件名
* @return 日期或""
*/
private String getDateByFileName(String fileName) {
String regex = "\\d{4}-\\d{2}-\\d{2}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(fileName);
return matcher.find() ? matcher.group() : "";
}
}
clean_file_config.txt文件
# 磁盘利用率阈值,超过该值将触发清理操作,范围为0.0到1.0,例如:0.80
thresholdDisk = 0.80
# 需要监控并处理的磁盘路径,例如: E:\ip 或 /home/miku/log
diskPath = /home/miku/log
# 将会自动清理该日期之前的日志,格式为 yyyy-MM-dd,例如2024-07-01
targetDate = 2024-07-01