From 454797df5c0d022eff8e1a51453dcd263edba6f3 Mon Sep 17 00:00:00 2001 From: Arun Gopalpuri Date: Tue, 16 Dec 2014 21:14:59 -0800 Subject: [PATCH 1/3] LOG4J2-524: auto-delete older rolled over files Deletes files older than maxAge, if not configured defaults to 14 days. --- .../core/appender/RollingFileAppender.java | 2 +- .../RollingRandomAccessFileAppender.java | 2 +- .../rolling/DefaultRolloverStrategy.java | 100 ++++++++++++++++-- 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java index 8c3870683c9..b7360ccfa29 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java @@ -173,7 +173,7 @@ public static RollingFileAppender createAppender( } if (strategy == null) { - strategy = DefaultRolloverStrategy.createStrategy(null, null, null, + strategy = DefaultRolloverStrategy.createStrategy(null, null, null, null, String.valueOf(Deflater.DEFAULT_COMPRESSION), config); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java index a9493fe45bc..86893d63045 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java @@ -195,7 +195,7 @@ public static RollingRandomAccessFileAppender createAppender( } if (strategy == null) { - strategy = DefaultRolloverStrategy.createStrategy(null, null, null, + strategy = DefaultRolloverStrategy.createStrategy(null, null, null, null, String.valueOf(Deflater.DEFAULT_COMPRESSION), config); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java index de09cef55f6..d4fc9498fcf 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java @@ -35,6 +35,12 @@ import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.status.StatusLogger; +import java.io.FileFilter; +import java.nio.file.Files; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Calendar; +import java.util.Date; + /** * When rolling over, DefaultRolloverStrategy renames files * according to an algorithm as described below. @@ -87,11 +93,13 @@ public class DefaultRolloverStrategy implements RolloverStrategy { private static final int MIN_WINDOW_SIZE = 1; private static final int DEFAULT_WINDOW_SIZE = 7; + private static final int DEFAULT_MAX_AGE = 14; /** * Create the DefaultRolloverStrategy. * @param max The maximum number of files to keep. * @param min The minimum number of files to keep. + * @param maxAge The maximum days to keep files. * @param fileIndex If set to "max" (the default), files with a higher index will be newer than files with a * smaller index. If set to "min", file renaming and the counter will follow the Fixed Window strategy. * @param compressionLevelStr The compression level, 0 (less) through 9 (more); applies only to ZIP files. @@ -102,6 +110,7 @@ public class DefaultRolloverStrategy implements RolloverStrategy { public static DefaultRolloverStrategy createStrategy( @PluginAttribute("max") final String max, @PluginAttribute("min") final String min, + @PluginAttribute("maxAge") final String maxAge, @PluginAttribute("fileIndex") final String fileIndex, @PluginAttribute("compressionLevel") final String compressionLevelStr, @PluginConfiguration final Configuration config) { @@ -122,8 +131,13 @@ public static DefaultRolloverStrategy createStrategy( LOGGER.error("Maximum window size must be greater than the minimum windows size. Set to " + maxIndex); } } + int maxAgeIndex = DEFAULT_MAX_AGE; + if (maxAge != null) { + maxAgeIndex = Integer.parseInt(maxAge); + } + final int compressionLevel = Integers.parseInt(compressionLevelStr, Deflater.DEFAULT_COMPRESSION); - return new DefaultRolloverStrategy(minIndex, maxIndex, useMax, compressionLevel, config.getStrSubstitutor()); + return new DefaultRolloverStrategy(minIndex, maxIndex, maxAgeIndex, useMax, compressionLevel, config.getStrSubstitutor()); } /** @@ -138,15 +152,16 @@ public static DefaultRolloverStrategy createStrategy( private final boolean useMax; private final StrSubstitutor subst; private final int compressionLevel; - + private final int maxAgeIndex; /** * Constructs a new instance. * @param minIndex The minimum index. * @param maxIndex The maximum index. */ - protected DefaultRolloverStrategy(final int minIndex, final int maxIndex, final boolean useMax, final int compressionLevel, final StrSubstitutor subst) { + protected DefaultRolloverStrategy(final int minIndex, final int maxIndex, final int maxAgeIndex, final boolean useMax, final int compressionLevel, final StrSubstitutor subst) { this.minIndex = minIndex; this.maxIndex = maxIndex; + this.maxAgeIndex = maxAgeIndex; this.useMax = useMax; this.compressionLevel = compressionLevel; this.subst = subst; @@ -164,20 +179,61 @@ public int getMinIndex() { return this.minIndex; } + public int getMaxAgeIndex() { + return this.maxAgeIndex; + } + private int purge(final int lowIndex, final int highIndex, final RollingFileManager manager) { return useMax ? purgeAscending(lowIndex, highIndex, manager) : purgeDescending(lowIndex, highIndex, manager); } /** - * Purge and rename old log files in preparation for rollover. The oldest file will have the smallest index, - * the newest the highest. - * - * @param lowIndex low index - * @param highIndex high index. Log file associated with high index will be deleted if needed. + * Purge files older than defined maxAge. If file older than current date - maxAge delete them or else keep it. + * @param maxAgeIndex maxAge Index * @param manager The RollingFileManager * @return true if purge was successful and rollover should be attempted. */ + private void purgeMaxAgeFiles(final int maxAgeIndex, final RollingFileManager manager) { + + final StringBuilder buf = new StringBuilder(); + + // LOG4J2-531: directory scan & rollover must use same format + String filename = manager.getFileName(); + File file = new File(filename); + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -maxAgeIndex); + Date cutoffDate = cal.getTime(); + + if (file.getParentFile().exists()) { + filename = file.getName().replaceAll("\\..*", ""); + + File[] files = file.getParentFile().listFiles( + new StartsWithFileFilter(filename, false)); + + for (int i = 0; i < files.length; i++) { + try { + BasicFileAttributes attr = Files.readAttributes(files[i].toPath(), BasicFileAttributes.class); + if(new Date(attr.creationTime().toMillis()).before(cutoffDate)){ + files[i].delete(); + } + } catch(Exception e) { + LOGGER.info("unable to get basic file attributes : ", e); + } + } + } + } + + + /** + * Purge and rename old log files in preparation for rollover. The oldest file will have the smallest index, + * the newest the highest. + * + * @param lowIndex low index + * @param highIndex high index. Log file associated with high index will be deleted if needed. + * @param manager The RollingFileManager + * @return true if purge was successful and rollover should be attempted. + */ private int purgeAscending(final int lowIndex, final int highIndex, final RollingFileManager manager) { int suffixLength = 0; @@ -396,6 +452,7 @@ private int purgeDescending(final int lowIndex, final int highIndex, final Rolli */ @Override public RolloverDescription rollover(final RollingFileManager manager) throws SecurityException { + purgeMaxAgeFiles(maxAgeIndex, manager); if (maxIndex < 0) { return null; } @@ -431,6 +488,33 @@ public RolloverDescription rollover(final RollingFileManager manager) throws Sec return new RolloverDescriptionImpl(currentFileName, false, renameAction, compressAction); } + class StartsWithFileFilter implements FileFilter { + private String startsWith; + private boolean inclDirs = false; + + /** + * + */ + public StartsWithFileFilter(String startsWith, + boolean includeDirectories) { + super(); + this.startsWith = startsWith.toUpperCase(); + inclDirs = includeDirectories; + } + + /* + * (non-Javadoc) + * + * @see java.io.FileFilter#accept(java.io.File) + */ + public boolean accept(File pathname) { + if (!inclDirs && pathname.isDirectory()) { + return false; + } else + return pathname.getName().toUpperCase().startsWith(startsWith); + } + } + @Override public String toString() { return "DefaultRolloverStrategy(min=" + minIndex + ", max=" + maxIndex + ')'; From 47ffb016603e9ba5a315fabe87f31b255e82e711 Mon Sep 17 00:00:00 2001 From: Arun Gopalpuri Date: Wed, 17 Dec 2014 12:27:54 -0800 Subject: [PATCH 2/3] Removed unnecessary StringBuffer Creation --- .../log4j/core/appender/rolling/DefaultRolloverStrategy.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java index d4fc9498fcf..2379096ad88 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java @@ -195,10 +195,6 @@ private int purge(final int lowIndex, final int highIndex, final RollingFileMana * @return true if purge was successful and rollover should be attempted. */ private void purgeMaxAgeFiles(final int maxAgeIndex, final RollingFileManager manager) { - - final StringBuilder buf = new StringBuilder(); - - // LOG4J2-531: directory scan & rollover must use same format String filename = manager.getFileName(); File file = new File(filename); Calendar cal = Calendar.getInstance(); From ee982bcbd22dbe1e4d14d16fdadf505b384d9aa0 Mon Sep 17 00:00:00 2001 From: Arun Gopalpuri Date: Sat, 20 Dec 2014 17:01:38 -0800 Subject: [PATCH 3/3] java doc updated and moved inner class to end. --- .../rolling/DefaultRolloverStrategy.java | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java index 2379096ad88..9b26aafb46f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java @@ -16,11 +16,6 @@ */ package org.apache.logging.log4j.core.appender.rolling; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.Deflater; - import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.appender.rolling.action.Action; import org.apache.logging.log4j.core.appender.rolling.action.FileRenameAction; @@ -35,11 +30,15 @@ import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.status.StatusLogger; +import java.io.File; import java.io.FileFilter; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.List; +import java.util.zip.Deflater; /** * When rolling over, DefaultRolloverStrategy renames files @@ -157,6 +156,7 @@ public static DefaultRolloverStrategy createStrategy( * Constructs a new instance. * @param minIndex The minimum index. * @param maxIndex The maximum index. + * @param maxAgeIndex The maximum age of log files. */ protected DefaultRolloverStrategy(final int minIndex, final int maxIndex, final int maxAgeIndex, final boolean useMax, final int compressionLevel, final StrSubstitutor subst) { this.minIndex = minIndex; @@ -192,7 +192,6 @@ private int purge(final int lowIndex, final int highIndex, final RollingFileMana * Purge files older than defined maxAge. If file older than current date - maxAge delete them or else keep it. * @param maxAgeIndex maxAge Index * @param manager The RollingFileManager - * @return true if purge was successful and rollover should be attempted. */ private void purgeMaxAgeFiles(final int maxAgeIndex, final RollingFileManager manager) { String filename = manager.getFileName(); @@ -214,7 +213,7 @@ private void purgeMaxAgeFiles(final int maxAgeIndex, final RollingFileManager ma files[i].delete(); } } catch(Exception e) { - LOGGER.info("unable to get basic file attributes : ", e); + LOGGER.warn("unable to get basic file attributes : ", e); } } } @@ -484,9 +483,14 @@ public RolloverDescription rollover(final RollingFileManager manager) throws Sec return new RolloverDescriptionImpl(currentFileName, false, renameAction, compressAction); } + @Override + public String toString() { + return "DefaultRolloverStrategy(min=" + minIndex + ", max=" + maxIndex + ", maxAgeIndex=" + maxAgeIndex + ')'; + } + class StartsWithFileFilter implements FileFilter { - private String startsWith; - private boolean inclDirs = false; + private final String startsWith; + private final boolean inclDirs; /** * @@ -510,10 +514,4 @@ public boolean accept(File pathname) { return pathname.getName().toUpperCase().startsWith(startsWith); } } - - @Override - public String toString() { - return "DefaultRolloverStrategy(min=" + minIndex + ", max=" + maxIndex + ')'; - } - }