You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/06/20 14:50:05 UTC
[logging-log4j2] 01/02: [LOG4J2-2403] Allow zero padding the
counter of a RollingFileAppender
This is an automated email from the ASF dual-hosted git repository.
rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 420c737dd701ddd644fed3ba4447ec74696aad0d
Author: Marco Herrn <ma...@mherrn.de>
AuthorDate: Tue Oct 23 14:44:03 2018 +0200
[LOG4J2-2403] Allow zero padding the counter of a RollingFileAppender
- Introduce a pattern for zero-padding (e.g. %03i) in file names
- This pattern also works for log patterns (e.g. %08sn)
---
.../appender/rolling/AbstractRolloverStrategy.java | 4 +-
.../logging/log4j/core/pattern/FormattingInfo.java | 48 +++++++++++++++-
.../logging/log4j/core/pattern/PatternParser.java | 18 ++++--
.../appender/rolling/RolloverFilePatternTest.java | 59 +++++++++++++++++++
.../appender/rolling/RolloverWithPaddingTest.java | 67 ++++++++++++++++++++++
.../SequenceNumberPatternConverterTest.java | 25 +++++++-
.../SequenceNumberPatternConverterTest.yml | 13 +++--
.../test/resources/log4j-rolling-with-padding.xml | 42 ++++++++++++++
8 files changed, 261 insertions(+), 15 deletions(-)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/AbstractRolloverStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/AbstractRolloverStrategy.java
index 741afb8..a69e220 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/AbstractRolloverStrategy.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/AbstractRolloverStrategy.java
@@ -46,6 +46,8 @@ public abstract class AbstractRolloverStrategy implements RolloverStrategy {
*/
protected static final Logger LOGGER = StatusLogger.getLogger();
+ public static final Pattern PATTERN_COUNTER= Pattern.compile(".*%((?<ZEROPAD>0)?(?<PADDING>\\d+))?i.*");
+
protected final StrSubstitutor strSubstitutor;
protected AbstractRolloverStrategy(final StrSubstitutor strSubstitutor) {
@@ -105,7 +107,7 @@ public abstract class AbstractRolloverStrategy implements RolloverStrategy {
} else {
parent.mkdirs();
}
- if (!logfilePattern.contains("%i")) {
+ if (!PATTERN_COUNTER.matcher(logfilePattern).matches()) {
return eligibleFiles;
}
final Path dir = parent.toPath();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java
index 0b9f2b9..bd88679 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java
@@ -30,6 +30,11 @@ public final class FormattingInfo {
private static final char[] SPACES = new char[] { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
/**
+ * Array of zeros.
+ */
+ private static final char[] ZEROS = new char[] { '0', '0', '0', '0', '0', '0', '0', '0' };
+
+ /**
* Default instance.
*/
private static final FormattingInfo DEFAULT = new FormattingInfo(false, 0, Integer.MAX_VALUE, true);
@@ -55,6 +60,11 @@ public final class FormattingInfo {
private final boolean leftTruncate;
/**
+ * Use zero-padding instead whitespace padding
+ */
+ private final boolean zeroPad;
+
+ /**
* Creates new instance.
*
* @param leftAlign
@@ -67,10 +77,29 @@ public final class FormattingInfo {
* truncates to the left if true
*/
public FormattingInfo(final boolean leftAlign, final int minLength, final int maxLength, final boolean leftTruncate) {
+ this(leftAlign, minLength, maxLength, leftTruncate, false);
+ }
+
+ /**
+ * Creates new instance.
+ *
+ * @param leftAlign
+ * left align if true.
+ * @param minLength
+ * minimum length.
+ * @param maxLength
+ * maximum length.
+ * @param leftTruncate
+ * truncates to the left if true
+ * @param zeroPad
+ * use zero-padding instead of whitespace-padding
+ */
+ public FormattingInfo(final boolean leftAlign, final int minLength, final int maxLength, final boolean leftTruncate, final boolean zeroPad) {
this.leftAlign = leftAlign;
this.minLength = minLength;
this.maxLength = maxLength;
this.leftTruncate = leftTruncate;
+ this.zeroPad = zeroPad;
}
/**
@@ -101,6 +130,15 @@ public final class FormattingInfo {
}
/**
+ * Determine if zero-padded.
+ *
+ * @return true if zero-padded.
+ */
+ public boolean isZeroPad() {
+ return zeroPad;
+ }
+
+ /**
* Get minimum length.
*
* @return minimum length.
@@ -146,11 +184,13 @@ public final class FormattingInfo {
} else {
int padLength = minLength - rawLength;
- for (; padLength > SPACES.length; padLength -= SPACES.length) {
- buffer.insert(fieldStart, SPACES);
+ final char[] paddingArray= zeroPad ? ZEROS : SPACES;
+
+ for (; padLength > paddingArray.length; padLength -= paddingArray.length) {
+ buffer.insert(fieldStart, paddingArray);
}
- buffer.insert(fieldStart, SPACES, 0, padLength);
+ buffer.insert(fieldStart, paddingArray, 0, padLength);
}
}
}
@@ -172,6 +212,8 @@ public final class FormattingInfo {
sb.append(minLength);
sb.append(", leftTruncate=");
sb.append(leftTruncate);
+ sb.append(", zeroPad=");
+ sb.append(zeroPad);
sb.append(']');
return sb.toString();
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
index 2e854e9..96aa819 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java
@@ -396,9 +396,15 @@ public final class PatternParser {
currentLiteral.append(c);
switch (c) {
+ case '0':
+ // a '0' directly after the % sign indicates zero-padding
+ formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
+ formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate(), true);
+ break;
+
case '-':
formattingInfo = new FormattingInfo(true, formattingInfo.getMinLength(),
- formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate());
+ formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate(), formattingInfo.isZeroPad());
break;
case '.':
@@ -409,7 +415,7 @@ public final class PatternParser {
if (c >= '0' && c <= '9') {
formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), c - '0',
- formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate());
+ formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate(), formattingInfo.isZeroPad());
state = ParserState.MIN_STATE;
} else {
i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules,
@@ -430,7 +436,7 @@ public final class PatternParser {
if (c >= '0' && c <= '9') {
// Multiply the existing value and add the value of the number just encountered.
formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength()
- * DECIMAL + c - '0', formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate());
+ * DECIMAL + c - '0', formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate(), formattingInfo.isZeroPad());
} else if (c == '.') {
state = ParserState.DOT_STATE;
} else {
@@ -448,14 +454,14 @@ public final class PatternParser {
switch (c) {
case '-':
formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
- formattingInfo.getMaxLength(),false);
+ formattingInfo.getMaxLength(),false, formattingInfo.isZeroPad());
break;
default:
if (c >= '0' && c <= '9') {
formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
- c - '0', formattingInfo.isLeftTruncate());
+ c - '0', formattingInfo.isLeftTruncate(), formattingInfo.isZeroPad());
state = ParserState.MAX_STATE;
} else {
LOGGER.error("Error occurred in position " + i + ".\n Was expecting digit, instead got char \"" + c
@@ -473,7 +479,7 @@ public final class PatternParser {
if (c >= '0' && c <= '9') {
// Multiply the existing value and add the value of the number just encountered.
formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
- formattingInfo.getMaxLength() * DECIMAL + c - '0', formattingInfo.isLeftTruncate());
+ formattingInfo.getMaxLength() * DECIMAL + c - '0', formattingInfo.isLeftTruncate(), formattingInfo.isZeroPad());
} else {
i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules,
patternConverters, formattingInfos, disableAnsi, noConsoleNoAnsi, convertBackslashes);
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RolloverFilePatternTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RolloverFilePatternTest.java
new file mode 100644
index 0000000..7d7ba91
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RolloverFilePatternTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util.regex.Matcher;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test getEligibleFiles method.
+ */
+public class RolloverFilePatternTest {
+
+ @Test
+ public void testFilePatternWithoutPadding() throws Exception {
+ final Matcher matcher = AbstractRolloverStrategy.PATTERN_COUNTER.matcher("target/logs/test-%i.log.gz");
+ assertTrue(matcher.matches());
+ assertNull(matcher.group("ZEROPAD"));
+ assertNull(matcher.group("PADDING"));
+ }
+
+ @Test
+ public void testFilePatternWithSpacePadding() throws Exception {
+ final Matcher matcher = AbstractRolloverStrategy.PATTERN_COUNTER.matcher("target/logs/test-%3i.log.gz");
+ assertTrue(matcher.matches());
+ assertNull(matcher.group("ZEROPAD"));
+ assertEquals("3", matcher.group("PADDING"));
+ }
+
+ @Test
+ public void testFilePatternWithZeroPadding() throws Exception {
+ final Matcher matcher = AbstractRolloverStrategy.PATTERN_COUNTER.matcher("target/logs/test-%03i.log.gz");
+ assertTrue(matcher.matches());
+ assertEquals("0", matcher.group("ZEROPAD"));
+ assertEquals("3", matcher.group("PADDING"));
+ }
+
+ @Test
+ public void testFilePatternUnmatched() throws Exception {
+ final Matcher matcher = AbstractRolloverStrategy.PATTERN_COUNTER.matcher("target/logs/test-%n.log.gz");
+ assertFalse(matcher.matches());
+ }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RolloverWithPaddingTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RolloverWithPaddingTest.java
new file mode 100644
index 0000000..7b0fb22
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RolloverWithPaddingTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+
+/**
+ * Tests that zero-padding in rolled files works correctly.
+ */
+public class RolloverWithPaddingTest {
+ private static final String CONFIG = "log4j-rolling-with-padding.xml";
+ private static final String DIR = "target/rolling-with-padding";
+
+ private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG);
+
+ @Rule
+ public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR);
+
+ @Test
+ public void testAppender() throws Exception {
+ final Logger logger = loggerContextRule.getLogger();
+ for (int i = 0; i < 10; ++i) {
+ // 30 chars per message: each message triggers a rollover
+ logger.fatal("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 == 6);
+
+ final File[] files = dir.listFiles();
+ final List<String> expected = Arrays.asList("rollingtest.log", "test-001.log", "test-002.log", "test-003.log", "test-004.log", "test-005.log");
+ assertEquals("Unexpected number of files", expected.size(), files.length);
+ for (final File file : files) {
+ if (!expected.contains(file.getName())) {
+ fail("unexpected file" + file);
+ }
+ }
+ }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverterTest.java
index b3fe206..4043e1d 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverterTest.java
@@ -21,11 +21,12 @@ import java.util.List;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.junit.LoggerContextRule;
import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.ClassRule;
import org.junit.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.ClassRule;
/**
*
@@ -35,6 +36,12 @@ public class SequenceNumberPatternConverterTest {
@ClassRule
public static LoggerContextRule ctx = new LoggerContextRule("SequenceNumberPatternConverterTest.yml");
+ @Before
+ public void before() {
+ ctx.getListAppender("List").clear();
+ ctx.getListAppender("Padded").clear();
+ }
+
@Test
public void testSequenceIncreases() throws Exception {
final Logger logger = ctx.getLogger();
@@ -46,6 +53,22 @@ public class SequenceNumberPatternConverterTest {
final ListAppender app = ctx.getListAppender("List");
final List<String> messages = app.getMessages();
+ System.out.println("Written messages: "+messages);
assertThat(messages, contains("1", "2", "3", "4", "5"));
}
+
+ @Test
+ public void testPaddedSequence() throws Exception {
+ final Logger logger = ctx.getLogger();
+ logger.info("Message 1");
+ logger.info("Message 2");
+ logger.info("Message 3");
+ logger.info("Message 4");
+ logger.info("Message 5");
+
+ final ListAppender app = ctx.getListAppender("Padded");
+ final List<String> messages = app.getMessages();
+ System.out.println("Written messages "+messages);
+ assertThat(messages, contains("001", "002", "003", "004", "005"));
+ }
}
diff --git a/log4j-core/src/test/resources/SequenceNumberPatternConverterTest.yml b/log4j-core/src/test/resources/SequenceNumberPatternConverterTest.yml
index 05f0b05..64698cf 100644
--- a/log4j-core/src/test/resources/SequenceNumberPatternConverterTest.yml
+++ b/log4j-core/src/test/resources/SequenceNumberPatternConverterTest.yml
@@ -4,12 +4,17 @@ Configuration:
Appenders:
List:
- name: List
- PatternLayout:
- pattern: '%sn'
+ - name: List
+ PatternLayout:
+ pattern: '%sn'
+
+ - name: Padded
+ PatternLayout:
+ pattern: '%03sn'
Loggers:
Root:
level: INFO
AppenderRef:
- ref: List
+ - ref: List
+ - ref: Padded
diff --git a/log4j-core/src/test/resources/log4j-rolling-with-padding.xml b/log4j-core/src/test/resources/log4j-rolling-with-padding.xml
new file mode 100644
index 0000000..4bb8d20
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-rolling-with-padding.xml
@@ -0,0 +1,42 @@
+<?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="WARN" name="RollingWithPadding">
+ <Properties>
+ <Property name="base">target/rolling-with-padding/</Property>
+ </Properties>
+
+ <Appenders>
+ <RollingFile name="RollingFile" fileName="${base}/rollingtest.log" filePattern="${base}/test-%03i.log">
+ <PatternLayout>
+ <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
+ </PatternLayout>
+ <Policies>
+ <SizeBasedTriggeringPolicy size="30" />
+ </Policies>
+ <DefaultRolloverStrategy max="5" />
+ </RollingFile>
+ </Appenders>
+
+ <Loggers>
+ <Root level="trace">
+ <AppenderRef ref="RollingFile" />
+ </Root>
+ </Loggers>
+
+</Configuration>