Skip to content

Commit

Permalink
feat: Add logic to clean up logs by last modified date for TimeBasedA…
Browse files Browse the repository at this point in the history
…rchiveRemover

Signed-off-by: Tianci Shen <[email protected]>
  • Loading branch information
Tianci Shen authored and tiancishen committed Jan 8, 2024
1 parent f3efb8f commit ed5b7a5
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
package ch.qos.logback.core.rolling;

import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP.Usage;
import ch.qos.logback.core.util.Duration;
import ch.qos.logback.core.util.FileSize;

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

FileSize maxFileSize;

Duration checkIncrement = null;

@Override
public void start() {
SizeAndTimeBasedFNATP<E> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<E>(Usage.EMBEDDED);
Expand All @@ -30,6 +33,10 @@ public void start() {
addInfo("Archive files will be limited to [" + maxFileSize + "] each.");
}

if (checkIncrement != null) {
sizeAndTimeBasedFNATP.setCheckIncrement(checkIncrement);
}

sizeAndTimeBasedFNATP.setMaxFileSize(maxFileSize);
timeBasedFileNamingAndTriggeringPolicy = sizeAndTimeBasedFNATP;

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

public void setCheckIncrement(Duration checkIncrement) {
this.checkIncrement = checkIncrement;
}

@Override
public String toString() {
return "c.q.l.core.rolling.SizeAndTimeBasedRollingPolicy@" + this.hashCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements Trig
TimeBasedFileNamingAndTriggeringPolicy<E> timeBasedFileNamingAndTriggeringPolicy;

boolean cleanHistoryOnStart = false;
boolean cleanLogsByLastModifiedDate = false;

public void start() {
// set the LR for our utility object
Expand Down Expand Up @@ -109,6 +110,7 @@ public void start() {
archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
archiveRemover.setMaxHistory(maxHistory);
archiveRemover.setTotalSizeCap(totalSizeCap.getSize());
archiveRemover.setCleanLogsByLastModifiedDate(cleanLogsByLastModifiedDate);
if (cleanHistoryOnStart) {
addInfo("Cleaning on start up");
Instant now = Instant.ofEpochMilli(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
Expand Down Expand Up @@ -271,6 +273,17 @@ public void setCleanHistoryOnStart(boolean cleanHistoryOnStart) {
this.cleanHistoryOnStart = cleanHistoryOnStart;
}


/**
* Should archive removal use a file's last modified date to determine deletion?
* Default is false.
*
* @param cleanLogsByLastModifiedDate
*/
public void setCleanLogsByLastModifiedDate(boolean cleanLogsByLastModifiedDate){
this.cleanLogsByLastModifiedDate = cleanLogsByLastModifiedDate;
}

@Override
public String toString() {
return "c.q.l.core.rolling.TimeBasedRollingPolicy@" + this.hashCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ public interface ArchiveRemover extends ContextAware {

void setTotalSizeCap(long totalSizeCap);

void setCleanLogsByLastModifiedDate(boolean cleanLogsByLastModifiedDate);

Future<?> cleanAsynchronously(Instant now);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public SizeAndTimeBasedArchiveRemover(FileNamePattern fileNamePattern, RollingCa
super(fileNamePattern, rc);
}

File getParentDir(Instant cleanupCutoff) {
return getParentDir(new File(fileNamePattern.convertMultipleArguments(cleanupCutoff, 0)));
}

protected File[] getFilesInPeriod(Instant instantOfPeriodToClean) {
File archive0 = new File(fileNamePattern.convertMultipleArguments(instantOfPeriodToClean, 0));
File parentDir = getParentDir(archive0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class TimeBasedArchiveRemover extends ContextAwareBase implements Archive
final RollingCalendar rc;
private int maxHistory = CoreConstants.UNBOUNDED_HISTORY;
private long totalSizeCap = CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP;
private boolean cleanLogsByLastModifiedDate = false;
final boolean parentClean;
long lastHeartBeat = UNINITIALIZED;

Expand Down Expand Up @@ -70,10 +71,54 @@ public void clean(Instant now) {
addInfo("Multiple periods, i.e. " + periodsElapsed
+ " periods, seem to have elapsed. This is expected at application start.");
}
for (int i = 0; i < periodsElapsed; i++) {
int offset = getPeriodOffsetForDeletionTarget() - i;
Instant instantOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset);
cleanPeriod(instantOfPeriodToClean);

if (cleanLogsByLastModifiedDate) {
// Delete old logs based on date the file was last modified
cleanLogsByDateModified(now);
} else {
// Delete old logs based on expected file name
for (int i = 0; i < periodsElapsed; i++) {
int offset = getPeriodOffsetForDeletionTarget() - i;
Instant instantOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset);
cleanPeriod(instantOfPeriodToClean);
}
}
}

/**
* Iterates through log files and deletes files outside the rollover window
* Expects the file name to occur before the date specifier
* Does not work well with file patterns that have auxiliary date specifiers
*
* @param now
*/
private void cleanLogsByDateModified(Instant now) {
File filePattern = new File(fileNamePattern.getPattern());
String fileNameBeforeDateSpecifier = filePattern.getName().split("\\%d\\{.+\\}")[0];
Instant cleanupCutoff = rc.getEndOfNextNthPeriod(now, getPeriodOffsetForDeletionTarget());

File parentDir;
parentDir = getParentDir(cleanupCutoff);
if (parentDir == null) {
addError("Cannot get parent directory");
return;
}

File[] matchedFiles;
matchedFiles = parentDir.listFiles((dir, name) -> name.contains(fileNameBeforeDateSpecifier));
if (matchedFiles == null) {
addError("Failed to find relevant log files");
return;
}

for (File file : matchedFiles) {
Instant lastModifiedDate = Instant.ofEpochMilli(file.lastModified());
if (cleanupCutoff.isAfter(lastModifiedDate)) {
checkAndDeleteFile(file);
}
}
if (parentClean && matchedFiles.length > 0) {
removeFolderIfEmpty(parentDir);
}
}

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

File getParentDir(Instant cleanupCutoff) {
return getParentDir(new File(fileNamePattern.convert(cleanupCutoff)));
}

File getParentDir(File file) {
File absolute = file.getAbsoluteFile();
File parentDir = absolute.getParentFile();
Expand Down Expand Up @@ -241,6 +290,10 @@ public void setTotalSizeCap(long totalSizeCap) {
this.totalSizeCap = totalSizeCap;
}

public void setCleanLogsByLastModifiedDate(boolean cleanLogsByLastModifiedDate) {
this.cleanLogsByLastModifiedDate = cleanLogsByLastModifiedDate;
}

public String toString() {
return "c.q.l.core.rolling.helper.TimeBasedArchiveRemover";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
Expand Down Expand Up @@ -338,10 +339,10 @@ public void dailyChronologSizeBasedRolloverWithSecondPhase() {
checkDirPatternCompliance(maxHistory + 1);
}

void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory, long durationInMillis) {
void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory, long durationInMillis, boolean cleanLogsByLastModifiedDate) {
ConfigParameters params = new ConfigParameters(currentTime).fileNamePattern(fileNamePattern)
.maxHistory(maxHistory);
buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START);
buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START, cleanLogsByLastModifiedDate);
rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
currentTime += durationInMillis / 2;
add(tbrp.compressionFuture);
Expand All @@ -359,7 +360,7 @@ public void cleanHistoryOnStartWithHourPattern() {
String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_HOUR_PATTERN + "}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
simulatedTime += MILLIS_IN_HOUR;
}
checkFileCount(expectedCountWithoutFolders(maxHistory));
Expand All @@ -379,7 +380,7 @@ public void cleanHistoryOnStartWithHourPatternWithCollisions() {
String fileNamePattern = randomOutputDir + "clean-%d{HH}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(now, fileNamePattern, maxHistory, MILLIS_IN_DAY);
logTwiceAndStop(now, fileNamePattern, maxHistory, MILLIS_IN_DAY, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
now = now + MILLIS_IN_HOUR;
}
checkFileCount(expectedCountWithoutFolders(maxHistory));
Expand All @@ -391,7 +392,7 @@ public void cleanHistoryOnStartWithDayPattern() {
String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_DAY);
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_DAY, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
simulatedTime += MILLIS_IN_DAY;
}
checkFileCount(expectedCountWithoutFolders(maxHistory));
Expand All @@ -403,7 +404,26 @@ public void cleanHistoryOnStartWithHourDayPattern() {
String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd-HH}.txt";
int maxHistory = 3;
for (int i = 0; i <= 5; i++) {
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
simulatedTime += MILLIS_IN_HOUR;
}
checkFileCount(expectedCountWithoutFolders(maxHistory));
}

@Test
public void cleanLogsByLastModifiedDateWithHourDayPattern() throws IOException {
long simulatedTime = WED_2016_03_23_T_230705_CET;
String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd-HH}.txt";
int maxHistory = 3;
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
simulatedTime += MILLIS_IN_HOUR;
File fileToDelete = new File(randomOutputDir + "clean-0000-00-00-00.txt");
if (!fileToDelete.exists()) {
assertTrue(fileToDelete.createNewFile());
}
assertTrue(fileToDelete.setLastModified(0));
for (int i = 0; i <= 2; i++) {
logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR, DO_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);
simulatedTime += MILLIS_IN_HOUR;
}
checkFileCount(expectedCountWithoutFolders(maxHistory));
Expand All @@ -422,7 +442,7 @@ int expectedCountWithFolders(int maxHistory, boolean withExtraFolder) {
return result;
}

void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart) {
void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart, boolean cleanLogsByLastModifiedDate) {
rfa.setContext(context);
rfa.setEncoder(encoder);
tbrp.setContext(context);
Expand All @@ -431,6 +451,7 @@ void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart)
tbrp.setTotalSizeCap(new FileSize(cp.sizeCap));
tbrp.setParent(rfa);
tbrp.setCleanHistoryOnStart(cleanHistoryOnStart);
tbrp.setCleanLogsByLastModifiedDate(cleanLogsByLastModifiedDate);
tbrp.timeBasedFileNamingAndTriggeringPolicy = tbfnatp;
tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(cp.simulatedTime);
tbrp.start();
Expand All @@ -440,10 +461,13 @@ void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart)

boolean DO_CLEAN_HISTORY_ON_START = true;
boolean DO_NOT_CLEAN_HISTORY_ON_START = false;
boolean DO_CLEAN_LOGS_BY_LAST_MODIFIED_DATE = true;
boolean DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE = false;


long logOverMultiplePeriods(ConfigParameters cp) {

buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START);
buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START, DO_NOT_CLEAN_LOGS_BY_LAST_MODIFIED_DATE);

int runLength = cp.simulatedNumberOfPeriods * ticksPerPeriod;
int startInactivityIndex = cp.startInactivity * ticksPerPeriod;
Expand Down

0 comments on commit ed5b7a5

Please sign in to comment.