Repository: logging-log4j2 Updated Branches: refs/heads/master 96327da7d -> 825f15574
[LOG4J2-684] ExtendedThrowablePatternConverter does not print suppressed exceptions. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/825f1557 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/825f1557 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/825f1557 Branch: refs/heads/master Commit: 825f15574d9cd020432e99ddfa307fb0287d825c Parents: 96327da Author: ggregory <[email protected]> Authored: Mon Jun 29 12:14:40 2015 -0700 Committer: ggregory <[email protected]> Committed: Mon Jun 29 12:14:40 2015 -0700 ---------------------------------------------------------------------- .../logging/log4j/core/impl/ThrowableProxy.java | 112 +++++++++++-------- ...nsoleAppenderDefaultSuppressedThrowable.java | 67 +++++++++++ .../log4j/core/impl/ThrowableProxyTest.java | 25 +++++ ...4j2-console-default-suppressed-throwable.xml | 31 +++++ src/changes/changes.xml | 3 + 5 files changed, 194 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/825f1557/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java index d0800cc..bcd38dd 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java @@ -51,7 +51,11 @@ import org.apache.logging.log4j.util.Strings; */ public class ThrowableProxy implements Serializable { - /** + private static final String CAUSED_BY_LABEL = "Caused by: "; + private static final String SUPPRESSED_LABEL = "Suppressed: "; + private static final String WRAPPED_BY_LABEL = "Wrapped by: "; + + /** * Cached StackTracePackageElement and ClassLoader. * <p> * Consider this class private. @@ -201,46 +205,64 @@ public class ThrowableProxy implements Serializable { return true; } - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - private void formatCause(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages) { - if (cause == null) { + private void formatCause(final StringBuilder sb, String prefix, final ThrowableProxy cause, final List<String> ignorePackages) { + formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages); + } + + private void formatThrowableProxy(final StringBuilder sb, String prefix, final String causeLabel, + final ThrowableProxy throwableProxy, final List<String> ignorePackages) { + if (throwableProxy == null) { return; } - sb.append("Caused by: ").append(cause).append(EOL); - this.formatElements(sb, cause.commonElementCount, cause.getStackTrace(), cause.extendedStackTrace, - ignorePackages); - this.formatCause(sb, cause.causeProxy, ignorePackages); + sb.append(prefix).append(causeLabel).append(throwableProxy).append(EOL); + this.formatElements(sb, prefix, throwableProxy.commonElementCount, + throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, ignorePackages); + this.formatSuppressed(sb, prefix + "\t", throwableProxy.suppressedProxies, ignorePackages); + this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages); } - private void formatElements(final StringBuilder sb, final int commonCount, final StackTraceElement[] causedTrace, - final ExtendedStackTraceElement[] extStackTrace, final List<String> ignorePackages) { - if (ignorePackages == null || ignorePackages.isEmpty()) { - for (final ExtendedStackTraceElement element : extStackTrace) { - this.formatEntry(element, sb); - } - } else { - int count = 0; - for (int i = 0; i < extStackTrace.length; ++i) { - if (!this.ignoreElement(causedTrace[i], ignorePackages)) { - if (count > 0) { - appendSuppressedCount(sb, count); - count = 0; - } - this.formatEntry(extStackTrace[i], sb); - } else { - ++count; - } - } - if (count > 0) { - appendSuppressedCount(sb, count); - } - } - if (commonCount != 0) { - sb.append("\t... ").append(commonCount).append(" more").append(EOL); - } - } + private void formatSuppressed(final StringBuilder sb, String prefix, final ThrowableProxy[] suppressedProxies, + final List<String> ignorePackages) { + if (suppressedProxies == null) { + return; + } + for (ThrowableProxy suppressedProxy : suppressedProxies) { + final ThrowableProxy cause = suppressedProxy; + formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, cause, ignorePackages); + } + } + + private void formatElements(final StringBuilder sb, String prefix, final int commonCount, + final StackTraceElement[] causedTrace, final ExtendedStackTraceElement[] extStackTrace, + final List<String> ignorePackages) { + if (ignorePackages == null || ignorePackages.isEmpty()) { + for (final ExtendedStackTraceElement element : extStackTrace) { + this.formatEntry(element, sb, prefix); + } + } else { + int count = 0; + for (int i = 0; i < extStackTrace.length; ++i) { + if (!this.ignoreElement(causedTrace[i], ignorePackages)) { + if (count > 0) { + appendSuppressedCount(sb, prefix, count); + count = 0; + } + this.formatEntry(extStackTrace[i], sb, prefix); + } else { + ++count; + } + } + if (count > 0) { + appendSuppressedCount(sb, prefix, count); + } + } + if (commonCount != 0) { + sb.append(prefix).append("\t... ").append(commonCount).append(" more").append(EOL); + } + } - private void appendSuppressedCount(final StringBuilder sb, final int count) { + private void appendSuppressedCount(final StringBuilder sb, String prefix, final int count) { + sb.append(prefix); if (count == 1) { sb.append("\t....").append(EOL); } else { @@ -248,7 +270,8 @@ public class ThrowableProxy implements Serializable { } } - private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb) { + private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb, String prefix) { + sb.append(prefix); sb.append("\tat "); sb.append(extStackTraceElement); sb.append(EOL); @@ -281,11 +304,11 @@ public class ThrowableProxy implements Serializable { final Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null; if (caused != null) { this.formatWrapper(sb, cause.causeProxy); - sb.append("Wrapped by: "); + sb.append(WRAPPED_BY_LABEL); } sb.append(cause).append(EOL); - this.formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), - cause.extendedStackTrace, packages); + this.formatElements(sb, "", cause.commonElementCount, + cause.getThrowable().getStackTrace(), cause.extendedStackTrace, packages); } public ThrowableProxy getCauseProxy() { @@ -312,11 +335,11 @@ public class ThrowableProxy implements Serializable { final StringBuilder sb = new StringBuilder(); if (this.causeProxy != null) { this.formatWrapper(sb, this.causeProxy); - sb.append("Wrapped by: "); + sb.append(WRAPPED_BY_LABEL); } sb.append(this.toString()); sb.append(EOL); - this.formatElements(sb, 0, this.throwable.getStackTrace(), this.extendedStackTrace, packages); + this.formatElements(sb, "", 0, this.throwable.getStackTrace(), this.extendedStackTrace, packages); return sb.toString(); } @@ -363,8 +386,9 @@ public class ThrowableProxy implements Serializable { } sb.append(EOL); final StackTraceElement[] causedTrace = this.throwable != null ? this.throwable.getStackTrace() : null; - this.formatElements(sb, 0, causedTrace, this.extendedStackTrace, ignorePackages); - this.formatCause(sb, this.causeProxy, ignorePackages); + this.formatElements(sb, "", 0, causedTrace, this.extendedStackTrace, ignorePackages); + this.formatSuppressed(sb, "\t", this.suppressedProxies, ignorePackages); + this.formatCause(sb, "", this.causeProxy, ignorePackages); return sb.toString(); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/825f1557/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderDefaultSuppressedThrowable.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderDefaultSuppressedThrowable.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderDefaultSuppressedThrowable.java new file mode 100644 index 0000000..e3295c0 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderDefaultSuppressedThrowable.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; + +import java.io.IOException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configurator; + +/** + * Shows how to use ANSI escape codes to color messages. Each message is printed + * to the console in color, but the rest of the log entry (time stamp for + * example) is in the default color for that console. + * <p> + * Running from a Windows command line from the root of the project: + * </p> + * + * <pre> + * java -classpath log4j-core\target\test-classes;log4j-core\target\classes;log4j-api\target\classes;%HOME%\.m2\repository\org\fusesource\jansi\jansi\1.11\jansi-1.11.jar; org.apache.logging.log4j.core.appender.ConsoleAppenderNoAnsiStyleLayoutMain log4j-core/target/test-classes/log4j2-console-style-ansi.xml + * </pre> + */ +public class ConsoleAppenderDefaultSuppressedThrowable { + + private static final Logger LOG = LogManager.getLogger(ConsoleAppenderDefaultSuppressedThrowable.class); + + public static void main(final String[] args) { + final String config = args.length == 0 ? "target/test-classes/log4j2-console-default-suppressed-throwable.xml" + : args[0]; + test(args, config); + } + + static void test(final String[] args, final String config) { + // System.out.println(System.getProperty("java.class.path")); + final LoggerContext ctx = Configurator.initialize(ConsoleAppenderDefaultSuppressedThrowable.class.getName(), + config); + try { + IOException ioEx = new IOException("test suppressed"); + ioEx.addSuppressed(new IOException("test suppressed 1", new IOException("test 1"))); + IOException ioEx2 = new IOException("test 2"); + ioEx2.addSuppressed(new IOException("test 3")); + ioEx.addSuppressed(new IOException("test suppressed 2", ioEx2)); + IOException e = new IOException("test", ioEx); + LOG.error("Error message {}, suppressed?", "Hi", e); + System.out.println("printStackTrace"); + e.printStackTrace(); + } finally { + Configurator.shutdown(ctx); + } + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/825f1557/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java index 08d0a4b..c3128d4 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableProxyTest.java @@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.impl; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; @@ -240,6 +241,30 @@ public class ThrowableProxyTest { LogManager.getLogger().error("Error", e1); } + @Test + public void testSuppressedExceptions() { + Exception e = new Exception(); + e.addSuppressed(new IOException("Suppressed #1")); + e.addSuppressed(new IOException("Suppressed #2")); + LogManager.getLogger().error("Error", e); + final ThrowableProxy proxy = new ThrowableProxy(e); + String extendedStackTraceAsString = proxy.getExtendedStackTraceAsString(); + assertTrue(extendedStackTraceAsString.contains("Suppressed #1")); + assertTrue(extendedStackTraceAsString.contains("Suppressed #2")); + } + + @Test + public void testCauseSuppressedExceptions() { + Exception cause = new Exception(); + cause.addSuppressed(new IOException("Suppressed #1")); + cause.addSuppressed(new IOException("Suppressed #2")); + LogManager.getLogger().error("Error", cause); + final ThrowableProxy proxy = new ThrowableProxy(new Exception(cause)); + String extendedStackTraceAsString = proxy.getExtendedStackTraceAsString(); + assertTrue(extendedStackTraceAsString.contains("Suppressed #1")); + assertTrue(extendedStackTraceAsString.contains("Suppressed #2")); + } + /** * Tests LOG4J2-934. */ http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/825f1557/log4j-core/src/test/resources/log4j2-console-default-suppressed-throwable.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j2-console-default-suppressed-throwable.xml b/log4j-core/src/test/resources/log4j2-console-default-suppressed-throwable.xml new file mode 100644 index 0000000..90f9373 --- /dev/null +++ b/log4j-core/src/test/resources/log4j2-console-default-suppressed-throwable.xml @@ -0,0 +1,31 @@ +<?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> + <Console name="Console" target="SYSTEM_OUT"> + <PatternLayout noConsoleNoAnsi="true" pattern="[%-5p] %d{yyyy/MM/dd HH:mm:ss} - %t - %c - %m%n" /> + </Console> + </Appenders> + <Loggers> + <Logger name="org.foo" level="DEBUG" /> + <Root level="TRACE"> + <AppenderRef ref="Console" /> + </Root> + </Loggers> +</Configuration> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/825f1557/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c6ad6dc..e682373 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -48,6 +48,9 @@ <action issue="LOG4J2-1051" dev="ggregory" type="fix" due-to="Lukasz Lenart"> NoClassDefFoundError when starting app on Google App Engine. </action> + <action issue="LOG4J2-684" dev="ggregory" type="fix" due-to="Joern Huxhorn, Mauro Molinari"> + ExtendedThrowablePatternConverter does not print suppressed exceptions. + </action> <action issue="LOG4J2-1069" dev="ggregory" type="fix" due-to="Sam Braam"> Improper handling of JSON escape chars when deserializing JSON log events. </action>
