In-line Core module method that just forwards to an API module method. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/5f756e36 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/5f756e36 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/5f756e36
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext Commit: 5f756e36056364c3dfc3002d188919767032f054 Parents: 3bab4fa Author: Gary Gregory <ggreg...@apache.org> Authored: Tue Sep 6 16:51:00 2016 -0400 Committer: Gary Gregory <ggreg...@apache.org> Committed: Tue Sep 6 16:51:00 2016 -0400 ---------------------------------------------------------------------- .../log4j/core/appender/ConsoleAppender.java | 3 +- .../db/jdbc/FactoryMethodConnectionSource.java | 3 +- .../log4j/core/appender/db/jpa/JpaAppender.java | 3 +- .../converter/ThrowableAttributeConverter.java | 3 +- .../logging/log4j/core/config/LoggerConfig.java | 3 +- .../config/plugins/convert/TypeConverters.java | 3 +- .../logging/log4j/core/impl/ThrowableProxy.java | 1461 +++++++++--------- .../log4j/core/net/MulticastDnsAdvertiser.java | 5 +- .../apache/logging/log4j/core/util/Loader.java | 13 - .../log4j/core/util/OptionConverter.java | 3 +- .../logging/log4j/core/util/Profiler.java | 5 +- .../nosql/appender/couchdb/CouchDbProvider.java | 3 +- .../nosql/appender/mongodb/MongoDbProvider.java | 5 +- 13 files changed, 756 insertions(+), 757 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java index d3f8fe9..910e97d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.core.layout.PatternLayout; import org.apache.logging.log4j.core.util.Booleans; import org.apache.logging.log4j.core.util.CloseShieldOutputStream; import org.apache.logging.log4j.core.util.Loader; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.PropertiesUtil; /** @@ -238,7 +239,7 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt } try { // We type the parameter as a wildcard to avoid a hard reference to Jansi. - final Class<?> clazz = Loader.loadClass(JANSI_CLASS); + final Class<?> clazz = LoaderUtil.loadClass(JANSI_CLASS); final Constructor<?> constructor = clazz.getConstructor(OutputStream.class); return new CloseShieldOutputStream((OutputStream) constructor.newInstance(outputStream)); } catch (final ClassNotFoundException cnfe) { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/FactoryMethodConnectionSource.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/FactoryMethodConnectionSource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/FactoryMethodConnectionSource.java index 40dac0c..2c9f01d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/FactoryMethodConnectionSource.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/FactoryMethodConnectionSource.java @@ -29,6 +29,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.Strings; /** @@ -79,7 +80,7 @@ public final class FactoryMethodConnectionSource implements ConnectionSource { final Method method; try { - final Class<?> factoryClass = Loader.loadClass(className); + final Class<?> factoryClass = LoaderUtil.loadClass(className); method = factoryClass.getMethod(methodName); } catch (final Exception e) { LOGGER.error(e.toString(), e); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaAppender.java index bfbdca6..55b6b90 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaAppender.java @@ -28,6 +28,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.util.Booleans; import org.apache.logging.log4j.core.util.Loader; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.Strings; /** @@ -85,7 +86,7 @@ public final class JpaAppender extends AbstractDatabaseAppender<JpaDatabaseManag try { final Class<? extends AbstractLogEventWrapperEntity> entityClass = - Loader.loadClass(entityClassName).asSubclass(AbstractLogEventWrapperEntity.class); + LoaderUtil.loadClass(entityClassName).asSubclass(AbstractLogEventWrapperEntity.class); try { entityClass.getConstructor(); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java index 1bc7f85..679c14d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java @@ -27,6 +27,7 @@ import javax.persistence.AttributeConverter; import javax.persistence.Converter; import org.apache.logging.log4j.core.util.Loader; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.Strings; /** @@ -125,7 +126,7 @@ public class ThrowableAttributeConverter implements AttributeConverter<Throwable final StackTraceElement[] stackTrace) { try { @SuppressWarnings("unchecked") - final Class<Throwable> throwableClass = (Class<Throwable>) Loader.loadClass(throwableClassName); + final Class<Throwable> throwableClass = (Class<Throwable>) LoaderUtil.loadClass(throwableClassName); if (!Throwable.class.isAssignableFrom(throwableClass)) { return null; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java index d5a03bc..ce21859 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java @@ -46,6 +46,7 @@ import org.apache.logging.log4j.core.util.Booleans; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.PerformanceSensitive; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.Strings; @@ -75,7 +76,7 @@ public class LoggerConfig extends AbstractFilterable { final String factory = PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_LOG_EVENT_FACTORY); if (factory != null) { try { - final Class<?> clazz = Loader.loadClass(factory); + final Class<?> clazz = LoaderUtil.loadClass(factory); if (clazz != null && LogEventFactory.class.isAssignableFrom(clazz)) { LOG_EVENT_FACTORY = (LogEventFactory) clazz.newInstance(); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java index 5c4924e..b70c0ab 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java @@ -38,6 +38,7 @@ import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.util.CronExpression; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; /** * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find @@ -175,7 +176,7 @@ public final class TypeConverters { public static class ClassConverter implements TypeConverter<Class<?>> { @Override public Class<?> convert(final String s) throws ClassNotFoundException { - return Loader.loadClass(s); + return LoaderUtil.loadClass(s); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/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 91093bf..ac93f50 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 @@ -1,730 +1,731 @@ -/* - * 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.impl; - -import java.io.Serializable; -import java.net.URL; -import java.security.CodeSource; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import org.apache.logging.log4j.core.pattern.PlainTextRenderer; -import org.apache.logging.log4j.core.pattern.TextRenderer; -import org.apache.logging.log4j.core.util.Loader; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.ReflectionUtil; -import org.apache.logging.log4j.util.Strings; - -/** - * Wraps a Throwable to add packaging information about each stack trace element. - * - * <p> - * A proxy is used to represent a throwable that may not exist in a different class loader or JVM. When an application - * deserializes a ThrowableProxy, the throwable may not be set, but the throwable's information is preserved in other - * fields of the proxy like the message and stack trace. - * </p> - * - * <p> - * TODO: Move this class to org.apache.logging.log4j.core because it is used from LogEvent. - * </p> - * <p> - * TODO: Deserialize: Try to rebuild Throwable if the target exception is in this class loader? - * </p> - */ -public class ThrowableProxy implements Serializable { - - private static final String TAB = "\t"; - 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. - * </p> - */ - static class CacheEntry { - private final ExtendedClassInfo element; - private final ClassLoader loader; - - public CacheEntry(final ExtendedClassInfo element, final ClassLoader loader) { - this.element = element; - this.loader = loader; - } - } - - private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0]; - - private static final char EOL = '\n'; - - private static final String EOL_STR = String.valueOf(EOL); - - private static final long serialVersionUID = -2752771578252251910L; - - private final ThrowableProxy causeProxy; - - private int commonElementCount; - - private final ExtendedStackTraceElement[] extendedStackTrace; - - private final String localizedMessage; - - private final String message; - - private final String name; - - private final ThrowableProxy[] suppressedProxies; - - private final transient Throwable throwable; - - /** - * For JSON and XML IO via Jackson. - */ - @SuppressWarnings("unused") - private ThrowableProxy() { - this.throwable = null; - this.name = null; - this.extendedStackTrace = null; - this.causeProxy = null; - this.message = null; - this.localizedMessage = null; - this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY; - } - - /** - * Constructs the wrapper for the Throwable that includes packaging data. - * - * @param throwable - * The Throwable to wrap, must not be null. - */ - public ThrowableProxy(final Throwable throwable) { - this(throwable, null); - } - - /** - * Constructs the wrapper for the Throwable that includes packaging data. - * - * @param throwable - * The Throwable to wrap, must not be null. - * @param visited - * The set of visited suppressed exceptions. - */ - private ThrowableProxy(final Throwable throwable, final Set<Throwable> visited) { - this.throwable = throwable; - this.name = throwable.getClass().getName(); - this.message = throwable.getMessage(); - this.localizedMessage = throwable.getLocalizedMessage(); - final Map<String, CacheEntry> map = new HashMap<>(); - final Stack<Class<?>> stack = ReflectionUtil.getCurrentStackTrace(); - this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace()); - final Throwable throwableCause = throwable.getCause(); - final Set<Throwable> causeVisited = new HashSet<>(1); - this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause, visited, causeVisited); - this.suppressedProxies = this.toSuppressedProxies(throwable, visited); - } - - /** - * Constructs the wrapper for a Throwable that is referenced as the cause by another Throwable. - * - * @param parent - * The Throwable referencing this Throwable. - * @param stack - * The Class stack. - * @param map - * The cache containing the packaging data. - * @param cause - * The Throwable to wrap. - * @param suppressedVisited TODO - * @param causeVisited TODO - */ - private ThrowableProxy(final Throwable parent, final Stack<Class<?>> stack, final Map<String, CacheEntry> map, - final Throwable cause, final Set<Throwable> suppressedVisited, final Set<Throwable> causeVisited) { - causeVisited.add(cause); - this.throwable = cause; - this.name = cause.getClass().getName(); - this.message = this.throwable.getMessage(); - this.localizedMessage = this.throwable.getLocalizedMessage(); - this.extendedStackTrace = this.toExtendedStackTrace(stack, map, parent.getStackTrace(), cause.getStackTrace()); - final Throwable causeCause = cause.getCause(); - this.causeProxy = causeCause == null || causeVisited.contains(causeCause) ? null : new ThrowableProxy(parent, - stack, map, causeCause, suppressedVisited, causeVisited); - this.suppressedProxies = this.toSuppressedProxies(cause, suppressedVisited); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { - return false; - } - final ThrowableProxy other = (ThrowableProxy) obj; - if (this.causeProxy == null) { - if (other.causeProxy != null) { - return false; - } - } else if (!this.causeProxy.equals(other.causeProxy)) { - return false; - } - if (this.commonElementCount != other.commonElementCount) { - return false; - } - if (this.name == null) { - if (other.name != null) { - return false; - } - } else if (!this.name.equals(other.name)) { - return false; - } - if (!Arrays.equals(this.extendedStackTrace, other.extendedStackTrace)) { - return false; - } - if (!Arrays.equals(this.suppressedProxies, other.suppressedProxies)) { - return false; - } - return true; - } - - private void formatCause(final StringBuilder sb, final String prefix, final ThrowableProxy cause, - final List<String> ignorePackages, TextRenderer textRenderer) { - formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages, textRenderer); - } - - private void formatThrowableProxy(final StringBuilder sb, final String prefix, final String causeLabel, - final ThrowableProxy throwableProxy, final List<String> ignorePackages, TextRenderer textRenderer) { - if (throwableProxy == null) { - return; - } - textRenderer.render(prefix, sb, "Prefix"); - textRenderer.render(causeLabel, sb, "CauseLabel"); - throwableProxy.renderOn(sb, textRenderer); - textRenderer.render(EOL_STR, sb, "Text"); - this.formatElements(sb, prefix, throwableProxy.commonElementCount, - throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, ignorePackages, textRenderer); - this.formatSuppressed(sb, prefix + TAB, throwableProxy.suppressedProxies, ignorePackages, textRenderer); - this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages, textRenderer); - } - - void renderOn(StringBuilder output, TextRenderer textRenderer) { - final String msg = this.message; - textRenderer.render(this.name, output, "Name"); - if (msg != null) { - textRenderer.render(": ", output, "NameMessageSeparator"); - textRenderer.render(msg, output, "Message"); - } - } - - private void formatSuppressed(final StringBuilder sb, final String prefix, final ThrowableProxy[] suppressedProxies, - final List<String> ignorePackages, TextRenderer textRenderer) { - if (suppressedProxies == null) { - return; - } - for (final ThrowableProxy suppressedProxy : suppressedProxies) { - formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, suppressedProxy, ignorePackages, textRenderer); - } - } - - private void formatElements(final StringBuilder sb, final String prefix, final int commonCount, - final StackTraceElement[] causedTrace, final ExtendedStackTraceElement[] extStackTrace, - final List<String> ignorePackages, TextRenderer textRenderer) { - if (ignorePackages == null || ignorePackages.isEmpty()) { - for (final ExtendedStackTraceElement element : extStackTrace) { - this.formatEntry(element, sb, prefix, textRenderer); - } - } 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, textRenderer); - count = 0; - } - this.formatEntry(extStackTrace[i], sb, prefix, textRenderer); - } else { - ++count; - } - } - if (count > 0) { - appendSuppressedCount(sb, prefix, count, textRenderer); - } - } - if (commonCount != 0) { - textRenderer.render(prefix, sb, "Prefix"); - textRenderer.render("\t... ", sb, "More"); - textRenderer.render(Integer.toString(commonCount), sb, "More"); - textRenderer.render(" more", sb, "More"); - textRenderer.render(EOL_STR, sb, "Text"); - } - } - - private void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count, TextRenderer textRenderer) { - textRenderer.render(prefix, sb, "Prefix"); - if (count == 1) { - textRenderer.render("\t... ", sb, "Suppressed"); - } else { - textRenderer.render("\t... suppressed ", sb, "Suppressed"); - textRenderer.render(Integer.toString(count), sb, "Suppressed"); - textRenderer.render(" lines", sb, "Suppressed"); - } - textRenderer.render(EOL_STR, sb, "Text"); - } - - private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb, final String prefix, TextRenderer textRenderer) { - textRenderer.render(prefix, sb, "Prefix"); - textRenderer.render("\tat ", sb, "At"); - extStackTraceElement.renderOn(sb, textRenderer); - textRenderer.render(EOL_STR, sb, "Text"); - } - - /** - * Formats the specified Throwable. - * - * @param sb - * StringBuilder to contain the formatted Throwable. - * @param cause - * The Throwable to format. - */ - public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause) { - this.formatWrapper(sb, cause, null, PlainTextRenderer.getInstance()); - } - - /** - * Formats the specified Throwable. - * - * @param sb - * StringBuilder to contain the formatted Throwable. - * @param cause - * The Throwable to format. - * @param ignorePackages - * The List of packages to be suppressed from the trace. - */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages) { - this.formatWrapper(sb, cause, ignorePackages, PlainTextRenderer.getInstance()); - } - - /** - * Formats the specified Throwable. - * - * @param sb - * StringBuilder to contain the formatted Throwable. - * @param cause - * The Throwable to format. - * @param ignorePackages - * The List of packages to be suppressed from the trace. - * @param textRenderer - * The text render - */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages, final TextRenderer textRenderer) { - final Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null; - if (caused != null) { - this.formatWrapper(sb, cause.causeProxy, ignorePackages, textRenderer); - sb.append(WRAPPED_BY_LABEL); - } - cause.renderOn(sb, textRenderer); - textRenderer.render(EOL_STR, sb, "Text"); - this.formatElements(sb, Strings.EMPTY, cause.commonElementCount, - cause.getThrowable().getStackTrace(), cause.extendedStackTrace, ignorePackages, textRenderer); - } - - public ThrowableProxy getCauseProxy() { - return this.causeProxy; - } - - /** - * Formats the Throwable that is the cause of this Throwable. - * - * @return The formatted Throwable that caused this Throwable. - */ - public String getCauseStackTraceAsString() { - return this.getCauseStackTraceAsString(null, PlainTextRenderer.getInstance()); - } - - /** - * Formats the Throwable that is the cause of this Throwable. - * - * @param packages - * The List of packages to be suppressed from the trace. - * @return The formatted Throwable that caused this Throwable. - */ - public String getCauseStackTraceAsString(final List<String> packages) { - return getCauseStackTraceAsString(packages, PlainTextRenderer.getInstance()); - } - - /** - * Formats the Throwable that is the cause of this Throwable. - * - * @param ignorePackages - * The List of packages to be suppressed from the trace. - * @param textRenderer - * the text renderer - * @return The formatted Throwable that caused this Throwable. - */ - public String getCauseStackTraceAsString(final List<String> ignorePackages, final TextRenderer textRenderer) { - final StringBuilder sb = new StringBuilder(); - if (this.causeProxy != null) { - this.formatWrapper(sb, this.causeProxy, ignorePackages, textRenderer); - sb.append(WRAPPED_BY_LABEL); - } - this.renderOn(sb, textRenderer); - textRenderer.render(EOL_STR, sb, "Text"); - this.formatElements(sb, Strings.EMPTY, 0, this.throwable.getStackTrace(), this.extendedStackTrace, - ignorePackages, textRenderer); - return sb.toString(); - } - - /** - * Returns the number of elements that are being omitted because they are common with the parent Throwable's stack - * trace. - * - * @return The number of elements omitted from the stack trace. - */ - public int getCommonElementCount() { - return this.commonElementCount; - } - - /** - * Gets the stack trace including packaging information. - * - * @return The stack trace including packaging information. - */ - public ExtendedStackTraceElement[] getExtendedStackTrace() { - return this.extendedStackTrace; - } - - /** - * Format the stack trace including packaging information. - * - * @return The formatted stack trace including packaging information. - */ - public String getExtendedStackTraceAsString() { - return this.getExtendedStackTraceAsString(null, PlainTextRenderer.getInstance()); - } - - /** - * Format the stack trace including packaging information. - * - * @param ignorePackages - * List of packages to be ignored in the trace. - * @return The formatted stack trace including packaging information. - */ - public String getExtendedStackTraceAsString(final List<String> ignorePackages) { - return getExtendedStackTraceAsString(ignorePackages, PlainTextRenderer.getInstance()); - } - - /** - * Format the stack trace including packaging information. - * - * @param ignorePackages - * List of packages to be ignored in the trace. - * @param textRenderer - * The message renderer - * @return The formatted stack trace including packaging information. - */ - public String getExtendedStackTraceAsString(final List<String> ignorePackages, final TextRenderer textRenderer) { - final StringBuilder sb = new StringBuilder(1024); - textRenderer.render(name, sb, "Name"); - textRenderer.render(": ", sb, "NameMessageSeparator"); - textRenderer.render(this.message, sb, "Message"); - textRenderer.render(EOL_STR, sb, "Text"); - final StackTraceElement[] causedTrace = this.throwable != null ? this.throwable.getStackTrace() : null; - this.formatElements(sb, Strings.EMPTY, 0, causedTrace, this.extendedStackTrace, ignorePackages, textRenderer); - this.formatSuppressed(sb, TAB, this.suppressedProxies, ignorePackages, textRenderer); - this.formatCause(sb, Strings.EMPTY, this.causeProxy, ignorePackages, textRenderer); - return sb.toString(); - } - - public String getLocalizedMessage() { - return this.localizedMessage; - } - - public String getMessage() { - return this.message; - } - - /** - * Return the FQCN of the Throwable. - * - * @return The FQCN of the Throwable. - */ - public String getName() { - return this.name; - } - - public StackTraceElement[] getStackTrace() { - return this.throwable == null ? null : this.throwable.getStackTrace(); - } - - /** - * Gets proxies for suppressed exceptions. - * - * @return proxies for suppressed exceptions. - */ - public ThrowableProxy[] getSuppressedProxies() { - return this.suppressedProxies; - } - - /** - * Format the suppressed Throwables. - * - * @return The formatted suppressed Throwables. - */ - public String getSuppressedStackTrace() { - final ThrowableProxy[] suppressed = this.getSuppressedProxies(); - if (suppressed == null || suppressed.length == 0) { - return Strings.EMPTY; - } - final StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:").append(EOL); - for (final ThrowableProxy proxy : suppressed) { - sb.append(proxy.getExtendedStackTraceAsString()); - } - return sb.toString(); - } - - /** - * The throwable or null if this object is deserialized from XML or JSON. - * - * @return The throwable or null if this object is deserialized from XML or JSON. - */ - public Throwable getThrowable() { - return this.throwable; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (this.causeProxy == null ? 0 : this.causeProxy.hashCode()); - result = prime * result + this.commonElementCount; - result = prime * result + (this.extendedStackTrace == null ? 0 : Arrays.hashCode(this.extendedStackTrace)); - result = prime * result + (this.suppressedProxies == null ? 0 : Arrays.hashCode(this.suppressedProxies)); - result = prime * result + (this.name == null ? 0 : this.name.hashCode()); - return result; - } - - private boolean ignoreElement(final StackTraceElement element, final List<String> ignorePackages) { - if (ignorePackages != null) { - final String className = element.getClassName(); - for (final String pkg : ignorePackages) { - if (className.startsWith(pkg)) { - return true; - } - } - } - return false; - } - - /** - * Loads classes not located via Reflection.getCallerClass. - * - * @param lastLoader - * The ClassLoader that loaded the Class that called this Class. - * @param className - * The name of the Class. - * @return The Class object for the Class or null if it could not be located. - */ - private Class<?> loadClass(final ClassLoader lastLoader, final String className) { - // XXX: this is overly complicated - Class<?> clazz; - if (lastLoader != null) { - try { - clazz = Loader.initializeClass(className, lastLoader); - if (clazz != null) { - return clazz; - } - } catch (final Throwable ignore) { - // Ignore exception. - } - } - try { - clazz = Loader.loadClass(className); - } catch (final ClassNotFoundException ignored) { - return initializeClass(className); - } catch (final NoClassDefFoundError ignored) { - return initializeClass(className); - } - return clazz; - } - - private Class<?> initializeClass(final String className) { - try { - return Loader.initializeClass(className, this.getClass().getClassLoader()); - } catch (final ClassNotFoundException ignore) { - return null; - } catch (final NoClassDefFoundError ignore) { - return null; - } - } - - /** - * Construct the CacheEntry from the Class's information. - * - * @param stackTraceElement - * The stack trace element - * @param callerClass - * The Class. - * @param exact - * True if the class was obtained via Reflection.getCallerClass. - * - * @return The CacheEntry. - */ - private CacheEntry toCacheEntry(final StackTraceElement stackTraceElement, final Class<?> callerClass, - final boolean exact) { - String location = "?"; - String version = "?"; - ClassLoader lastLoader = null; - if (callerClass != null) { - try { - final CodeSource source = callerClass.getProtectionDomain().getCodeSource(); - if (source != null) { - final URL locationURL = source.getLocation(); - if (locationURL != null) { - final String str = locationURL.toString().replace('\\', '/'); - int index = str.lastIndexOf("/"); - if (index >= 0 && index == str.length() - 1) { - index = str.lastIndexOf("/", index - 1); - location = str.substring(index + 1); - } else { - location = str.substring(index + 1); - } - } - } - } catch (final Exception ex) { - // Ignore the exception. - } - final Package pkg = callerClass.getPackage(); - if (pkg != null) { - final String ver = pkg.getImplementationVersion(); - if (ver != null) { - version = ver; - } - } - lastLoader = callerClass.getClassLoader(); - } - return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader); - } - - /** - * Resolve all the stack entries in this stack trace that are not common with the parent. - * - * @param stack - * The callers Class stack. - * @param map - * The cache of CacheEntry objects. - * @param rootTrace - * The first stack trace resolve or null. - * @param stackTrace - * The stack trace being resolved. - * @return The StackTracePackageElement array. - */ - ExtendedStackTraceElement[] toExtendedStackTrace(final Stack<Class<?>> stack, final Map<String, CacheEntry> map, - final StackTraceElement[] rootTrace, final StackTraceElement[] stackTrace) { - int stackLength; - if (rootTrace != null) { - int rootIndex = rootTrace.length - 1; - int stackIndex = stackTrace.length - 1; - while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) { - --rootIndex; - --stackIndex; - } - this.commonElementCount = stackTrace.length - 1 - stackIndex; - stackLength = stackIndex + 1; - } else { - this.commonElementCount = 0; - stackLength = stackTrace.length; - } - final ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength]; - Class<?> clazz = stack.isEmpty() ? null : stack.peek(); - ClassLoader lastLoader = null; - for (int i = stackLength - 1; i >= 0; --i) { - final StackTraceElement stackTraceElement = stackTrace[i]; - final String className = stackTraceElement.getClassName(); - // The stack returned from getCurrentStack may be missing entries for java.lang.reflect.Method.invoke() - // and its implementation. The Throwable might also contain stack entries that are no longer - // present as those methods have returned. - ExtendedClassInfo extClassInfo; - if (clazz != null && className.equals(clazz.getName())) { - final CacheEntry entry = this.toCacheEntry(stackTraceElement, clazz, true); - extClassInfo = entry.element; - lastLoader = entry.loader; - stack.pop(); - clazz = stack.isEmpty() ? null : stack.peek(); - } else { - final CacheEntry cacheEntry = map.get(className); - if (cacheEntry != null) { - final CacheEntry entry = cacheEntry; - extClassInfo = entry.element; - if (entry.loader != null) { - lastLoader = entry.loader; - } - } else { - final CacheEntry entry = this.toCacheEntry(stackTraceElement, - this.loadClass(lastLoader, className), false); - extClassInfo = entry.element; - map.put(stackTraceElement.toString(), entry); - if (entry.loader != null) { - lastLoader = entry.loader; - } - } - } - extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo); - } - return extStackTrace; - } - - @Override - public String toString() { - final String msg = this.message; - return msg != null ? this.name + ": " + msg : this.name; - } - - private ThrowableProxy[] toSuppressedProxies(final Throwable thrown, Set<Throwable> suppressedVisited) { - try { - final Throwable[] suppressed = thrown.getSuppressed(); - if (suppressed == null) { - return EMPTY_THROWABLE_PROXY_ARRAY; - } - final List<ThrowableProxy> proxies = new ArrayList<>(suppressed.length); - if (suppressedVisited == null) { - suppressedVisited = new HashSet<>(proxies.size()); - } - for (int i = 0; i < suppressed.length; i++) { - final Throwable candidate = suppressed[i]; - if (!suppressedVisited.contains(candidate)) { - suppressedVisited.add(candidate); - proxies.add(new ThrowableProxy(candidate, suppressedVisited)); - } - } - return proxies.toArray(new ThrowableProxy[proxies.size()]); - } catch (final Exception e) { - StatusLogger.getLogger().error(e); - } - return null; - } -} +/* + * 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.impl; + +import java.io.Serializable; +import java.net.URL; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.apache.logging.log4j.core.pattern.PlainTextRenderer; +import org.apache.logging.log4j.core.pattern.TextRenderer; +import org.apache.logging.log4j.core.util.Loader; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; +import org.apache.logging.log4j.util.ReflectionUtil; +import org.apache.logging.log4j.util.Strings; + +/** + * Wraps a Throwable to add packaging information about each stack trace element. + * + * <p> + * A proxy is used to represent a throwable that may not exist in a different class loader or JVM. When an application + * deserializes a ThrowableProxy, the throwable may not be set, but the throwable's information is preserved in other + * fields of the proxy like the message and stack trace. + * </p> + * + * <p> + * TODO: Move this class to org.apache.logging.log4j.core because it is used from LogEvent. + * </p> + * <p> + * TODO: Deserialize: Try to rebuild Throwable if the target exception is in this class loader? + * </p> + */ +public class ThrowableProxy implements Serializable { + + private static final String TAB = "\t"; + 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. + * </p> + */ + static class CacheEntry { + private final ExtendedClassInfo element; + private final ClassLoader loader; + + public CacheEntry(final ExtendedClassInfo element, final ClassLoader loader) { + this.element = element; + this.loader = loader; + } + } + + private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0]; + + private static final char EOL = '\n'; + + private static final String EOL_STR = String.valueOf(EOL); + + private static final long serialVersionUID = -2752771578252251910L; + + private final ThrowableProxy causeProxy; + + private int commonElementCount; + + private final ExtendedStackTraceElement[] extendedStackTrace; + + private final String localizedMessage; + + private final String message; + + private final String name; + + private final ThrowableProxy[] suppressedProxies; + + private final transient Throwable throwable; + + /** + * For JSON and XML IO via Jackson. + */ + @SuppressWarnings("unused") + private ThrowableProxy() { + this.throwable = null; + this.name = null; + this.extendedStackTrace = null; + this.causeProxy = null; + this.message = null; + this.localizedMessage = null; + this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY; + } + + /** + * Constructs the wrapper for the Throwable that includes packaging data. + * + * @param throwable + * The Throwable to wrap, must not be null. + */ + public ThrowableProxy(final Throwable throwable) { + this(throwable, null); + } + + /** + * Constructs the wrapper for the Throwable that includes packaging data. + * + * @param throwable + * The Throwable to wrap, must not be null. + * @param visited + * The set of visited suppressed exceptions. + */ + private ThrowableProxy(final Throwable throwable, final Set<Throwable> visited) { + this.throwable = throwable; + this.name = throwable.getClass().getName(); + this.message = throwable.getMessage(); + this.localizedMessage = throwable.getLocalizedMessage(); + final Map<String, CacheEntry> map = new HashMap<>(); + final Stack<Class<?>> stack = ReflectionUtil.getCurrentStackTrace(); + this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace()); + final Throwable throwableCause = throwable.getCause(); + final Set<Throwable> causeVisited = new HashSet<>(1); + this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause, visited, causeVisited); + this.suppressedProxies = this.toSuppressedProxies(throwable, visited); + } + + /** + * Constructs the wrapper for a Throwable that is referenced as the cause by another Throwable. + * + * @param parent + * The Throwable referencing this Throwable. + * @param stack + * The Class stack. + * @param map + * The cache containing the packaging data. + * @param cause + * The Throwable to wrap. + * @param suppressedVisited TODO + * @param causeVisited TODO + */ + private ThrowableProxy(final Throwable parent, final Stack<Class<?>> stack, final Map<String, CacheEntry> map, + final Throwable cause, final Set<Throwable> suppressedVisited, final Set<Throwable> causeVisited) { + causeVisited.add(cause); + this.throwable = cause; + this.name = cause.getClass().getName(); + this.message = this.throwable.getMessage(); + this.localizedMessage = this.throwable.getLocalizedMessage(); + this.extendedStackTrace = this.toExtendedStackTrace(stack, map, parent.getStackTrace(), cause.getStackTrace()); + final Throwable causeCause = cause.getCause(); + this.causeProxy = causeCause == null || causeVisited.contains(causeCause) ? null : new ThrowableProxy(parent, + stack, map, causeCause, suppressedVisited, causeVisited); + this.suppressedProxies = this.toSuppressedProxies(cause, suppressedVisited); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (this.getClass() != obj.getClass()) { + return false; + } + final ThrowableProxy other = (ThrowableProxy) obj; + if (this.causeProxy == null) { + if (other.causeProxy != null) { + return false; + } + } else if (!this.causeProxy.equals(other.causeProxy)) { + return false; + } + if (this.commonElementCount != other.commonElementCount) { + return false; + } + if (this.name == null) { + if (other.name != null) { + return false; + } + } else if (!this.name.equals(other.name)) { + return false; + } + if (!Arrays.equals(this.extendedStackTrace, other.extendedStackTrace)) { + return false; + } + if (!Arrays.equals(this.suppressedProxies, other.suppressedProxies)) { + return false; + } + return true; + } + + private void formatCause(final StringBuilder sb, final String prefix, final ThrowableProxy cause, + final List<String> ignorePackages, TextRenderer textRenderer) { + formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages, textRenderer); + } + + private void formatThrowableProxy(final StringBuilder sb, final String prefix, final String causeLabel, + final ThrowableProxy throwableProxy, final List<String> ignorePackages, TextRenderer textRenderer) { + if (throwableProxy == null) { + return; + } + textRenderer.render(prefix, sb, "Prefix"); + textRenderer.render(causeLabel, sb, "CauseLabel"); + throwableProxy.renderOn(sb, textRenderer); + textRenderer.render(EOL_STR, sb, "Text"); + this.formatElements(sb, prefix, throwableProxy.commonElementCount, + throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, ignorePackages, textRenderer); + this.formatSuppressed(sb, prefix + TAB, throwableProxy.suppressedProxies, ignorePackages, textRenderer); + this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages, textRenderer); + } + + void renderOn(StringBuilder output, TextRenderer textRenderer) { + final String msg = this.message; + textRenderer.render(this.name, output, "Name"); + if (msg != null) { + textRenderer.render(": ", output, "NameMessageSeparator"); + textRenderer.render(msg, output, "Message"); + } + } + + private void formatSuppressed(final StringBuilder sb, final String prefix, final ThrowableProxy[] suppressedProxies, + final List<String> ignorePackages, TextRenderer textRenderer) { + if (suppressedProxies == null) { + return; + } + for (final ThrowableProxy suppressedProxy : suppressedProxies) { + formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, suppressedProxy, ignorePackages, textRenderer); + } + } + + private void formatElements(final StringBuilder sb, final String prefix, final int commonCount, + final StackTraceElement[] causedTrace, final ExtendedStackTraceElement[] extStackTrace, + final List<String> ignorePackages, TextRenderer textRenderer) { + if (ignorePackages == null || ignorePackages.isEmpty()) { + for (final ExtendedStackTraceElement element : extStackTrace) { + this.formatEntry(element, sb, prefix, textRenderer); + } + } 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, textRenderer); + count = 0; + } + this.formatEntry(extStackTrace[i], sb, prefix, textRenderer); + } else { + ++count; + } + } + if (count > 0) { + appendSuppressedCount(sb, prefix, count, textRenderer); + } + } + if (commonCount != 0) { + textRenderer.render(prefix, sb, "Prefix"); + textRenderer.render("\t... ", sb, "More"); + textRenderer.render(Integer.toString(commonCount), sb, "More"); + textRenderer.render(" more", sb, "More"); + textRenderer.render(EOL_STR, sb, "Text"); + } + } + + private void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count, TextRenderer textRenderer) { + textRenderer.render(prefix, sb, "Prefix"); + if (count == 1) { + textRenderer.render("\t... ", sb, "Suppressed"); + } else { + textRenderer.render("\t... suppressed ", sb, "Suppressed"); + textRenderer.render(Integer.toString(count), sb, "Suppressed"); + textRenderer.render(" lines", sb, "Suppressed"); + } + textRenderer.render(EOL_STR, sb, "Text"); + } + + private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb, final String prefix, TextRenderer textRenderer) { + textRenderer.render(prefix, sb, "Prefix"); + textRenderer.render("\tat ", sb, "At"); + extStackTraceElement.renderOn(sb, textRenderer); + textRenderer.render(EOL_STR, sb, "Text"); + } + + /** + * Formats the specified Throwable. + * + * @param sb + * StringBuilder to contain the formatted Throwable. + * @param cause + * The Throwable to format. + */ + public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause) { + this.formatWrapper(sb, cause, null, PlainTextRenderer.getInstance()); + } + + /** + * Formats the specified Throwable. + * + * @param sb + * StringBuilder to contain the formatted Throwable. + * @param cause + * The Throwable to format. + * @param ignorePackages + * The List of packages to be suppressed from the trace. + */ + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages) { + this.formatWrapper(sb, cause, ignorePackages, PlainTextRenderer.getInstance()); + } + + /** + * Formats the specified Throwable. + * + * @param sb + * StringBuilder to contain the formatted Throwable. + * @param cause + * The Throwable to format. + * @param ignorePackages + * The List of packages to be suppressed from the trace. + * @param textRenderer + * The text render + */ + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages, final TextRenderer textRenderer) { + final Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null; + if (caused != null) { + this.formatWrapper(sb, cause.causeProxy, ignorePackages, textRenderer); + sb.append(WRAPPED_BY_LABEL); + } + cause.renderOn(sb, textRenderer); + textRenderer.render(EOL_STR, sb, "Text"); + this.formatElements(sb, Strings.EMPTY, cause.commonElementCount, + cause.getThrowable().getStackTrace(), cause.extendedStackTrace, ignorePackages, textRenderer); + } + + public ThrowableProxy getCauseProxy() { + return this.causeProxy; + } + + /** + * Formats the Throwable that is the cause of this Throwable. + * + * @return The formatted Throwable that caused this Throwable. + */ + public String getCauseStackTraceAsString() { + return this.getCauseStackTraceAsString(null, PlainTextRenderer.getInstance()); + } + + /** + * Formats the Throwable that is the cause of this Throwable. + * + * @param packages + * The List of packages to be suppressed from the trace. + * @return The formatted Throwable that caused this Throwable. + */ + public String getCauseStackTraceAsString(final List<String> packages) { + return getCauseStackTraceAsString(packages, PlainTextRenderer.getInstance()); + } + + /** + * Formats the Throwable that is the cause of this Throwable. + * + * @param ignorePackages + * The List of packages to be suppressed from the trace. + * @param textRenderer + * the text renderer + * @return The formatted Throwable that caused this Throwable. + */ + public String getCauseStackTraceAsString(final List<String> ignorePackages, final TextRenderer textRenderer) { + final StringBuilder sb = new StringBuilder(); + if (this.causeProxy != null) { + this.formatWrapper(sb, this.causeProxy, ignorePackages, textRenderer); + sb.append(WRAPPED_BY_LABEL); + } + this.renderOn(sb, textRenderer); + textRenderer.render(EOL_STR, sb, "Text"); + this.formatElements(sb, Strings.EMPTY, 0, this.throwable.getStackTrace(), this.extendedStackTrace, + ignorePackages, textRenderer); + return sb.toString(); + } + + /** + * Returns the number of elements that are being omitted because they are common with the parent Throwable's stack + * trace. + * + * @return The number of elements omitted from the stack trace. + */ + public int getCommonElementCount() { + return this.commonElementCount; + } + + /** + * Gets the stack trace including packaging information. + * + * @return The stack trace including packaging information. + */ + public ExtendedStackTraceElement[] getExtendedStackTrace() { + return this.extendedStackTrace; + } + + /** + * Format the stack trace including packaging information. + * + * @return The formatted stack trace including packaging information. + */ + public String getExtendedStackTraceAsString() { + return this.getExtendedStackTraceAsString(null, PlainTextRenderer.getInstance()); + } + + /** + * Format the stack trace including packaging information. + * + * @param ignorePackages + * List of packages to be ignored in the trace. + * @return The formatted stack trace including packaging information. + */ + public String getExtendedStackTraceAsString(final List<String> ignorePackages) { + return getExtendedStackTraceAsString(ignorePackages, PlainTextRenderer.getInstance()); + } + + /** + * Format the stack trace including packaging information. + * + * @param ignorePackages + * List of packages to be ignored in the trace. + * @param textRenderer + * The message renderer + * @return The formatted stack trace including packaging information. + */ + public String getExtendedStackTraceAsString(final List<String> ignorePackages, final TextRenderer textRenderer) { + final StringBuilder sb = new StringBuilder(1024); + textRenderer.render(name, sb, "Name"); + textRenderer.render(": ", sb, "NameMessageSeparator"); + textRenderer.render(this.message, sb, "Message"); + textRenderer.render(EOL_STR, sb, "Text"); + final StackTraceElement[] causedTrace = this.throwable != null ? this.throwable.getStackTrace() : null; + this.formatElements(sb, Strings.EMPTY, 0, causedTrace, this.extendedStackTrace, ignorePackages, textRenderer); + this.formatSuppressed(sb, TAB, this.suppressedProxies, ignorePackages, textRenderer); + this.formatCause(sb, Strings.EMPTY, this.causeProxy, ignorePackages, textRenderer); + return sb.toString(); + } + + public String getLocalizedMessage() { + return this.localizedMessage; + } + + public String getMessage() { + return this.message; + } + + /** + * Return the FQCN of the Throwable. + * + * @return The FQCN of the Throwable. + */ + public String getName() { + return this.name; + } + + public StackTraceElement[] getStackTrace() { + return this.throwable == null ? null : this.throwable.getStackTrace(); + } + + /** + * Gets proxies for suppressed exceptions. + * + * @return proxies for suppressed exceptions. + */ + public ThrowableProxy[] getSuppressedProxies() { + return this.suppressedProxies; + } + + /** + * Format the suppressed Throwables. + * + * @return The formatted suppressed Throwables. + */ + public String getSuppressedStackTrace() { + final ThrowableProxy[] suppressed = this.getSuppressedProxies(); + if (suppressed == null || suppressed.length == 0) { + return Strings.EMPTY; + } + final StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:").append(EOL); + for (final ThrowableProxy proxy : suppressed) { + sb.append(proxy.getExtendedStackTraceAsString()); + } + return sb.toString(); + } + + /** + * The throwable or null if this object is deserialized from XML or JSON. + * + * @return The throwable or null if this object is deserialized from XML or JSON. + */ + public Throwable getThrowable() { + return this.throwable; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.causeProxy == null ? 0 : this.causeProxy.hashCode()); + result = prime * result + this.commonElementCount; + result = prime * result + (this.extendedStackTrace == null ? 0 : Arrays.hashCode(this.extendedStackTrace)); + result = prime * result + (this.suppressedProxies == null ? 0 : Arrays.hashCode(this.suppressedProxies)); + result = prime * result + (this.name == null ? 0 : this.name.hashCode()); + return result; + } + + private boolean ignoreElement(final StackTraceElement element, final List<String> ignorePackages) { + if (ignorePackages != null) { + final String className = element.getClassName(); + for (final String pkg : ignorePackages) { + if (className.startsWith(pkg)) { + return true; + } + } + } + return false; + } + + /** + * Loads classes not located via Reflection.getCallerClass. + * + * @param lastLoader + * The ClassLoader that loaded the Class that called this Class. + * @param className + * The name of the Class. + * @return The Class object for the Class or null if it could not be located. + */ + private Class<?> loadClass(final ClassLoader lastLoader, final String className) { + // XXX: this is overly complicated + Class<?> clazz; + if (lastLoader != null) { + try { + clazz = Loader.initializeClass(className, lastLoader); + if (clazz != null) { + return clazz; + } + } catch (final Throwable ignore) { + // Ignore exception. + } + } + try { + clazz = LoaderUtil.loadClass(className); + } catch (final ClassNotFoundException ignored) { + return initializeClass(className); + } catch (final NoClassDefFoundError ignored) { + return initializeClass(className); + } + return clazz; + } + + private Class<?> initializeClass(final String className) { + try { + return Loader.initializeClass(className, this.getClass().getClassLoader()); + } catch (final ClassNotFoundException ignore) { + return null; + } catch (final NoClassDefFoundError ignore) { + return null; + } + } + + /** + * Construct the CacheEntry from the Class's information. + * + * @param stackTraceElement + * The stack trace element + * @param callerClass + * The Class. + * @param exact + * True if the class was obtained via Reflection.getCallerClass. + * + * @return The CacheEntry. + */ + private CacheEntry toCacheEntry(final StackTraceElement stackTraceElement, final Class<?> callerClass, + final boolean exact) { + String location = "?"; + String version = "?"; + ClassLoader lastLoader = null; + if (callerClass != null) { + try { + final CodeSource source = callerClass.getProtectionDomain().getCodeSource(); + if (source != null) { + final URL locationURL = source.getLocation(); + if (locationURL != null) { + final String str = locationURL.toString().replace('\\', '/'); + int index = str.lastIndexOf("/"); + if (index >= 0 && index == str.length() - 1) { + index = str.lastIndexOf("/", index - 1); + location = str.substring(index + 1); + } else { + location = str.substring(index + 1); + } + } + } + } catch (final Exception ex) { + // Ignore the exception. + } + final Package pkg = callerClass.getPackage(); + if (pkg != null) { + final String ver = pkg.getImplementationVersion(); + if (ver != null) { + version = ver; + } + } + lastLoader = callerClass.getClassLoader(); + } + return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader); + } + + /** + * Resolve all the stack entries in this stack trace that are not common with the parent. + * + * @param stack + * The callers Class stack. + * @param map + * The cache of CacheEntry objects. + * @param rootTrace + * The first stack trace resolve or null. + * @param stackTrace + * The stack trace being resolved. + * @return The StackTracePackageElement array. + */ + ExtendedStackTraceElement[] toExtendedStackTrace(final Stack<Class<?>> stack, final Map<String, CacheEntry> map, + final StackTraceElement[] rootTrace, final StackTraceElement[] stackTrace) { + int stackLength; + if (rootTrace != null) { + int rootIndex = rootTrace.length - 1; + int stackIndex = stackTrace.length - 1; + while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) { + --rootIndex; + --stackIndex; + } + this.commonElementCount = stackTrace.length - 1 - stackIndex; + stackLength = stackIndex + 1; + } else { + this.commonElementCount = 0; + stackLength = stackTrace.length; + } + final ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength]; + Class<?> clazz = stack.isEmpty() ? null : stack.peek(); + ClassLoader lastLoader = null; + for (int i = stackLength - 1; i >= 0; --i) { + final StackTraceElement stackTraceElement = stackTrace[i]; + final String className = stackTraceElement.getClassName(); + // The stack returned from getCurrentStack may be missing entries for java.lang.reflect.Method.invoke() + // and its implementation. The Throwable might also contain stack entries that are no longer + // present as those methods have returned. + ExtendedClassInfo extClassInfo; + if (clazz != null && className.equals(clazz.getName())) { + final CacheEntry entry = this.toCacheEntry(stackTraceElement, clazz, true); + extClassInfo = entry.element; + lastLoader = entry.loader; + stack.pop(); + clazz = stack.isEmpty() ? null : stack.peek(); + } else { + final CacheEntry cacheEntry = map.get(className); + if (cacheEntry != null) { + final CacheEntry entry = cacheEntry; + extClassInfo = entry.element; + if (entry.loader != null) { + lastLoader = entry.loader; + } + } else { + final CacheEntry entry = this.toCacheEntry(stackTraceElement, + this.loadClass(lastLoader, className), false); + extClassInfo = entry.element; + map.put(stackTraceElement.toString(), entry); + if (entry.loader != null) { + lastLoader = entry.loader; + } + } + } + extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo); + } + return extStackTrace; + } + + @Override + public String toString() { + final String msg = this.message; + return msg != null ? this.name + ": " + msg : this.name; + } + + private ThrowableProxy[] toSuppressedProxies(final Throwable thrown, Set<Throwable> suppressedVisited) { + try { + final Throwable[] suppressed = thrown.getSuppressed(); + if (suppressed == null) { + return EMPTY_THROWABLE_PROXY_ARRAY; + } + final List<ThrowableProxy> proxies = new ArrayList<>(suppressed.length); + if (suppressedVisited == null) { + suppressedVisited = new HashSet<>(proxies.size()); + } + for (int i = 0; i < suppressed.length; i++) { + final Throwable candidate = suppressed[i]; + if (!suppressedVisited.contains(candidate)) { + suppressedVisited.add(candidate); + proxies.add(new ThrowableProxy(candidate, suppressedVisited)); + } + } + return proxies.toArray(new ThrowableProxy[proxies.size()]); + } catch (final Exception e) { + StatusLogger.getLogger().error(e); + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MulticastDnsAdvertiser.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MulticastDnsAdvertiser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MulticastDnsAdvertiser.java index 479d25f..ea5c2e8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MulticastDnsAdvertiser.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MulticastDnsAdvertiser.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; /** * Advertise an entity via ZeroConf/MulticastDNS and the JmDNS library. @@ -186,8 +187,8 @@ public class MulticastDnsAdvertiser implements Advertiser { private static Object initializeJmDns() { try { - jmDNSClass = Loader.loadClass("javax.jmdns.JmDNS"); - serviceInfoClass = Loader.loadClass("javax.jmdns.ServiceInfo"); + jmDNSClass = LoaderUtil.loadClass("javax.jmdns.JmDNS"); + serviceInfoClass = LoaderUtil.loadClass("javax.jmdns.ServiceInfo"); // if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API boolean isVersion3 = false; try { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java index 9e88c09..6400294 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java @@ -215,19 +215,6 @@ public final class Loader { } /** - * Load a Class by name. Note that unlike {@link ClassLoader#loadClass(String) ClassLoader.loadClass}, this method - * will initialize the class as well if it hasn't been already. This is equivalent to the calling the - * {@link ClassLoader#loadClass(String, boolean) protected version} with the second parameter equal to {@code true}. - * - * @param className The class name. - * @return The Class. - * @throws ClassNotFoundException if the Class could not be found. - */ - public static Class<?> loadClass(final String className) throws ClassNotFoundException { - return LoaderUtil.loadClass(className); - } - - /** * Loads and initializes a named Class using a given ClassLoader. * * @param className The class name. http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java index aad31be..9970d61 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java @@ -21,6 +21,7 @@ import java.util.Properties; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.Strings; @@ -225,7 +226,7 @@ public final class OptionConverter { final Object defaultValue) { if (className != null) { try { - final Class<?> classObj = Loader.loadClass(className); + final Class<?> classObj = LoaderUtil.loadClass(className); if (!superClass.isAssignableFrom(classObj)) { LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable.", className, superClass.getName()); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-core/src/test/java/org/apache/logging/log4j/core/util/Profiler.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/Profiler.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/Profiler.java index 41c03cd..4e28860 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/Profiler.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/Profiler.java @@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.util; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.Strings; /** @@ -32,8 +33,8 @@ public final class Profiler { static { try { - controllerClazz = Loader.loadClass("com.yourkit.api.Controller"); - profilingModes = Loader.loadClass("com.yourkit.api.ProfilingModes"); + controllerClazz = LoaderUtil.loadClass("com.yourkit.api.Controller"); + profilingModes = LoaderUtil.loadClass("com.yourkit.api.ProfilingModes"); try { profiler = controllerClazz.getConstructor().newInstance(); } catch (final Exception e) { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbProvider.java ---------------------------------------------------------------------- diff --git a/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbProvider.java b/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbProvider.java index 1f9f0e7..bb4a175 100644 --- a/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbProvider.java +++ b/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbProvider.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.NameUtil; import org.apache.logging.log4j.nosql.appender.NoSqlProvider; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.Strings; import org.lightcouch.CouchDbClient; import org.lightcouch.CouchDbProperties; @@ -93,7 +94,7 @@ public final class CouchDbProvider implements NoSqlProvider<CouchDbConnection> { String description; if (Strings.isNotEmpty(factoryClassName) && Strings.isNotEmpty(factoryMethodName)) { try { - final Class<?> factoryClass = Loader.loadClass(factoryClassName); + final Class<?> factoryClass = LoaderUtil.loadClass(factoryClassName); final Method method = factoryClass.getMethod(factoryMethodName); final Object object = method.invoke(null); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5f756e36/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/mongodb/MongoDbProvider.java ---------------------------------------------------------------------- diff --git a/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/mongodb/MongoDbProvider.java b/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/mongodb/MongoDbProvider.java index 142a73a..0b85d1f 100644 --- a/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/mongodb/MongoDbProvider.java +++ b/log4j-nosql/src/main/java/org/apache/logging/log4j/nosql/appender/mongodb/MongoDbProvider.java @@ -30,6 +30,7 @@ import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.NameUtil; import org.apache.logging.log4j.nosql.appender.NoSqlProvider; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.Strings; import com.mongodb.DB; @@ -108,7 +109,7 @@ public final class MongoDbProvider implements NoSqlProvider<MongoDbConnection> { String description; if (Strings.isNotEmpty(factoryClassName) && Strings.isNotEmpty(factoryMethodName)) { try { - final Class<?> factoryClass = Loader.loadClass(factoryClassName); + final Class<?> factoryClass = LoaderUtil.loadClass(factoryClassName); final Method method = factoryClass.getMethod(factoryMethodName); final Object object = method.invoke(null); @@ -206,7 +207,7 @@ public final class MongoDbProvider implements NoSqlProvider<MongoDbConnection> { if (Strings.isNotEmpty(writeConcernConstant)) { if (Strings.isNotEmpty(writeConcernConstantClassName)) { try { - final Class<?> writeConcernConstantClass = Loader.loadClass(writeConcernConstantClassName); + final Class<?> writeConcernConstantClass = LoaderUtil.loadClass(writeConcernConstantClassName); final Field field = writeConcernConstantClass.getField(writeConcernConstant); writeConcern = (WriteConcern) field.get(null); } catch (final Exception e) {