Skip to content

Commit a88e7c5

Browse files
Tianci Shentiancishen
Tianci Shen
authored andcommitted
feat: Add logic to clean up logs by last modified date for TimeBasedArchiveRemover
Signed-off-by: Tianci Shen <[email protected]>
1 parent f3efb8f commit a88e7c5

File tree

6 files changed

+121
-12
lines changed

6 files changed

+121
-12
lines changed

logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedRollingPolicy.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
package ch.qos.logback.core.rolling;
1515

1616
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP.Usage;
17+
import ch.qos.logback.core.util.Duration;
1718
import ch.qos.logback.core.util.FileSize;
1819

1920
public class SizeAndTimeBasedRollingPolicy<E> extends TimeBasedRollingPolicy<E> {
2021

2122
FileSize maxFileSize;
2223

24+
Duration checkIncrement = null;
25+
2326
@Override
2427
public void start() {
2528
SizeAndTimeBasedFNATP<E> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<E>(Usage.EMBEDDED);
@@ -30,6 +33,10 @@ public void start() {
3033
addInfo("Archive files will be limited to [" + maxFileSize + "] each.");
3134
}
3235

36+
if (checkIncrement != null) {
37+
sizeAndTimeBasedFNATP.setCheckIncrement(checkIncrement);
38+
}
39+
3340
sizeAndTimeBasedFNATP.setMaxFileSize(maxFileSize);
3441
timeBasedFileNamingAndTriggeringPolicy = sizeAndTimeBasedFNATP;
3542

@@ -47,6 +54,10 @@ public void setMaxFileSize(FileSize aMaxFileSize) {
4754
this.maxFileSize = aMaxFileSize;
4855
}
4956

57+
public void setCheckIncrement(Duration checkIncrement) {
58+
this.checkIncrement = checkIncrement;
59+
}
60+
5061
@Override
5162
public String toString() {
5263
return "c.q.l.core.rolling.SizeAndTimeBasedRollingPolicy@" + this.hashCode();

logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements Trig
6161
TimeBasedFileNamingAndTriggeringPolicy<E> timeBasedFileNamingAndTriggeringPolicy;
6262

6363
boolean cleanHistoryOnStart = false;
64+
boolean cleanLogsByLastModifiedDate = false;
6465

6566
public void start() {
6667
// set the LR for our utility object
@@ -109,6 +110,7 @@ public void start() {
109110
archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
110111
archiveRemover.setMaxHistory(maxHistory);
111112
archiveRemover.setTotalSizeCap(totalSizeCap.getSize());
113+
archiveRemover.setCleanLogsByLastModifiedDate(cleanLogsByLastModifiedDate);
112114
if (cleanHistoryOnStart) {
113115
addInfo("Cleaning on start up");
114116
Instant now = Instant.ofEpochMilli(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
@@ -271,6 +273,17 @@ public void setCleanHistoryOnStart(boolean cleanHistoryOnStart) {
271273
this.cleanHistoryOnStart = cleanHistoryOnStart;
272274
}
273275

276+
277+
/**
278+
* Should archive removal use a file's last modified date to determine deletion?
279+
* Default is false.
280+
*
281+
* @param cleanLogsByLastModifiedDate
282+
*/
283+
public void setCleanLogsByLastModifiedDate(boolean cleanLogsByLastModifiedDate){
284+
this.cleanLogsByLastModifiedDate = cleanLogsByLastModifiedDate;
285+
}
286+
274287
@Override
275288
public String toString() {
276289
return "c.q.l.core.rolling.TimeBasedRollingPolicy@" + this.hashCode();

logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ArchiveRemover.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ public interface ArchiveRemover extends ContextAware {
3131

3232
void setTotalSizeCap(long totalSizeCap);
3333

34+
void setCleanLogsByLastModifiedDate(boolean cleanLogsByLastModifiedDate);
35+
3436
Future<?> cleanAsynchronously(Instant now);
3537
}

logback-core/src/main/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemover.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public SizeAndTimeBasedArchiveRemover(FileNamePattern fileNamePattern, RollingCa
2929
super(fileNamePattern, rc);
3030
}
3131

32+
File getParentDir(Instant cleanupCutoff) {
33+
return getParentDir(new File(fileNamePattern.convertMultipleArguments(cleanupCutoff, 0)));
34+
}
35+
3236
protected File[] getFilesInPeriod(Instant instantOfPeriodToClean) {
3337
File archive0 = new File(fileNamePattern.convertMultipleArguments(instantOfPeriodToClean, 0));
3438
File parentDir = getParentDir(archive0);

logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class TimeBasedArchiveRemover extends ContextAwareBase implements Archive
3636
final RollingCalendar rc;
3737
private int maxHistory = CoreConstants.UNBOUNDED_HISTORY;
3838
private long totalSizeCap = CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP;
39+
private boolean cleanLogsByLastModifiedDate = false;
3940
final boolean parentClean;
4041
long lastHeartBeat = UNINITIALIZED;
4142

@@ -70,10 +71,54 @@ public void clean(Instant now) {
7071
addInfo("Multiple periods, i.e. " + periodsElapsed
7172
+ " periods, seem to have elapsed. This is expected at application start.");
7273
}
73-
for (int i = 0; i < periodsElapsed; i++) {
74-
int offset = getPeriodOffsetForDeletionTarget() - i;
75-
Instant instantOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset);
76-
cleanPeriod(instantOfPeriodToClean);
74+
75+
if (cleanLogsByLastModifiedDate) {
76+
// Delete old logs based on date the file was last modified
77+
cleanLogsByDateModified(now);
78+
} else {
79+
// Delete old logs based on expected file name
80+
for (int i = 0; i < periodsElapsed; i++) {
81+
int offset = getPeriodOffsetForDeletionTarget() - i;
82+
Instant instantOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset);
83+
cleanPeriod(instantOfPeriodToClean);
84+
}
85+
}
86+
}
87+
88+
/**
89+
* Iterates through log files and deletes files outside the rollover window
90+
* Expects the file name to occur before the date specifier
91+
* Does not work well with file patterns that have auxiliary date specifiers
92+
*
93+
* @param now
94+
*/
95+
private void cleanLogsByDateModified(Instant now) {
96+
File filePattern = new File(fileNamePattern.getPattern());
97+
String fileNameBeforeDateSpecifier = filePattern.getName().split("\\%d\\{.+\\}")[0];
98+
Instant cleanupCutoff = rc.getEndOfNextNthPeriod(now, getPeriodOffsetForDeletionTarget());
99+
100+
File parentDir;
101+
parentDir = getParentDir(cleanupCutoff);
102+
if (parentDir == null) {
103+
addError("Cannot get parent directory");
104+
return;
105+
}
106+
107+
File[] matchedFiles;
108+
matchedFiles = parentDir.listFiles((dir, name) -> name.contains(fileNameBeforeDateSpecifier));
109+
if (matchedFiles == null) {
110+
addError("Failed to find relevant log files");
111+
return;
112+
}
113+
114+
for (File file : matchedFiles) {
115+
Instant lastModifiedDate = Instant.ofEpochMilli(file.lastModified());
116+
if (cleanupCutoff.isAfter(lastModifiedDate)) {
117+
checkAndDeleteFile(file);
118+
}
119+
}
120+
if (parentClean && matchedFiles.length > 0) {
121+
removeFolderIfEmpty(parentDir);
77122
}
78123
}
79124

@@ -148,6 +193,10 @@ protected void descendingSort(File[] matchingFileArray, Instant instant) {
148193
// nothing to do in super class
149194
}
150195

196+
File getParentDir(Instant cleanupCutoff) {
197+
return getParentDir(new File(fileNamePattern.convert(cleanupCutoff)));
198+
}
199+
151200
File getParentDir(File file) {
152201
File absolute = file.getAbsoluteFile();
153202
File parentDir = absolute.getParentFile();
@@ -241,6 +290,10 @@ public void setTotalSizeCap(long totalSizeCap) {
241290
this.totalSizeCap = totalSizeCap;
242291
}
243292

293+
public void setCleanLogsByLastModifiedDate(boolean cleanLogsByLastModifiedDate) {
294+
this.cleanLogsByLastModifiedDate = cleanLogsByLastModifiedDate;
295+
}
296+
244297
public String toString() {
245298
return "c.q.l.core.rolling.helper.TimeBasedArchiveRemover";
246299
}

logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_Test.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import static ch.qos.logback.core.CoreConstants.DAILY_DATE_PATTERN;
1717
import static org.junit.jupiter.api.Assertions.assertEquals;
1818
import static org.junit.jupiter.api.Assertions.assertTrue;
19+
import static org.junit.jupiter.api.Assertions.assertFalse;
1920

2021
import java.io.File;
2122
import java.io.FileFilter;
23+
import java.io.IOException;
2224
import java.time.Instant;
2325
import java.time.ZoneId;
2426
import java.time.ZonedDateTime;
@@ -338,10 +340,10 @@ public void dailyChronologSizeBasedRolloverWithSecondPhase() {
338340
checkDirPatternCompliance(maxHistory + 1);
339341
}
340342

341-
void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory, long durationInMillis) {
343+
void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory, long durationInMillis, boolean cleanLogsByLastModifiedDate) {
342344
ConfigParameters params = new ConfigParameters(currentTime).fileNamePattern(fileNamePattern)
343345
.maxHistory(maxHistory);
344-
buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START);
346+
buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START, cleanLogsByLastModifiedDate);
345347
rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
346348
currentTime += durationInMillis / 2;
347349
add(tbrp.compressionFuture);
@@ -359,7 +361,7 @@ public void cleanHistoryOnStartWithHourPattern() {
359361
String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_HOUR_PATTERN + "}.txt";
360362
int maxHistory = 3;
361363
for (int i = 0; i <= 5; i++) {
362-
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
364+
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
363365
simulatedTime += MILLIS_IN_HOUR;
364366
}
365367
checkFileCount(expectedCountWithoutFolders(maxHistory));
@@ -379,7 +381,7 @@ public void cleanHistoryOnStartWithHourPatternWithCollisions() {
379381
String fileNamePattern = randomOutputDir + "clean-%d{HH}.txt";
380382
int maxHistory = 3;
381383
for (int i = 0; i <= 5; i++) {
382-
logTwiceAndStop(now, fileNamePattern, maxHistory, MILLIS_IN_DAY);
384+
logTwiceAndStop(now, fileNamePattern, maxHistory, MILLIS_IN_DAY, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
383385
now = now + MILLIS_IN_HOUR;
384386
}
385387
checkFileCount(expectedCountWithoutFolders(maxHistory));
@@ -391,7 +393,7 @@ public void cleanHistoryOnStartWithDayPattern() {
391393
String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
392394
int maxHistory = 3;
393395
for (int i = 0; i <= 5; i++) {
394-
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_DAY);
396+
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_DAY, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
395397
simulatedTime += MILLIS_IN_DAY;
396398
}
397399
checkFileCount(expectedCountWithoutFolders(maxHistory));
@@ -403,12 +405,32 @@ public void cleanHistoryOnStartWithHourDayPattern() {
403405
String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd-HH}.txt";
404406
int maxHistory = 3;
405407
for (int i = 0; i <= 5; i++) {
406-
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
408+
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
407409
simulatedTime += MILLIS_IN_HOUR;
408410
}
409411
checkFileCount(expectedCountWithoutFolders(maxHistory));
410412
}
411413

414+
@Test
415+
public void cleanLogsByLastModifiedDateWithHourDayPattern() throws IOException {
416+
long simulatedTime = WED_2016_03_23_T_230705_CET;
417+
String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd-HH}.txt";
418+
int maxHistory = 3;
419+
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
420+
simulatedTime += MILLIS_IN_HOUR;
421+
File fileToDelete = new File(randomOutputDir + "clean-0000-00-00-00.txt");
422+
if (!fileToDelete.exists()) {
423+
assertTrue(fileToDelete.createNewFile());
424+
}
425+
assertTrue(fileToDelete.setLastModified(0));
426+
for (int i = 0; i <= 2; i++) {
427+
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
428+
simulatedTime += MILLIS_IN_HOUR;
429+
}
430+
checkFileCount(expectedCountWithoutFolders(maxHistory));
431+
assertFalse(fileToDelete.exists());
432+
}
433+
412434
int expectedCountWithoutFolders(int maxHistory) {
413435
return maxHistory + 1;
414436
}
@@ -422,7 +444,7 @@ int expectedCountWithFolders(int maxHistory, boolean withExtraFolder) {
422444
return result;
423445
}
424446

425-
void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart) {
447+
void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart, boolean cleanLogsByLastModifiedDate) {
426448
rfa.setContext(context);
427449
rfa.setEncoder(encoder);
428450
tbrp.setContext(context);
@@ -431,6 +453,7 @@ void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart)
431453
tbrp.setTotalSizeCap(new FileSize(cp.sizeCap));
432454
tbrp.setParent(rfa);
433455
tbrp.setCleanHistoryOnStart(cleanHistoryOnStart);
456+
tbrp.setCleanLogsByLastModifiedDate(cleanLogsByLastModifiedDate);
434457
tbrp.timeBasedFileNamingAndTriggeringPolicy = tbfnatp;
435458
tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(cp.simulatedTime);
436459
tbrp.start();
@@ -440,10 +463,13 @@ void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart)
440463

441464
boolean DO_CLEAN_HISTORY_ON_START = true;
442465
boolean DO_NOT_CLEAN_HISTORY_ON_START = false;
466+
boolean DO_CLEAN_LOGS_BY_LAST_MODIFIED_DATE = true;
467+
boolean DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE = false;
468+
443469

444470
long logOverMultiplePeriods(ConfigParameters cp) {
445471

446-
buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START);
472+
buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
447473

448474
int runLength = cp.simulatedNumberOfPeriods * ticksPerPeriod;
449475
int startInactivityIndex = cp.startInactivity * ticksPerPeriod;

0 commit comments

Comments
 (0)