Repository: logging-log4j2 Updated Branches: refs/heads/master ee4573bf8 -> df0270af2
LOG4J2-1217 - PatternLayout option to limit length of text. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/df0270af Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/df0270af Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/df0270af Branch: refs/heads/master Commit: df0270af2ec4f4ad64474a778f5d9b2731d4cce9 Parents: ee4573b Author: Matt Sicker <[email protected]> Authored: Wed Mar 2 22:32:31 2016 -0600 Committer: Matt Sicker <[email protected]> Committed: Wed Mar 2 22:32:31 2016 -0600 ---------------------------------------------------------------------- .../log4j/core/pattern/MaxLengthConverter.java | 103 +++++++++++++++++++ .../core/pattern/MaxLengthConverterTest.java | 73 +++++++++++++ src/changes/changes.xml | 3 + src/site/xdoc/manual/layouts.xml.vm | 38 +++++++ 4 files changed, 217 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/df0270af/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MaxLengthConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MaxLengthConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MaxLengthConverter.java new file mode 100644 index 0000000..ca053f2 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MaxLengthConverter.java @@ -0,0 +1,103 @@ +/* + * 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.pattern; + +import java.util.List; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.layout.PatternLayout; + +/** + * Max length pattern converter. Limit contained text to a maximum length. + * On invalid length the default value 100 is used (and an error message is logged). + * If max length is greater than 20, an abbreviated text will get ellipsis ("...") appended. + * Example usage (for email subject): + * {@code "%maxLen{[AppName, ${hostName}, ${web:contextPath}] %p: %c{1} - %m%notEmpty{ =>%ex{short}}}{160}"} + * + * @author Thies Wellpott + */ +@Plugin(name = "maxLength", category = PatternConverter.CATEGORY) +@ConverterKeys({"maxLength", "maxLen"}) +public final class MaxLengthConverter extends LogEventPatternConverter { + + /** + * Gets an instance of the class. + * + * @param config The current Configuration. + * @param options pattern options, an array of two elements: pattern, max length (defaults to 100 on invalid value). + * @return instance of class. + */ + public static MaxLengthConverter newInstance(final Configuration config, final String[] options) { + if (options.length != 2) { + LOGGER.error("Incorrect number of options on maxLength: expected 2 received {}: {}", options.length, + options); + return null; + } + if (options[0] == null) { + LOGGER.error("No pattern supplied on maxLength"); + return null; + } + if (options[1] == null) { + LOGGER.error("No length supplied on maxLength"); + return null; + } + final PatternParser parser = PatternLayout.createPatternParser(config); + final List<PatternFormatter> formatters = parser.parse(options[0]); + return new MaxLengthConverter(formatters, AbstractAppender.parseInt(options[1], 100)); + } + + + private final List<PatternFormatter> formatters; + private final int maxLength; + + /** + * Construct the converter. + * + * @param formatters The PatternFormatters to generate the text to manipulate. + * @param maxLength The max. length of the resulting string. Ellipsis ("...") is appended on shorted string, if greater than 20. + */ + private MaxLengthConverter(final List<PatternFormatter> formatters, final int maxLength) { + super("MaxLength", "maxLength"); + this.maxLength = maxLength; + this.formatters = formatters; + LOGGER.trace("new MaxLengthConverter with {}", maxLength); + } + + + @Override + public void format(final LogEvent event, final StringBuilder toAppendTo) { + final StringBuilder buf = new StringBuilder(); + for (final PatternFormatter formatter : formatters) { + formatter.format(event, buf); + if (buf.length() > maxLength) { // stop early + break; + } + } + if (buf.length() > maxLength) { + buf.setLength(maxLength); + if (maxLength > 20) { // only append ellipses if length is not very short + buf.append("..."); + } + } + toAppendTo.append(buf); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/df0270af/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MaxLengthConverterTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MaxLengthConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MaxLengthConverterTest.java new file mode 100644 index 0000000..e379915 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MaxLengthConverterTest.java @@ -0,0 +1,73 @@ +/* + * 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.pattern; + +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.Message; +import org.apache.logging.log4j.message.SimpleMessage; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + */ +public class MaxLengthConverterTest { + + private static MaxLengthConverter converter = MaxLengthConverter.newInstance(null, new String[]{"%m", "10"}); + + @Test + public void testUnderMaxLength() throws Exception { + final Message message = new SimpleMessage("0123456789"); + final LogEvent event = Log4jLogEvent.newBuilder() + .setLoggerName("MyLogger") + .setLevel(Level.DEBUG) + .setMessage(message) + .build(); + final StringBuilder sb = new StringBuilder(); + converter.format(event, sb); + assertEquals("0123456789", sb.toString()); + } + + @Test + public void testOverMaxLength() throws Exception { + final Message message = new SimpleMessage("01234567890123456789"); + final LogEvent event = Log4jLogEvent.newBuilder() + .setLoggerName("MyLogger") + .setLevel(Level.DEBUG) + .setMessage(message) + .build(); + final StringBuilder sb = new StringBuilder(); + converter.format(event, sb); + assertEquals("0123456789", sb.toString()); + } + @Test + public void testOverMaxLength21WithEllipsis() throws Exception { + final Message message = new SimpleMessage("012345678901234567890123456789"); + final LogEvent event = Log4jLogEvent.newBuilder() + .setLoggerName("MyLogger") + .setLevel(Level.DEBUG) + .setMessage(message) + .build(); + final StringBuilder sb = new StringBuilder(); + MaxLengthConverter.newInstance(null, new String[]{"%m", "21"}).format(event, sb); + assertEquals("012345678901234567890...", sb.toString()); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/df0270af/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index aa6581c..5066c0a 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -190,6 +190,9 @@ <action issue="LOG4J2-1306" dev="mattsicker" type="update"> JeroMqAppender should use ShutdownCallbackRegistry instead of runtime hooks. </action> + <action issue="LOG4J2-1217" dev="mattsicker" type="add" due-to="Thies Wellpott"> + PatternLayout option to limit length of text. + </action> </release> <release version="2.5" date="2015-12-06" description="GA Release 2.5"> <action issue="LOG4J2-324" dev="rpopma" type="fix"> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/df0270af/src/site/xdoc/manual/layouts.xml.vm ---------------------------------------------------------------------- diff --git a/src/site/xdoc/manual/layouts.xml.vm b/src/site/xdoc/manual/layouts.xml.vm index fa602e4..d193380 100644 --- a/src/site/xdoc/manual/layouts.xml.vm +++ b/src/site/xdoc/manual/layouts.xml.vm @@ -910,6 +910,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternMap"/> <b>K</b>{key}<br /> <b>map</b>{key}<br /> <b>MAP</b>{key} @@ -957,6 +958,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternMessage"/> <b>m</b><br /> <b>msg</b><br /> <b>message</b> @@ -977,18 +979,40 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternMarker"/> <b>marker</b> </td> <td>The full name of the marker, including parents, if one is present.</td> </tr> <tr> <td align="center"> + <a name="PatternMarkerSimpleName"/> <b>markerSimpleName</b> </td> <td>The simple name of the marker (not including parents), if one is present.</td> </tr> <tr> <td align="center"> + <a name="PatternMaxLength"/> + <b>maxLen</b><br/> + <b>maxLength</b> + </td> + <td> + <p> + Outputs the result of evaluating the pattern and truncating the result. If the length is greater + than 20, then the output will contain a trailing ellipsis. If the provided length is invalid, a + default value of 100 is used. + </p> + <p> + Example syntax: <code>%maxLen{%p: %c{1} - %m%notEmpty{ =>%ex{short}}}{160}</code> will be limited to + 160 characters with a trailing ellipsis. Another example: <code>%maxLen{%m}{20}</code> will be + limited to 20 characters and no trailing ellipsis. + </p> + </td> + </tr> + <tr> + <td align="center"> + <a name="PatternNewLine"/> <b>n</b> </td> <td> @@ -1029,6 +1053,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternLevel"/> <b>p</b>|<b>level</b>{<em>level</em>=<em>label</em>, <em>level</em>=<em>label</em>, ...} <b>p</b>|<b>level</b>{length=<em>n</em>} <b>p</b>|<b>level</b>{lowerCase=<em>true</em>|<em>false</em>} @@ -1065,6 +1090,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternRelative"/> <b>r</b><br /> <b>relative</b> </td> @@ -1074,6 +1100,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternReplace"/> <b>replace</b>{pattern}{regex}{substitution} </td> <td> @@ -1089,6 +1116,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternException"/> <b>rEx</b>["none"|"short"|"full"|depth],[filters(packages)}<br /> <b>rException</b>["none"|"short"|"full"|depth],[filters(packages)}<br /> <b>rThrowable</b>["none"|"short"|"full"|depth],[filters(packages)} @@ -1109,6 +1137,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternSequenceNumber"/> <b>sn</b><br /> <b>sequenceNumber</b> </td> @@ -1118,6 +1147,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternStyle"/> <b>style</b>{pattern}{ANSI style} </td> <td> @@ -1239,6 +1269,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternThreadId"/> <b>T</b><br /> <b>tid</b><br /> <b>threadId</b> @@ -1247,6 +1278,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternThreadName"/> <b>t</b><br /> <b>thread</b><br /> <b>threadName</b> @@ -1255,6 +1287,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternThreadPriority"/> <b>tp</b><br /> <b>threadPriority</b> </td> @@ -1262,6 +1295,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternNDC"/> <b>x</b><br /> <b>NDC</b> </td> @@ -1271,6 +1305,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternMDC"/> <b>X</b>{key[,key2...]}<br /> <b>mdc</b>{key[,key2...]}<br /> <b>MDC</b>{key[,key2...]} @@ -1300,6 +1335,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternUUID"/> <b>u</b>{"RANDOM" | "TIME"}<br /> <b>uuid</b> </td> @@ -1314,6 +1350,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternExtendedException"/> <b>xEx</b>{"none"|"short"|"full"|depth],[filters(packages)}<br /> <b>xException</b>["none"|"short"|"full"|depth],[filters(packages)}<br /> <b>xThrowable</b>["none"|"short"|"full"|depth],[filters(packages)} @@ -1338,6 +1375,7 @@ WARN [main]: Message 2</pre> </tr> <tr> <td align="center"> + <a name="PatternPercentLiteral"/> <b>%</b> </td> <td>The sequence %% outputs a single percent sign.
