Repository: logging-log4j2 Updated Branches: refs/heads/master 2fe34f762 -> 6a4c88d4a
[LOG4J2-905] Ability to disable (date) lookup completely, compatibility issues with other libraries like Camel. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/6a4c88d4 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/6a4c88d4 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/6a4c88d4 Branch: refs/heads/master Commit: 6a4c88d4ae85f6a70843df23e0781becc790c821 Parents: 2fe34f7 Author: Gary Gregory <ggreg...@apache.org> Authored: Fri Aug 19 00:31:44 2016 -0700 Committer: Gary Gregory <ggreg...@apache.org> Committed: Fri Aug 19 00:31:44 2016 -0700 ---------------------------------------------------------------------- .../core/pattern/MessagePatternConverter.java | 45 +++++-- .../logging/log4j/core/util/ArrayUtils.java | 124 +++++++++++++++++++ .../layout/PatternLayoutNoLookupDateTest.java | 26 ++++ .../src/test/resources/log4j-list-nolookups.xml | 29 +++++ src/changes/changes.xml | 3 + src/site/xdoc/manual/layouts.xml.vm | 12 +- 6 files changed, 223 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6a4c88d4/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java index 75af44c..1fd2d2b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java @@ -21,6 +21,7 @@ import java.util.Locale; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.util.ArrayUtils; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MultiformatMessage; @@ -34,9 +35,12 @@ import org.apache.logging.log4j.util.StringBuilderFormattable; @ConverterKeys({ "m", "msg", "message" }) public final class MessagePatternConverter extends LogEventPatternConverter { + private static final String NOLOOKUPS = "nolookups"; + private final String[] formats; private final Configuration config; private final TextRenderer textRenderer; + private final boolean noLookups; /** * Private constructor. @@ -48,22 +52,37 @@ public final class MessagePatternConverter extends LogEventPatternConverter { super("Message", "message"); this.formats = options; this.config = config; - this.textRenderer = loadMessageRenderer(options); + final int noLookupsIdx = loadNoLookups(options); + this.noLookups = noLookupsIdx >= 0; + this.textRenderer = loadMessageRenderer(noLookupsIdx >= 0 ? ArrayUtils.remove(options, noLookupsIdx) : options); + } + + private int loadNoLookups(String[] options) { + if (options != null) { + for (int i = 0; i < options.length; i++) { + final String option = options[i]; + if (NOLOOKUPS.equalsIgnoreCase(option)) { + return i; + } + } + } + return -1; } private TextRenderer loadMessageRenderer(String[] options) { - if (formats != null && formats.length > 0) { - final String format = formats[0].toUpperCase(Locale.ROOT); - switch (format) { - case "ANSI": - if (Loader.isJansiAvailable()) { - return new JAnsiTextRenderer(formats, JAnsiTextRenderer.DefaultMessageStyleMap); + if (options != null) { + for (String option : options) { + switch (option.toUpperCase(Locale.ROOT)) { + case "ANSI": + if (Loader.isJansiAvailable()) { + return new JAnsiTextRenderer(options, JAnsiTextRenderer.DefaultMessageStyleMap); + } + StatusLogger.getLogger() + .warn("You requested ANSI message rendering but JANSI is not on the classpath."); + return null; + case "HTML": + return new HtmlTextRenderer(options); } - StatusLogger.getLogger() - .warn("You requested ANSI message rendering but JANSI is not on the classpath."); - return null; - case "HTML": - return new HtmlTextRenderer(formats); } } return null; @@ -98,7 +117,7 @@ public final class MessagePatternConverter extends LogEventPatternConverter { stringBuilderFormattable.formatTo(workingBuilder); // TODO can we optimize this? - if (config != null) { + if (config != null && !noLookups) { for (int i = offset; i < workingBuilder.length() - 1; i++) { if (workingBuilder.charAt(i) == '$' && workingBuilder.charAt(i + 1) == '{') { final String value = workingBuilder.substring(offset, workingBuilder.length()); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6a4c88d4/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ArrayUtils.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ArrayUtils.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ArrayUtils.java new file mode 100644 index 0000000..bcf8d54 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ArrayUtils.java @@ -0,0 +1,124 @@ +/* + * 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.util; + +import java.lang.reflect.Array; + +/** + * Copied from Apache Commons Lang (including the {@code @since} tags.) + */ +public class ArrayUtils { + + /** + * <p>Returns the length of the specified array. + * This method can deal with {@code Object} arrays and with primitive arrays.</p> + * + * <p>If the input array is {@code null}, {@code 0} is returned.</p> + * + * <pre> + * ArrayUtils.getLength(null) = 0 + * ArrayUtils.getLength([]) = 0 + * ArrayUtils.getLength([null]) = 1 + * ArrayUtils.getLength([true, false]) = 2 + * ArrayUtils.getLength([1, 2, 3]) = 3 + * ArrayUtils.getLength(["a", "b", "c"]) = 3 + * </pre> + * + * @param array the array to retrieve the length from, may be null + * @return The length of the array, or {@code 0} if the array is {@code null} + * @throws IllegalArgumentException if the object argument is not an array. + * @since 2.1 + */ + public static int getLength(final Object array) { + if (array == null) { + return 0; + } + return Array.getLength(array); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + private static Object remove(final Object array, final int index) { + final int length = getLength(array); + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + + final Object result = Array.newInstance(array.getClass().getComponentType(), length - 1); + System.arraycopy(array, 0, result, 0, index); + if (index < length - 1) { + System.arraycopy(array, index + 1, result, index, length - index - 1); + } + + return result; + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove(["a"], 0) = [] + * ArrayUtils.remove(["a", "b"], 0) = ["b"] + * ArrayUtils.remove(["a", "b"], 1) = ["a"] + * ArrayUtils.remove(["a", "b", "c"], 1) = ["a", "c"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + @SuppressWarnings("unchecked") // remove() always creates an array of the same type as its input + public static <T> T[] remove(final T[] array, final int index) { + return (T[]) remove((Object) array, index); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6a4c88d4/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutNoLookupDateTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutNoLookupDateTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutNoLookupDateTest.java new file mode 100644 index 0000000..5b2f7be --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutNoLookupDateTest.java @@ -0,0 +1,26 @@ +package org.apache.logging.log4j.core.layout; + +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +/** + * See (LOG4J2-905) Ability to disable (date) lookup completely, compatibility issues with other libraries like camel. + */ +public class PatternLayoutNoLookupDateTest { + + @Rule + public final LoggerContextRule context = new LoggerContextRule("log4j-list-nolookups.xml"); + + @Test + public void testDateLookupInMessage() { + final String template = "${date:YYYY-MM-dd}"; + context.getLogger(PatternLayoutNoLookupDateTest.class.getName()).info(template); + final ListAppender listAppender = context.getListAppender("List"); + final String string = listAppender.getMessages().get(0); + Assert.assertTrue(string, string.contains(template)); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6a4c88d4/log4j-core/src/test/resources/log4j-list-nolookups.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j-list-nolookups.xml b/log4j-core/src/test/resources/log4j-list-nolookups.xml new file mode 100644 index 0000000..2477db7 --- /dev/null +++ b/log4j-core/src/test/resources/log4j-list-nolookups.xml @@ -0,0 +1,29 @@ +<?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"> + <Appenders> + <List name="List"> + <PatternLayout pattern="[%-5level] %c{1.} %msg{ansi}{nolookups}%n" /> + </List> + </Appenders> + <Loggers> + <Root level="debug"> + <AppenderRef ref="List" /> + </Root> + </Loggers> +</Configuration> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6a4c88d4/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0af30cc..675a24a 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -24,6 +24,9 @@ </properties> <body> <release version="2.7" date="2016-MM-DD" description="GA Release 2.7"> + <action issue="LOG4J2-905" dev="ggregory" type="fix" due-to="Gary Gregory, Moritz Löser"> + Ability to disable (date) lookup completely, compatibility issues with other libraries like Camel. + </action> <action issue="LOG4J2-1526" dev="mikes" type="fix"> Possibility to set StatusLogger destination in ConfigurationBuilder. </action> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6a4c88d4/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 8607604..69622f9 100644 --- a/src/site/xdoc/manual/layouts.xml.vm +++ b/src/site/xdoc/manual/layouts.xml.vm @@ -1013,9 +1013,9 @@ WARN [main]: Message 2</pre> <tr> <td align="center"> <a name="PatternMessage"/> - <b>m</b>{ansi}<br /> - <b>msg</b>{ansi}<br /> - <b>message</b>{ansi} + <b>m</b>{nolookups}{ansi}<br /> + <b>msg</b>{nolookups}{ansi}<br /> + <b>message</b>{nolookups}{ansi} </td> <td> <p> @@ -1049,6 +1049,12 @@ WARN [main]: Message 2</pre> The call site can look like this: </p> <pre class="prettyprint linenums">logger.info("@|KeyStyle {}|@ = @|ValueStyle {}|@", entry.getKey(), entry.getValue());</pre> + <p> + Use <code>{nolookups}</code> to log messages like <code>"${esc.d}{date:YYYY-MM-dd}"</code> + without using any lookups. Normally calling <code>logger.info("Try ${esc.d}{date:YYYY-MM-dd}")</code> + would replace the date template <code>${esc.d}{date:YYYY-MM-dd}</code> with an actual date. + Using <code>nolookups</code> disables this feature and logs the message string untouched. + </p> </td> </tr> <tr>