LOG4J2-1341 (GC) HighlightConverter and StyleConverter are now GC-free. This closes GitHub pull request #34 (https://github.com/apache/logging-log4j2/pull/34).
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/c4af088d Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/c4af088d Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/c4af088d Branch: refs/heads/LOG4J-1181 Commit: c4af088d7d0a181f4e9b84f401bd56f2e1342797 Parents: 8529086 Author: rpopma <[email protected]> Authored: Sun Jul 31 14:43:14 2016 +0900 Committer: rpopma <[email protected]> Committed: Sun Jul 31 14:43:14 2016 +0900 ---------------------------------------------------------------------- .../log4j/core/pattern/HighlightConverter.java | 37 ++++++---- .../log4j/core/pattern/StyleConverter.java | 24 ++++-- .../core/pattern/HighlightConverterTest.java | 78 ++++++++++++++++++++ log4j-core/src/test/resources/gcFreeLogging.xml | 10 +-- .../resources/gcFreeMixedSyncAsyncLogging.xml | 10 +-- log4j-core/src/test/resources/log4j-style.xml | 4 +- src/changes/changes.xml | 5 +- src/site/xdoc/manual/garbagefree.xml | 10 ++- 8 files changed, 143 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java index 0e2f63d..c0ab673 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java @@ -34,14 +34,14 @@ import org.apache.logging.log4j.util.Strings; * <p> * For example: * </p> - * + * * <pre> * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable} * </pre> * <p> * You can define custom colors for each Level: * </p> - * + * * <pre> * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=cyan, * TRACE=black} @@ -49,9 +49,9 @@ import org.apache.logging.log4j.util.Strings; * <p> * You can use a predefined style: * </p> - * + * * <pre> - * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{STYLE=Log4j} + * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{STYLE=DEFAULT} * </pre> * <p> * The available predefined styles are: @@ -108,7 +108,7 @@ public final class HighlightConverter extends LogEventPatternConverter implement * <p> * The format of the option string in {@code option[1]} is: * </p> - * + * * <pre> * Level1=Value, Level2=Value, ... * </pre> @@ -193,6 +193,8 @@ public final class HighlightConverter extends LogEventPatternConverter implement private final boolean noAnsi; + private final String defaultStyle; + /** * Construct the converter. * @@ -205,6 +207,7 @@ public final class HighlightConverter extends LogEventPatternConverter implement super("style", "style"); this.patternFormatters = patternFormatters; this.levelStyles = levelStyles; + this.defaultStyle = AnsiEscape.getDefaultStyle(); this.noAnsi = noAnsi; } @@ -213,17 +216,25 @@ public final class HighlightConverter extends LogEventPatternConverter implement */ @Override public void format(final LogEvent event, final StringBuilder toAppendTo) { - final StringBuilder buf = new StringBuilder(); - for (final PatternFormatter formatter : patternFormatters) { - formatter.format(event, buf); + final int start = toAppendTo.length(); + int end = 0; + if (!noAnsi) { // use ANSI: set prefix + toAppendTo.append(levelStyles.get(event.getLevel())); + end = toAppendTo.length(); + } + + //noinspection ForLoopReplaceableByForEach + for (int i = 0, size = patternFormatters.size(); i < size; i++) { + patternFormatters.get(i).format(event, toAppendTo); } - if (buf.length() > 0) { - if (noAnsi) { - toAppendTo.append(buf.toString()); + // if we use ANSI we need to add the postfix or erase the unnecessary prefix + final boolean empty = toAppendTo.length() == end; + if (!noAnsi) { + if (empty) { + toAppendTo.setLength(start); // erase prefix } else { - toAppendTo.append(levelStyles.get(event.getLevel())).append(buf.toString()). - append(AnsiEscape.getDefaultStyle()); + toAppendTo.append(defaultStyle); // add postfix } } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java index 202d771..0d8c730 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java @@ -94,17 +94,25 @@ public final class StyleConverter extends LogEventPatternConverter implements An */ @Override public void format(final LogEvent event, final StringBuilder toAppendTo) { - final StringBuilder buf = new StringBuilder(); - for (final PatternFormatter formatter : patternFormatters) { - formatter.format(event, buf); + int start = 0; + int end = 0; + if (!noAnsi) { // use ANSI: set prefix + start = toAppendTo.length(); + toAppendTo.append(style); + end = toAppendTo.length(); + } + + //noinspection ForLoopReplaceableByForEach + for (int i = 0, size = patternFormatters.size(); i < size; i++) { + patternFormatters.get(i).format(event, toAppendTo); } - if (buf.length() > 0) { - if (noAnsi) { - // faster to test and do this than setting style and defaultStyle to empty strings. - toAppendTo.append(buf.toString()); + // if we use ANSI we need to add the postfix or erase the unnecessary prefix + if (!noAnsi) { + if (toAppendTo.length() == end) { + toAppendTo.setLength(start); // erase prefix } else { - toAppendTo.append(style).append(buf.toString()).append(defaultStyle); + toAppendTo.append(defaultStyle); // add postfix } } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java new file mode 100644 index 0000000..6051f85 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java @@ -0,0 +1,78 @@ +package org.apache.logging.log4j.core.pattern;/* + * 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. + */ + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.message.SimpleMessage; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Tests the HighlightConverter. + */ +public class HighlightConverterTest { + + @Test + public void testAnsiEmpty() { + final String[] options = {"", PatternParser.NO_CONSOLE_NO_ANSI + "=false"}; + final HighlightConverter converter = HighlightConverter.newInstance(null, options); + + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( + new SimpleMessage("message in a bottle")).build(); + final StringBuilder buffer = new StringBuilder(); + converter.format(event, buffer); + assertEquals("", buffer.toString()); + } + + @Test + public void testAnsiNonEmpty() { + final String[] options = {"%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=false"}; + final HighlightConverter converter = HighlightConverter.newInstance(null, options); + + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( + new SimpleMessage("message in a bottle")).build(); + final StringBuilder buffer = new StringBuilder(); + converter.format(event, buffer); + assertEquals("\u001B[32mINFO : message in a bottle\u001B[m", buffer.toString()); + } + + @Test + public void testNoAnsiEmpty() { + final String[] options = {"", PatternParser.NO_CONSOLE_NO_ANSI + "=true"}; + final HighlightConverter converter = HighlightConverter.newInstance(null, options); + + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( + new SimpleMessage("message in a bottle")).build(); + final StringBuilder buffer = new StringBuilder(); + converter.format(event, buffer); + assertEquals("", buffer.toString()); + } + + @Test + public void testNoAnsiNonEmpty() { + final String[] options = {"%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=true"}; + final HighlightConverter converter = HighlightConverter.newInstance(null, options); + + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( + new SimpleMessage("message in a bottle")).build(); + final StringBuilder buffer = new StringBuilder(); + converter.format(event, buffer); + assertEquals("INFO : message in a bottle", buffer.toString()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/log4j-core/src/test/resources/gcFreeLogging.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/gcFreeLogging.xml b/log4j-core/src/test/resources/gcFreeLogging.xml index f90d754..8893466 100644 --- a/log4j-core/src/test/resources/gcFreeLogging.xml +++ b/log4j-core/src/test/resources/gcFreeLogging.xml @@ -6,13 +6,13 @@ </Console> <File name="File" fileName="target/gcfreefile.log" bufferedIO="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %m%n}</Pattern> </PatternLayout> </File> <RollingFile name="RollingFile" fileName="target/gcfreeRollingFile.log" filePattern="target/gcfree-%d{MM-dd-yy-HH-mm-ss}.log.gz"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %m%n}</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="50M" /> @@ -20,7 +20,7 @@ </RollingFile> <RandomAccessFile name="RandomAccessFile" fileName="target/gcfreeRAF.log" immediateFlush="false" append="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %X{aKey} %m %ex%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %X{aKey} %m %ex%n}</Pattern> </PatternLayout> </RandomAccessFile> <RollingRandomAccessFile name="RollingRandomAccessFile" @@ -28,7 +28,7 @@ filePattern="target/afterRollover-%i.log" append="false" immediateFlush="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %X{aKey} %m %location %ex%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %X{aKey} %m %location %ex%n}</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="50 M"/> @@ -38,7 +38,7 @@ fileName="target/gcfreemmap.log" immediateFlush="false" append="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %X{aKey} %m%ex%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %X{aKey} %m%ex%n}</Pattern> </PatternLayout> </MemoryMappedFile> <RandomAccessFile name="RandomAccessFileGelf" fileName="target/gcfree.json" immediateFlush="false" append="false"> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/log4j-core/src/test/resources/gcFreeMixedSyncAsyncLogging.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/gcFreeMixedSyncAsyncLogging.xml b/log4j-core/src/test/resources/gcFreeMixedSyncAsyncLogging.xml index e4c0000..50bff57 100644 --- a/log4j-core/src/test/resources/gcFreeMixedSyncAsyncLogging.xml +++ b/log4j-core/src/test/resources/gcFreeMixedSyncAsyncLogging.xml @@ -6,13 +6,13 @@ </Console> <File name="File" fileName="target/gcfreefileMixed.log" bufferedIO="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %m%n}</Pattern> </PatternLayout> </File> <RollingFile name="RollingFile" fileName="target/gcfreeRollingFileMixed.log" filePattern="target/gcfree-%d{MM-dd-yy-HH-mm-ss}.log.gz"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %m%n}</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="50M" /> @@ -20,7 +20,7 @@ </RollingFile> <RandomAccessFile name="RandomAccessFile" fileName="target/gcfreeRAFMixed.log" immediateFlush="false" append="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %X{aKey} %m %ex%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %X{aKey} %m %ex%n}</Pattern> </PatternLayout> </RandomAccessFile> <RollingRandomAccessFile name="RollingRandomAccessFile" @@ -28,7 +28,7 @@ filePattern="target/afterRollover-%i.log" append="false" immediateFlush="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %X{aKey} %m %location %ex%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %X{aKey} %m %location %ex%n}</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="50 M"/> @@ -38,7 +38,7 @@ fileName="target/gcfreemmapMixed.log" immediateFlush="false" append="false"> <PatternLayout> - <Pattern>%d %p %c{1.} [%t] %X{aKey} %m%ex%n</Pattern> + <Pattern>%highlight{%style{%d}{bright,cyan} %p %c{1.} [%t] %X{aKey} %m%ex%n}</Pattern> </PatternLayout> </MemoryMappedFile> <RandomAccessFile name="RandomAccessFileGelf" fileName="target/gcfreeMixed.json" immediateFlush="false" append="false"> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/log4j-core/src/test/resources/log4j-style.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j-style.xml b/log4j-core/src/test/resources/log4j-style.xml index 9b18b37..6acf873 100644 --- a/log4j-core/src/test/resources/log4j-style.xml +++ b/log4j-core/src/test/resources/log4j-style.xml @@ -20,7 +20,7 @@ <Appenders> <List name="List"> <PatternLayout> - <Pattern>%d %highlight{%p} %style{%logger}{bright,cyan} %C{1.} %msg%n</Pattern> + <Pattern>%d %highlight{%p} %style{%logger}{bright,cyan}%style{}{bright,cyan} %C{1.} %msg%n</Pattern> </PatternLayout> </List> </Appenders> @@ -31,4 +31,4 @@ </Root> </Loggers> -</Configuration> \ No newline at end of file +</Configuration> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8ec3453..25d4b07 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -23,7 +23,10 @@ <title>Changes</title> </properties> <body> - <release version="2.7.0" date="2016-MM-DD" description="GA Release 2.7.0"> + <release version="2.7" date="2016-MM-DD" description="GA Release 2.7.0"> + <action issue="LOG4J2-1341" dev="rpopma" type="fix" due-to="Richard Zschech"> + (GC) HighlightConverter and StyleConverter are now GC-free. + </action> <action issue="LOG4J2-1467" dev="rpopma, ggregory" type="fix" due-to="Ralf, Gary Gregory"> [OSGi] Missing import package. </action> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c4af088d/src/site/xdoc/manual/garbagefree.xml ---------------------------------------------------------------------- diff --git a/src/site/xdoc/manual/garbagefree.xml b/src/site/xdoc/manual/garbagefree.xml index 58ca1d0..46499d5 100644 --- a/src/site/xdoc/manual/garbagefree.xml +++ b/src/site/xdoc/manual/garbagefree.xml @@ -234,6 +234,14 @@ </td> </tr> <tr> + <td>%highlight{pattern}{style}</td> + <td>Adds ANSI colors (unless nested pattern is not garbage free)</td> + </tr> + <tr> + <td>style{pattern}{ANSI style}</td> + <td>Style the message (unless nested pattern is not garbage free)</td> + </tr> + <tr> <td>%m, %msg, %message</td> <td>Log message (unless message text contains '${')</td> </tr> @@ -501,4 +509,4 @@ public void garbageFree() { </subsection> </section> </body> -</document> \ No newline at end of file +</document>
