LOG4J2-435 added support for nested conditions, and JUnit tests Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e9875dc6 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e9875dc6 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e9875dc6
Branch: refs/heads/master Commit: e9875dc684e478331af75f665e4fc8351a9893ce Parents: 4f0868c Author: rpopma <[email protected]> Authored: Sat Nov 28 23:24:24 2015 +0900 Committer: rpopma <[email protected]> Committed: Sat Nov 28 23:24:24 2015 +0900 ---------------------------------------------------------------------- .../rolling/action/IfAccumulatedFileCount.java | 30 ++++- .../rolling/action/IfAccumulatedFileSize.java | 30 ++++- .../core/appender/rolling/action/IfAll.java | 33 ++++- .../appender/rolling/action/IfFileName.java | 42 +++++-- .../appender/rolling/action/IfLastModified.java | 30 ++++- ...lingAppenderDeleteAccumulatedCount1Test.java | 3 +- ...lingAppenderDeleteAccumulatedCount2Test.java | 3 +- .../RollingAppenderDeleteNestedTest.java | 120 +++++++++++++++++++ .../action/IfAccumulatedFileCountTest.java | 25 ++++ .../action/IfAccumulatedFileSizeTest.java | 36 ++++++ .../appender/rolling/action/IfFileNameTest.java | 52 ++++++++ .../rolling/action/IfLastModifiedTest.java | 36 +++++- .../log4j-rolling-with-custom-delete-nested.xml | 51 ++++++++ 13 files changed, 460 insertions(+), 31 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCount.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCount.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCount.java index b411d50..6cdcc84 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCount.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCount.java @@ -18,10 +18,14 @@ package org.apache.logging.log4j.core.appender.rolling.action; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.status.StatusLogger; @@ -33,17 +37,24 @@ public final class IfAccumulatedFileCount implements PathCondition { private static final Logger LOGGER = StatusLogger.getLogger(); private final int threshold; private int count; + private final PathCondition[] nestedConditions; - private IfAccumulatedFileCount(final int thresholdParam) { + private IfAccumulatedFileCount(final int thresholdParam, final PathCondition[] nestedConditions) { if (thresholdParam <= 0) { throw new IllegalArgumentException("Count must be a positive integer but was " + thresholdParam); } this.threshold = thresholdParam; + this.nestedConditions = nestedConditions == null ? new PathCondition[0] : Arrays.copyOf(nestedConditions, + nestedConditions.length); } public int getThresholdCount() { return threshold; } + + public List<PathCondition> getNestedConditions() { + return Collections.unmodifiableList(Arrays.asList(nestedConditions)); + } /* * (non-Javadoc) @@ -52,10 +63,13 @@ public final class IfAccumulatedFileCount implements PathCondition { * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) */ @Override - public boolean accept(final Path baseDir, final Path relativePath, final BasicFileAttributes attrs) { + public boolean accept(final Path basePath, final Path relativePath, final BasicFileAttributes attrs) { final boolean result = ++count > threshold; final String match = result ? ">" : "<="; LOGGER.trace("IfAccumulatedFileCount: {} count '{}' {} threshold '{}'", relativePath, count, match, threshold); + if (result) { + return IfAll.accept(nestedConditions, basePath, relativePath, attrs); + } return result; } @@ -67,6 +81,7 @@ public final class IfAccumulatedFileCount implements PathCondition { @Override public void beforeFileTreeWalk() { count = 0; + IfAll.beforeFileTreeWalk(nestedConditions); } /** @@ -77,15 +92,20 @@ public final class IfAccumulatedFileCount implements PathCondition { */ @PluginFactory public static IfAccumulatedFileCount createFileCountCondition( // - @PluginAttribute(value = "exceeds", defaultInt = Integer.MAX_VALUE) final int threshold) { + // @formatter:off + @PluginAttribute(value = "exceeds", defaultInt = Integer.MAX_VALUE) final int threshold, + @PluginElement("PathConditions") final PathCondition... nestedConditions) { + // @formatter:on + if (threshold == Integer.MAX_VALUE) { LOGGER.error("IfAccumulatedFileCount invalid or missing threshold value."); } - return new IfAccumulatedFileCount(threshold); + return new IfAccumulatedFileCount(threshold, nestedConditions); } @Override public String toString() { - return "IfAccumulatedFileCount(exceeds=" + threshold + ")"; + final String nested = nestedConditions.length == 0 ? "" : " AND " + Arrays.toString(nestedConditions); + return "IfAccumulatedFileCount(exceeds=" + threshold + nested + ")"; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSize.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSize.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSize.java index c1378e6..e7a6bd0 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSize.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSize.java @@ -18,11 +18,15 @@ package org.apache.logging.log4j.core.appender.rolling.action; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.appender.rolling.FileSize; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.status.StatusLogger; @@ -34,17 +38,24 @@ public final class IfAccumulatedFileSize implements PathCondition { private static final Logger LOGGER = StatusLogger.getLogger(); private final long thresholdBytes; private long accumulatedSize; + private final PathCondition[] nestedConditions; - private IfAccumulatedFileSize(final long thresholdSize) { + private IfAccumulatedFileSize(final long thresholdSize, final PathCondition[] nestedConditions) { if (thresholdSize <= 0) { throw new IllegalArgumentException("Count must be a positive integer but was " + thresholdSize); } this.thresholdBytes = thresholdSize; + this.nestedConditions = nestedConditions == null ? new PathCondition[0] : Arrays.copyOf(nestedConditions, + nestedConditions.length); } public long getThresholdBytes() { return thresholdBytes; } + + public List<PathCondition> getNestedConditions() { + return Collections.unmodifiableList(Arrays.asList(nestedConditions)); + } /* * (non-Javadoc) @@ -53,12 +64,15 @@ public final class IfAccumulatedFileSize implements PathCondition { * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) */ @Override - public boolean accept(final Path baseDir, final Path relativePath, final BasicFileAttributes attrs) { + public boolean accept(final Path basePath, final Path relativePath, final BasicFileAttributes attrs) { accumulatedSize += attrs.size(); final boolean result = accumulatedSize > thresholdBytes; final String match = result ? ">" : "<="; LOGGER.trace("IfAccumulatedFileSize: {} accumulated size '{}' {} thresholdBytes '{}'", relativePath, accumulatedSize, match, thresholdBytes); + if (result) { + return IfAll.accept(nestedConditions, basePath, relativePath, attrs); + } return result; } @@ -70,6 +84,7 @@ public final class IfAccumulatedFileSize implements PathCondition { @Override public void beforeFileTreeWalk() { accumulatedSize = 0; + IfAll.beforeFileTreeWalk(nestedConditions); } /** @@ -80,16 +95,21 @@ public final class IfAccumulatedFileSize implements PathCondition { */ @PluginFactory public static IfAccumulatedFileSize createFileSizeCondition( // - @PluginAttribute("exceeds") final String size) { + // @formatter:off + @PluginAttribute("exceeds") final String size, + @PluginElement("PathConditions") final PathCondition... nestedConditions) { + // @formatter:on + if (size == null) { LOGGER.error("IfAccumulatedFileSize missing mandatory size threshold."); } final long threshold = size == null ? Long.MAX_VALUE : FileSize.parse(size, Long.MAX_VALUE); - return new IfAccumulatedFileSize(threshold); + return new IfAccumulatedFileSize(threshold, nestedConditions); } @Override public String toString() { - return "IfAccumulatedFileSize(exceeds=" + thresholdBytes + ")"; + final String nested = nestedConditions.length == 0 ? "" : " AND " + Arrays.toString(nestedConditions); + return "IfAccumulatedFileSize(exceeds=" + thresholdBytes + nested + ")"; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java index 84a9388..627a082 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java @@ -44,14 +44,31 @@ public final class IfAll implements PathCondition { /* * (non-Javadoc) - * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) + * + * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, + * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) */ @Override public boolean accept(final Path baseDir, final Path relativePath, final BasicFileAttributes attrs) { if (components == null || components.length == 0) { return false; // unconditional delete not supported } - for (final PathCondition component : components) { + return accept(components, baseDir, relativePath, attrs); + } + + /** + * Returns {@code true} if all the specified conditions accept the specified path, {@code false} otherwise. + * + * @param list the array of conditions to evaluate + * @param baseDir the directory from where to start scanning for deletion candidate files + * @param relativePath the candidate for deletion. This path is relative to the baseDir. + * @param attrs attributes of the candidate path + * @return {@code true} if all the specified conditions accept the specified path, {@code false} otherwise + * @throws NullPointerException if any of the parameters is {@code null} + */ + public static boolean accept(final PathCondition[] list, final Path baseDir, final Path relativePath, + final BasicFileAttributes attrs) { + for (final PathCondition component : list) { if (!component.accept(baseDir, relativePath, attrs)) { return false; } @@ -61,11 +78,21 @@ public final class IfAll implements PathCondition { /* * (non-Javadoc) + * * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#beforeFileTreeWalk() */ @Override public void beforeFileTreeWalk() { - for (PathCondition condition : components) { + beforeFileTreeWalk(components); + } + + /** + * Calls {@link #beforeFileTreeWalk()} on all of the specified nested conditions. + * + * @param nestedConditions the conditions to call {@link #beforeFileTreeWalk()} on + */ + public static void beforeFileTreeWalk(PathCondition[] nestedConditions) { + for (PathCondition condition : nestedConditions) { condition.beforeFileTreeWalk(); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileName.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileName.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileName.java index a746a20..9bed9f7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileName.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileName.java @@ -21,11 +21,15 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.regex.Pattern; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.status.StatusLogger; @@ -34,14 +38,15 @@ import org.apache.logging.log4j.status.StatusLogger; * expression. If both a regular expression and a glob pattern are specified the glob pattern is used and the regular * expression is ignored. * <p> - * The regular expression is a pattern as defined by the {@link Pattern} class. A glob is a simplified pattern expression - * described in {@link FileSystem#getPathMatcher(String)}. + * The regular expression is a pattern as defined by the {@link Pattern} class. A glob is a simplified pattern + * expression described in {@link FileSystem#getPathMatcher(String)}. */ @Plugin(name = "IfFileName", category = "Core", printObject = true) public final class IfFileName implements PathCondition { private static final Logger LOGGER = StatusLogger.getLogger(); private final PathMatcher pathMatcher; private final String syntaxAndPattern; + private final PathCondition[] nestedConditions; /** * Constructs a FileNameFilter filter. If both a regular expression and a glob pattern are specified the glob @@ -49,14 +54,17 @@ public final class IfFileName implements PathCondition { * * @param glob the baseDir-relative path pattern of the files to delete (may contain '*' and '?' wildcarts) * @param regex the regular expression that matches the baseDir-relative path of the file(s) to delete + * @param nestedConditions nested conditions to evaluate if this condition accepts a path */ - private IfFileName(final String glob, final String regex) { + private IfFileName(final String glob, final String regex, final PathCondition[] nestedConditions) { if (regex == null && glob == null) { throw new IllegalArgumentException("Specify either a path glob or a regular expression. " + "Both cannot be null."); } - syntaxAndPattern = createSyntaxAndPatternString(glob, regex); - pathMatcher = FileSystems.getDefault().getPathMatcher(syntaxAndPattern); + this.syntaxAndPattern = createSyntaxAndPatternString(glob, regex); + this.pathMatcher = FileSystems.getDefault().getPathMatcher(syntaxAndPattern); + this.nestedConditions = nestedConditions == null ? new PathCondition[0] : Arrays.copyOf(nestedConditions, + nestedConditions.length); } static String createSyntaxAndPatternString(final String glob, final String regex) { @@ -77,10 +85,16 @@ public final class IfFileName implements PathCondition { public String getSyntaxAndPattern() { return syntaxAndPattern; } + + public List<PathCondition> getNestedConditions() { + return Collections.unmodifiableList(Arrays.asList(nestedConditions)); + } /* * (non-Javadoc) - * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) + * + * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, + * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) */ @Override public boolean accept(final Path basePath, final Path relativePath, final BasicFileAttributes attrs) { @@ -88,15 +102,20 @@ public final class IfFileName implements PathCondition { final String match = result ? " " : " not "; LOGGER.trace("IfFileName: '{}' does{}match relative path '{}'", syntaxAndPattern, match, relativePath); + if (result) { + return IfAll.accept(nestedConditions, basePath, relativePath, attrs); + } return result; } /* * (non-Javadoc) + * * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#beforeFileTreeWalk() */ @Override public void beforeFileTreeWalk() { + IfAll.beforeFileTreeWalk(nestedConditions); } /** @@ -107,18 +126,23 @@ public final class IfFileName implements PathCondition { * * @param glob the baseDir-relative path pattern of the files to delete (may contain '*' and '?' wildcarts) * @param regex the regular expression that matches the baseDir-relative path of the file(s) to delete + * @param nestedConditions nested conditions to evaluate if this condition accepts a path * @return A IfFileName condition. * @see FileSystem#getPathMatcher(String) */ @PluginFactory public static IfFileName createNameCondition( // + // @formatter:off @PluginAttribute("glob") final String glob, // - @PluginAttribute("regex") final String regex) { - return new IfFileName(glob, regex); + @PluginAttribute("regex") final String regex, // + @PluginElement("PathConditions") final PathCondition... nestedConditions) { + // @formatter:on + return new IfFileName(glob, regex, nestedConditions); } @Override public String toString() { - return "IfFileName(" + syntaxAndPattern + ")"; + final String nested = nestedConditions.length == 0 ? "" : " AND " + Arrays.toString(nestedConditions); + return "IfFileName(" + syntaxAndPattern + nested + ")"; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModified.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModified.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModified.java index 064f702..a67dba9 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModified.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModified.java @@ -19,11 +19,15 @@ package org.apache.logging.log4j.core.appender.rolling.action; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.util.Clock; import org.apache.logging.log4j.core.util.ClockFactory; @@ -38,27 +42,37 @@ public final class IfLastModified implements PathCondition { private static final Clock CLOCK = ClockFactory.getClock(); private final Duration age; + private final PathCondition[] nestedConditions; - private IfLastModified(final Duration age) { + private IfLastModified(final Duration age, final PathCondition[] nestedConditions) { this.age = Objects.requireNonNull(age, "age"); + this.nestedConditions = nestedConditions == null ? new PathCondition[0] : Arrays.copyOf(nestedConditions, + nestedConditions.length); } public Duration getAge() { return age; } + + public List<PathCondition> getNestedConditions() { + return Collections.unmodifiableList(Arrays.asList(nestedConditions)); + } /* * (non-Javadoc) * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) */ @Override - public boolean accept(final Path baseDir, final Path relativePath, final BasicFileAttributes attrs) { + public boolean accept(final Path basePath, final Path relativePath, final BasicFileAttributes attrs) { final FileTime fileTime = attrs.lastModifiedTime(); final long millis = fileTime.toMillis(); final long ageMillis = CLOCK.currentTimeMillis() - millis; final boolean result = ageMillis >= age.toMillis(); final String match = result ? ">=" : "<"; LOGGER.trace("IfLastModified: {} ageMillis '{}' {} '{}'", relativePath, ageMillis, match, age); + if (result) { + return IfAll.accept(nestedConditions, basePath, relativePath, attrs); + } return result; } @@ -68,22 +82,28 @@ public final class IfLastModified implements PathCondition { */ @Override public void beforeFileTreeWalk() { + IfAll.beforeFileTreeWalk(nestedConditions); } /** * Create an IfLastModified condition. * * @param age The path age that is accepted by this condition. Must be a valid Duration. + * @param nestedConditions nested conditions to evaluate if this condition accepts a path * @return An IfLastModified condition. */ @PluginFactory public static IfLastModified createAgeCondition( // - @PluginAttribute("age") final Duration age) { - return new IfLastModified(age); + // @formatter:off + @PluginAttribute("age") final Duration age, // + @PluginElement("PathConditions") final PathCondition... nestedConditions) { + // @formatter:on + return new IfLastModified(age, nestedConditions); } @Override public String toString() { - return "IfLastModified(age=" + age + ")"; + final String nested = nestedConditions.length == 0 ? "" : " AND " + Arrays.toString(nestedConditions); + return "IfLastModified(age=" + age + nested + ")"; } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java index 2206b63..b2d6fa7 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount1Test.java @@ -39,7 +39,8 @@ import org.junit.rules.RuleChain; import static org.junit.Assert.*; /** - * + * Tests that sibling conditions are invoked in configured order. + * This does not work for properties configurations. Use nested conditions instead. */ public class RollingAppenderDeleteAccumulatedCount1Test { private static final String CONFIG = "log4j-rolling-with-custom-delete-accum-count1.xml"; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java index 9f85eba..d63491e 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteAccumulatedCount2Test.java @@ -39,7 +39,8 @@ import org.junit.rules.RuleChain; import static org.junit.Assert.*; /** - * + * Tests that sibling conditions are invoked in configured order. + * This does not work for properties configurations. Use nested conditions instead. */ public class RollingAppenderDeleteAccumulatedCount2Test { private static final String CONFIG = "log4j-rolling-with-custom-delete-accum-count2.xml"; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java new file mode 100644 index 0000000..59fc3c9 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDeleteNestedTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.appender.rolling; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; +import java.util.Arrays; +import java.util.List; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.util.datetime.FixedDateFormat; +import org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExternalResource; +import org.junit.rules.RuleChain; + +import static org.junit.Assert.*; + +/** + * + */ +public class RollingAppenderDeleteNestedTest { + private static final String CONFIG = "log4j-rolling-with-custom-delete-nested.xml"; + private static final String DIR = "target/rolling-with-delete-nested/test"; + + private final LoggerContextRule ctx = new LoggerContextRule(CONFIG); + + @Rule + public RuleChain chain = RuleChain.outerRule(new ExternalResource() { + @Override + protected void before() throws Throwable { + deleteDir(); + } + }).around(ctx); + + @Test + public void testAppender() throws Exception { + Path p1 = writeTextTo(DIR + "/my-1.log"); // glob="test-*.log" + Path p2 = writeTextTo(DIR + "/my-2.log"); + Path p3 = writeTextTo(DIR + "/my-3.log"); + Path p4 = writeTextTo(DIR + "/my-4.log"); + Path p5 = writeTextTo(DIR + "/my-5.log"); + + final Logger logger = ctx.getLogger(); + for (int i = 0; i < 10; ++i) { + updateLastModified(p1, p2, p3, p4, p5); // make my-*.log files most recent + + // 30 chars per message: each message triggers a rollover + logger.debug("This is a test message number " + i); // 30 chars: + } + Thread.sleep(100); // Allow time for rollover to complete + + final File dir = new File(DIR); + assertTrue("Dir " + DIR + " should exist", dir.exists()); + assertTrue("Dir " + DIR + " should contain files", dir.listFiles().length > 0); + + final File[] files = dir.listFiles(); + for (File file : files) { + System.out.println(file + " (" + file.length() + "B) " + + FixedDateFormat.create(FixedFormat.ABSOLUTE).format(file.lastModified())); + } + + List<String> expected = Arrays.asList("test-7.log", "test-8.log", "test-9.log", "test-10.log", + "my-1.log", "my-2.log", "my-3.log", "my-4.log", "my-5.log"); + assertEquals(Arrays.toString(files), expected.size() - 1, files.length); + for (File file : files) { + assertTrue("unexpected file " + file, expected.contains(file.getName())); + } + } + + private void updateLastModified(Path... paths) throws IOException { + for (Path path : paths) { + Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis() + 100)); + } + } + + private Path writeTextTo(String location) throws IOException { + Path path = Paths.get(location); + Files.createDirectories(path.getParent()); + try (BufferedWriter buffy = Files.newBufferedWriter(path, Charset.defaultCharset())) { + buffy.write("some text"); + buffy.newLine(); + buffy.flush(); + } + return path; + } + + private static void deleteDir() { + final File dir = new File(DIR); + if (dir.exists()) { + final File[] files = dir.listFiles(); + for (final File file : files) { + file.delete(); + } + dir.delete(); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCountTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCountTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCountTest.java index 343aa09..bb2a0d0 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCountTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileCountTest.java @@ -47,4 +47,29 @@ public class IfAccumulatedFileCountTest { } } + @Test + public void testAcceptCallsNestedConditionsOnlyIfPathAccepted() { + final CountingCondition counter = new CountingCondition(true); + IfAccumulatedFileCount condition = IfAccumulatedFileCount.createFileCountCondition(3, counter); + + for (int i = 1; i < 10; i++) { + if (i <= 3) { + assertFalse("i=" + i, condition.accept(null, null, null)); + assertEquals(0, counter.getAcceptCount()); + } else { + assertTrue(condition.accept(null, null, null)); + assertEquals(i - 3, counter.getAcceptCount()); + } + } + } + + @Test + public void testBeforeTreeWalk() { + final CountingCondition counter = new CountingCondition(true); + final IfAccumulatedFileCount filter = IfAccumulatedFileCount.createFileCountCondition(30, counter, counter, + counter); + filter.beforeFileTreeWalk(); + assertEquals(3, counter.getBeforeFileTreeWalkCount()); + } + } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSizeTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSizeTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSizeTest.java index d756a95..d22df55 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSizeTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfAccumulatedFileSizeTest.java @@ -91,4 +91,40 @@ public class IfAccumulatedFileSizeTest { } } + @Test + public void testAcceptCallsNestedConditionsOnlyIfPathAccepted() { + final CountingCondition counter = new CountingCondition(true); + IfAccumulatedFileSize condition = IfAccumulatedFileSize.createFileSizeCondition("2KB", counter); + DummyFileAttributes attribs = new DummyFileAttributes(); + + long quarter = condition.getThresholdBytes() / 4; + attribs.size = quarter; + assertFalse(condition.accept(null, null, attribs)); + assertEquals(0, counter.getAcceptCount()); + + assertFalse(condition.accept(null, null, attribs)); + assertEquals(0, counter.getAcceptCount()); + + assertFalse(condition.accept(null, null, attribs)); + assertEquals(0, counter.getAcceptCount()); + + assertFalse(condition.accept(null, null, attribs)); + assertEquals(0, counter.getAcceptCount()); + + assertTrue(condition.accept(null, null, attribs)); + assertEquals(1, counter.getAcceptCount()); + + assertTrue(condition.accept(null, null, attribs)); + assertEquals(2, counter.getAcceptCount()); + } + + @Test + public void testBeforeTreeWalk() { + final CountingCondition counter = new CountingCondition(true); + final IfAccumulatedFileSize filter = IfAccumulatedFileSize.createFileSizeCondition("2GB", counter, counter, + counter); + filter.beforeFileTreeWalk(); + assertEquals(3, counter.getBeforeFileTreeWalkCount()); + } + } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileNameTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileNameTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileNameTest.java index 5b81cfb..2e84aa9 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileNameTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfFileNameTest.java @@ -76,4 +76,56 @@ public class IfFileNameTest { final Path pathMatchingRegex = Paths.get("regex"); assertTrue(regexFilter.accept(null, pathMatchingRegex, null)); } + + @Test + public void testAcceptCallsNestedConditionsOnlyIfPathAccepted1() { + final CountingCondition counter = new CountingCondition(true); + final IfFileName regexFilter = IfFileName.createNameCondition(null, "regex", counter); + final Path pathMatchingRegex = Paths.get("regex"); + + assertTrue(regexFilter.accept(null, pathMatchingRegex, null)); + assertEquals(1, counter.getAcceptCount()); + assertTrue(regexFilter.accept(null, pathMatchingRegex, null)); + assertEquals(2, counter.getAcceptCount()); + assertTrue(regexFilter.accept(null, pathMatchingRegex, null)); + assertEquals(3, counter.getAcceptCount()); + + final Path noMatch = Paths.get("nomatch"); + assertFalse(regexFilter.accept(null, noMatch, null)); + assertEquals(3, counter.getAcceptCount()); // no increase + assertFalse(regexFilter.accept(null, noMatch, null)); + assertEquals(3, counter.getAcceptCount()); + assertFalse(regexFilter.accept(null, noMatch, null)); + assertEquals(3, counter.getAcceptCount()); + } + + @Test + public void testAcceptCallsNestedConditionsOnlyIfPathAccepted2() { + final CountingCondition counter = new CountingCondition(true); + final IfFileName globFilter = IfFileName.createNameCondition("glob", null, counter); + final Path pathMatchingGlob = Paths.get("glob"); + + assertTrue(globFilter.accept(null, pathMatchingGlob, null)); + assertEquals(1, counter.getAcceptCount()); + assertTrue(globFilter.accept(null, pathMatchingGlob, null)); + assertEquals(2, counter.getAcceptCount()); + assertTrue(globFilter.accept(null, pathMatchingGlob, null)); + assertEquals(3, counter.getAcceptCount()); + + final Path noMatch = Paths.get("nomatch"); + assertFalse(globFilter.accept(null, noMatch, null)); + assertEquals(3, counter.getAcceptCount()); // no increase + assertFalse(globFilter.accept(null, noMatch, null)); + assertEquals(3, counter.getAcceptCount()); + assertFalse(globFilter.accept(null, noMatch, null)); + assertEquals(3, counter.getAcceptCount()); + } + + @Test + public void testBeforeTreeWalk() { + final CountingCondition counter = new CountingCondition(true); + final IfFileName pathFilter = IfFileName.createNameCondition("path", null, counter, counter, counter); + pathFilter.beforeFileTreeWalk(); + assertEquals(3, counter.getBeforeFileTreeWalkCount()); + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModifiedTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModifiedTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModifiedTest.java index b23ca68..d279660 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModifiedTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/IfLastModifiedTest.java @@ -19,8 +19,6 @@ package org.apache.logging.log4j.core.appender.rolling.action; import java.nio.file.attribute.FileTime; -import org.apache.logging.log4j.core.appender.rolling.action.Duration; -import org.apache.logging.log4j.core.appender.rolling.action.IfLastModified; import org.junit.Test; import static org.junit.Assert.*; @@ -62,4 +60,38 @@ public class IfLastModifiedTest { attrs.lastModified = FileTime.fromMillis(System.currentTimeMillis() - age); assertFalse(filter.accept(null, null, attrs)); } + + @Test + public void testAcceptCallsNestedConditionsOnlyIfPathAccepted() { + final CountingCondition counter = new CountingCondition(true); + IfLastModified filter = IfLastModified.createAgeCondition(Duration.parse("PT33S"), counter); + DummyFileAttributes attrs = new DummyFileAttributes(); + final long oldEnough = 33 * 1000 + 5; + attrs.lastModified = FileTime.fromMillis(System.currentTimeMillis() - oldEnough); + + assertTrue(filter.accept(null, null, attrs)); + assertEquals(1, counter.getAcceptCount()); + assertTrue(filter.accept(null, null, attrs)); + assertEquals(2, counter.getAcceptCount()); + assertTrue(filter.accept(null, null, attrs)); + assertEquals(3, counter.getAcceptCount()); + + final long tooYoung = 33 * 1000 - 5; + attrs.lastModified = FileTime.fromMillis(System.currentTimeMillis() - tooYoung); + assertFalse(filter.accept(null, null, attrs)); + assertEquals(3, counter.getAcceptCount()); // no increase + assertFalse(filter.accept(null, null, attrs)); + assertEquals(3, counter.getAcceptCount()); + assertFalse(filter.accept(null, null, attrs)); + assertEquals(3, counter.getAcceptCount()); + } + + @Test + public void testBeforeTreeWalk() { + final CountingCondition counter = new CountingCondition(true); + final IfLastModified filter = IfLastModified.createAgeCondition(Duration.parse("PT33S"), counter, counter, + counter); + filter.beforeFileTreeWalk(); + assertEquals(3, counter.getBeforeFileTreeWalkCount()); + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e9875dc6/log4j-core/src/test/resources/log4j-rolling-with-custom-delete-nested.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j-rolling-with-custom-delete-nested.xml b/log4j-core/src/test/resources/log4j-rolling-with-custom-delete-nested.xml new file mode 100644 index 0000000..cde178d --- /dev/null +++ b/log4j-core/src/test/resources/log4j-rolling-with-custom-delete-nested.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +<Configuration status="TRACE" name="RollingWithCustomDeleteAccumulatedSizeTest"> + <Properties> + <Property name="base">target/rolling-with-delete-nested/</Property> + </Properties> + + <Appenders> + <RollingFile name="RollingFile" fileName="${base}/rollingtest.log" + filePattern="${base}/test/test-%i.log"> + <PatternLayout> + <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> + </PatternLayout> + <Policies> + <SizeBasedTriggeringPolicy size="30" /> + </Policies> + <DefaultRolloverStrategy max="100" stopCustomActionsOnError="true"> + <Delete basePath="${base}/test"> + <!-- delete only test-*.logs, keep the 3 most recent ones --> + <IfFileName glob="test-*.log"> + <!-- nesting guarantees that count not incremented if name does not match --> + <IfAccumulatedFileCount exceeds="3" /> + </IfFileName> + </Delete> + </DefaultRolloverStrategy> + </RollingFile> + </Appenders> + + <Loggers> + <Root level="trace"> + <AppenderRef ref="RollingFile" /> + </Root> + </Loggers> + +</Configuration> \ No newline at end of file
