Junjie
21 小时以前 42ce1f4b6f9df984d14e29f9d9ff188de7f3c6d7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package com.zy.core.task;
 
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
 
@Slf4j
@Component
public class DeviceLogScheduler {
 
    private static final ReentrantLock FILE_OP_LOCK = new ReentrantLock();
    private static final int DEFAULT_EXPIRE_DAYS = 1;
    private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("uuuuMMdd")
            .withResolverStyle(ResolverStyle.STRICT);
 
    @Value("${deviceLogStorage.type}")
    private String storageType;
    @Value("${deviceLogStorage.loggingPath}")
    private String loggingPath;
    @Value("${deviceLogStorage.expireDays}")
    private Integer expireDays;
 
    @PostConstruct
    public void validateStorageMode() {
        if (!"file".equalsIgnoreCase(storageType)) {
            throw new IllegalStateException("deviceLogStorage.type 仅支持 file,当前配置为: " + storageType);
        }
        ensureLoggingDirectoryExistsOrThrow();
    }
 
    @Scheduled(cron = "${deviceLogStorage.cleanupScanCron:0 0 3 * * ?}")
    public void delDeviceLog() {
        if (!FILE_OP_LOCK.tryLock()) {
            return;
        }
        try {
            ensureLoggingDirectoryExists();
            clearFileLog(resolveExpireDays());
        } finally {
            FILE_OP_LOCK.unlock();
        }
    }
 
    private void ensureLoggingDirectoryExistsOrThrow() {
        Path loggingRootPath = Paths.get(loggingPath);
        try {
            Files.createDirectories(loggingRootPath);
        } catch (Exception e) {
            throw new IllegalStateException("初始化设备日志目录失败, path=" + loggingPath, e);
        }
    }
 
    private void ensureLoggingDirectoryExists() {
        try {
            Files.createDirectories(Paths.get(loggingPath));
        } catch (Exception e) {
            log.warn("初始化设备日志目录失败, path={}", loggingPath, e);
        }
    }
 
    private int resolveExpireDays() {
        if (expireDays == null || expireDays <= 0) {
            log.warn("deviceLogStorage.expireDays 配置无效,使用默认值: {}", DEFAULT_EXPIRE_DAYS);
            return DEFAULT_EXPIRE_DAYS;
        }
        return expireDays;
    }
 
    private void clearFileLog(int days) {
        try {
            Path baseDir = Paths.get(loggingPath);
            if (!Files.exists(baseDir)) {
                return;
            }
            LocalDate earliestReservedDay = LocalDate.now().minusDays(days - 1L);
            List<Path> expiredDayDirs = new ArrayList<>();
            try (Stream<Path> stream = Files.list(baseDir)) {
                stream.filter(Files::isDirectory)
                        .forEach(dayDir -> collectExpiredDayDirectory(dayDir, earliestReservedDay, expiredDayDirs));
            }
            for (Path dayDir : expiredDayDirs) {
                Files.walkFileTree(dayDir, new SimpleFileVisitor<>() {
                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws java.io.IOException {
                        Files.deleteIfExists(file);
                        return FileVisitResult.CONTINUE;
                    }
 
                    @Override
                    public FileVisitResult postVisitDirectory(Path dir, java.io.IOException exc) throws java.io.IOException {
                        Files.deleteIfExists(dir);
                        return FileVisitResult.CONTINUE;
                    }
                });
            }
        } catch (Exception e) {
            log.error("删除设备日志文件失败, path={}", loggingPath, e);
        }
    }
 
    private void collectExpiredDayDirectory(Path dayDir, LocalDate earliestReservedDay, List<Path> expiredDayDirs) {
        LocalDate dayValue = parseDayDirectory(dayDir.getFileName().toString());
        if (dayValue == null || !dayValue.isBefore(earliestReservedDay)) {
            return;
        }
        expiredDayDirs.add(dayDir);
    }
 
    private LocalDate parseDayDirectory(String dayDirectoryName) {
        try {
            return LocalDate.parse(dayDirectoryName, DAY_FORMATTER);
        } catch (DateTimeParseException ignored) {
            return null;
        }
    }
}