Repository: logging-log4j2
Updated Branches:
  refs/heads/master 7333573d9 -> de6593f5f


[LOG4J2-2391] Refactor as much logic as possible out of ThrowableProxy

This change isolates initialization logic into a new
ThrowableProxyHelper class, and rendering logic
into ThrowableProxyRenderer.

Both new utility classes are package private. ThrowableProxy public
API has not changed.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/de6593f5
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/de6593f5
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/de6593f5

Branch: refs/heads/master
Commit: de6593f5f3b5acbb2cdb4ee91e4e1f472db5dcbb
Parents: 7333573
Author: Carter Kozak <cko...@apache.org>
Authored: Tue Aug 21 11:58:20 2018 -0400
Committer: Carter Kozak <cko...@apache.org>
Committed: Mon Sep 3 19:32:26 2018 -0400

----------------------------------------------------------------------
 .../logging/log4j/core/impl/ThrowableProxy.java | 379 ++-----------------
 .../log4j/core/impl/ThrowableProxyHelper.java   | 234 ++++++++++++
 .../log4j/core/impl/ThrowableProxyRenderer.java | 217 +++++++++++
 .../log4j/core/impl/ThrowableProxyTest.java     |   8 +-
 4 files changed, 478 insertions(+), 360 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de6593f5/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 42ed186..c948a0c 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
@@ -17,9 +17,6 @@
 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;
@@ -30,9 +27,6 @@ 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.StackLocatorUtil;
 import org.apache.logging.log4j.util.Strings;
 
@@ -54,29 +48,6 @@ import org.apache.logging.log4j.util.Strings;
  */
 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);
@@ -110,7 +81,7 @@ public class ThrowableProxy implements Serializable {
         this.causeProxy = null;
         this.message = null;
         this.localizedMessage = null;
-        this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY;
+        this.suppressedProxies = 
ThrowableProxyHelper.EMPTY_THROWABLE_PROXY_ARRAY;
     }
 
     /**
@@ -128,19 +99,19 @@ public class ThrowableProxy implements Serializable {
      * @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) {
+    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 Map<String, ThrowableProxyHelper.CacheEntry> map = new 
HashMap<>();
         final Stack<Class<?>> stack = StackLocatorUtil.getCurrentStackTrace();
-        this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, 
throwable.getStackTrace());
+        this.extendedStackTrace = 
ThrowableProxyHelper.toExtendedStackTrace(this, 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);
+        this.suppressedProxies = 
ThrowableProxyHelper.toSuppressedProxies(throwable, visited);
     }
 
     /**
@@ -153,7 +124,8 @@ public class ThrowableProxy implements Serializable {
      * @param suppressedVisited TODO
      * @param causeVisited      TODO
      */
-    private ThrowableProxy(final Throwable parent, final Stack<Class<?>> 
stack, final Map<String, CacheEntry> map,
+    private ThrowableProxy(final Throwable parent, final Stack<Class<?>> stack,
+                           final Map<String, ThrowableProxyHelper.CacheEntry> 
map,
                            final Throwable cause, final Set<Throwable> 
suppressedVisited,
                            final Set<Throwable> causeVisited) {
         causeVisited.add(cause);
@@ -161,11 +133,11 @@ public class ThrowableProxy implements Serializable {
         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());
+        this.extendedStackTrace = 
ThrowableProxyHelper.toExtendedStackTrace(this, 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);
+        this.suppressedProxies = 
ThrowableProxyHelper.toSuppressedProxies(cause, suppressedVisited);
     }
 
     @Override
@@ -206,111 +178,6 @@ public class ThrowableProxy implements Serializable {
         return true;
     }
 
-    private void formatCause(final StringBuilder sb, final String prefix, 
final ThrowableProxy cause,
-                             final List<String> ignorePackages, final 
TextRenderer textRenderer, final String suffix, String lineSeparator) {
-        formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, 
ignorePackages, textRenderer, suffix, lineSeparator);
-    }
-
-    private void formatThrowableProxy(final StringBuilder sb, final String 
prefix, final String causeLabel,
-                                      final ThrowableProxy throwableProxy, 
final List<String> ignorePackages,
-                                      final TextRenderer textRenderer, final 
String suffix, String lineSeparator) {
-        if (throwableProxy == null) {
-            return;
-        }
-        textRenderer.render(prefix, sb, "Prefix");
-        textRenderer.render(causeLabel, sb, "CauseLabel");
-        throwableProxy.renderOn(sb, textRenderer);
-        renderSuffix(suffix, sb, textRenderer);
-        textRenderer.render(lineSeparator, sb, "Text");
-        this.formatElements(sb, prefix, throwableProxy.commonElementCount,
-            throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, 
ignorePackages, textRenderer, suffix, lineSeparator);
-        this.formatSuppressed(sb, prefix + TAB, 
throwableProxy.suppressedProxies, ignorePackages, textRenderer, suffix, 
lineSeparator);
-        this.formatCause(sb, prefix, throwableProxy.causeProxy, 
ignorePackages, textRenderer, suffix, lineSeparator);
-    }
-
-    void renderOn(final StringBuilder output, final 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, final 
TextRenderer textRenderer, final String suffix, String lineSeparator) {
-        if (suppressedProxies == null) {
-            return;
-        }
-        for (final ThrowableProxy suppressedProxy : suppressedProxies) {
-            formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, 
suppressedProxy, ignorePackages, textRenderer, suffix, lineSeparator);
-        }
-    }
-
-    private void formatElements(final StringBuilder sb, final String prefix, 
final int commonCount,
-                                final StackTraceElement[] causedTrace, final 
ExtendedStackTraceElement[] extStackTrace,
-                                final List<String> ignorePackages, final 
TextRenderer textRenderer, final String suffix, String lineSeparator) {
-        if (ignorePackages == null || ignorePackages.isEmpty()) {
-            for (final ExtendedStackTraceElement element : extStackTrace) {
-                this.formatEntry(element, sb, prefix, textRenderer, suffix, 
lineSeparator);
-            }
-        } 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, 
suffix, lineSeparator);
-                        count = 0;
-                    }
-                    this.formatEntry(extStackTrace[i], sb, prefix, 
textRenderer, suffix, lineSeparator);
-                } else {
-                    ++count;
-                }
-            }
-            if (count > 0) {
-                appendSuppressedCount(sb, prefix, count, textRenderer, suffix, 
lineSeparator);
-            }
-        }
-        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");
-            renderSuffix(suffix, sb, textRenderer);
-            textRenderer.render(lineSeparator, sb, "Text");
-        }
-    }
-
-    private void renderSuffix(final String suffix, final StringBuilder sb, 
final TextRenderer textRenderer) {
-        if (!suffix.isEmpty()) {
-            textRenderer.render(" ", sb, "Suffix");
-            textRenderer.render(suffix, sb, "Suffix");
-        }
-    }
-
-    private void appendSuppressedCount(final StringBuilder sb, final String 
prefix, final int count,
-                                       final TextRenderer textRenderer, final 
String suffix, String lineSeparator) {
-        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");
-        }
-        renderSuffix(suffix, sb, textRenderer);
-        textRenderer.render(lineSeparator, sb, "Text");
-    }
-
-    private void formatEntry(final ExtendedStackTraceElement 
extStackTraceElement, final StringBuilder sb,
-                             final String prefix, final TextRenderer 
textRenderer, final String suffix, String lineSeparator) {
-        textRenderer.render(prefix, sb, "Prefix");
-        textRenderer.render("\tat ", sb, "At");
-        extStackTraceElement.renderOn(sb, textRenderer);
-        renderSuffix(suffix, sb, textRenderer);
-        textRenderer.render(lineSeparator, sb, "Text");
-    }
-
     /**
      * Formats the specified Throwable.
      *  @param sb    StringBuilder to contain the formatted Throwable.
@@ -359,17 +226,7 @@ public class ThrowableProxy implements Serializable {
     @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
     public void formatWrapper(final StringBuilder sb, final ThrowableProxy 
cause, final List<String> ignorePackages,
                               final TextRenderer textRenderer, final String 
suffix, final String lineSeparator) {
-        final Throwable caused = cause.getCauseProxy() != null ? 
cause.getCauseProxy().getThrowable() : null;
-        if (caused != null) {
-            this.formatWrapper(sb, cause.causeProxy, ignorePackages, 
textRenderer, suffix, lineSeparator);
-            sb.append(WRAPPED_BY_LABEL);
-            renderSuffix(suffix, sb, textRenderer);
-        }
-        cause.renderOn(sb, textRenderer);
-        renderSuffix(suffix, sb, textRenderer);
-        textRenderer.render(lineSeparator, sb, "Text");
-        this.formatElements(sb, Strings.EMPTY, cause.commonElementCount,
-            cause.getThrowable().getStackTrace(), cause.extendedStackTrace, 
ignorePackages, textRenderer, suffix, lineSeparator);
+        ThrowableProxyRenderer.formatWrapper(sb,  cause, ignorePackages, 
textRenderer, suffix, lineSeparator);
     }
 
     public ThrowableProxy getCauseProxy() {
@@ -420,16 +277,7 @@ public class ThrowableProxy implements Serializable {
      */
     public String getCauseStackTraceAsString(final List<String> 
ignorePackages, final TextRenderer textRenderer, final String suffix, final 
String lineSeparator) {
         final StringBuilder sb = new StringBuilder();
-        if (this.causeProxy != null) {
-            this.formatWrapper(sb, this.causeProxy, ignorePackages, 
textRenderer, suffix, lineSeparator);
-            sb.append(WRAPPED_BY_LABEL);
-            renderSuffix(suffix, sb, textRenderer);
-        }
-        this.renderOn(sb, textRenderer);
-        renderSuffix(suffix, sb, textRenderer);
-        textRenderer.render(lineSeparator, sb, "Text");
-        this.formatElements(sb, Strings.EMPTY, 0, 
this.throwable.getStackTrace(), this.extendedStackTrace,
-            ignorePackages, textRenderer, suffix, lineSeparator);
+        ThrowableProxyRenderer.formatCauseStackTrace(this, sb, ignorePackages, 
textRenderer, suffix, lineSeparator);
         return sb.toString();
     }
 
@@ -444,6 +292,17 @@ public class ThrowableProxy implements Serializable {
     }
 
     /**
+     * Set the value of {@link ThrowableProxy#commonElementCount}.
+     *
+     * Method is package-private, to be used internally for initialization.
+     *
+     * @param value New value of commonElementCount.
+     */
+    void setCommonElementCount(int value) {
+        this.commonElementCount = value;
+    }
+
+    /**
      * Gets the stack trace including packaging information.
      *
      * @return The stack trace including packaging information.
@@ -519,15 +378,7 @@ public class ThrowableProxy implements Serializable {
      * @param lineSeparator The end-of-line separator.
      */
     public void formatExtendedStackTraceTo(final StringBuilder sb, final 
List<String> ignorePackages, final TextRenderer textRenderer, final String 
suffix, final String lineSeparator) {
-        textRenderer.render(name, sb, "Name");
-        textRenderer.render(": ", sb, "NameMessageSeparator");
-        textRenderer.render(this.message, sb, "Message");
-        renderSuffix(suffix, sb, textRenderer);
-        textRenderer.render(lineSeparator, sb, "Text");
-        final StackTraceElement[] causedTrace = this.throwable != null ? 
this.throwable.getStackTrace() : null;
-        this.formatElements(sb, Strings.EMPTY, 0, causedTrace, 
this.extendedStackTrace, ignorePackages, textRenderer, suffix, lineSeparator);
-        this.formatSuppressed(sb, TAB, this.suppressedProxies, ignorePackages, 
textRenderer, suffix, lineSeparator);
-        this.formatCause(sb, Strings.EMPTY, this.causeProxy, ignorePackages, 
textRenderer, suffix, lineSeparator);
+        ThrowableProxyRenderer.formatExtendedStackTraceTo(this, sb, 
ignorePackages, textRenderer, suffix, lineSeparator);
     }
 
     public String getLocalizedMessage() {
@@ -599,193 +450,9 @@ public class ThrowableProxy implements Serializable {
         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 = lastLoader.loadClass(className);
-                if (clazz != null) {
-                    return clazz;
-                }
-            } catch (final Throwable ignore) {
-                // Ignore exception.
-            }
-        }
-        try {
-            clazz = LoaderUtil.loadClass(className);
-        } catch (final ClassNotFoundException | NoClassDefFoundError e) {
-            return loadClass(className);
-        } catch (final SecurityException e) {
-            return null;
-        }
-        return clazz;
-    }
-
-    private Class<?> loadClass(final String className) {
-        try {
-            return Loader.loadClass(className, 
this.getClass().getClassLoader());
-        } catch (final ClassNotFoundException | NoClassDefFoundError | 
SecurityException e) {
-            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 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;
-                }
-            }
-            try {
-                lastLoader = callerClass.getClassLoader();
-            } catch (final SecurityException e) {
-                lastLoader = null;
-            }
-        }
-        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(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(this.loadClass(lastLoader, className), false);
-                    extClassInfo = entry.element;
-                    map.put(className, 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 || suppressed.length == 0) {
-                return EMPTY_THROWABLE_PROXY_ARRAY;
-            }
-            final List<ThrowableProxy> proxies = new 
ArrayList<>(suppressed.length);
-            if (suppressedVisited == null) {
-                suppressedVisited = new HashSet<>(suppressed.length);
-            }
-            for (int i = 0; i < suppressed.length; i++) {
-                final Throwable candidate = suppressed[i];
-                if (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/de6593f5/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyHelper.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyHelper.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyHelper.java
new file mode 100644
index 0000000..547ca09
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyHelper.java
@@ -0,0 +1,234 @@
+/*
+ * 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 org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * {@link ThrowableProxyHelper} provides utilities required to initialize a 
new {@link ThrowableProxy}
+ * instance.
+ */
+class ThrowableProxyHelper {
+
+    static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new 
ThrowableProxy[0];
+
+    private ThrowableProxyHelper() {
+        // Utility Class
+    }
+
+    /**
+     * Cached StackTracePackageElement and ClassLoader.
+     * <p>
+     * Consider this class private.
+     * </p>
+     */
+    static final class CacheEntry {
+        private final ExtendedClassInfo element;
+        private final ClassLoader loader;
+
+        private CacheEntry(final ExtendedClassInfo element, final ClassLoader 
loader) {
+            this.element = element;
+            this.loader = loader;
+        }
+    }
+
+    /**
+     * Resolve all the stack entries in this stack trace that are not common 
with the parent.
+     *
+     * @param src        Instance for which to build an extended stack trace.
+     * @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.
+     */
+    static ExtendedStackTraceElement[] toExtendedStackTrace(
+            final ThrowableProxy src,
+            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;
+            }
+            src.setCommonElementCount(stackTrace.length - 1 - stackIndex);
+            stackLength = stackIndex + 1;
+        } else {
+            src.setCommonElementCount(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 = toCacheEntry(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 = 
toCacheEntry(ThrowableProxyHelper.loadClass(lastLoader, className), false);
+                    extClassInfo = entry.element;
+                    map.put(className, entry);
+                    if (entry.loader != null) {
+                        lastLoader = entry.loader;
+                    }
+                }
+            }
+            extStackTrace[i] = new 
ExtendedStackTraceElement(stackTraceElement, extClassInfo);
+        }
+        return extStackTrace;
+    }
+
+    static ThrowableProxy[] toSuppressedProxies(final Throwable thrown, 
Set<Throwable> suppressedVisited) {
+        try {
+            final Throwable[] suppressed = thrown.getSuppressed();
+            if (suppressed == null || suppressed.length == 0) {
+                return EMPTY_THROWABLE_PROXY_ARRAY;
+            }
+            final List<ThrowableProxy> proxies = new 
ArrayList<>(suppressed.length);
+            if (suppressedVisited == null) {
+                suppressedVisited = new HashSet<>(suppressed.length);
+            }
+            for (int i = 0; i < suppressed.length; i++) {
+                final Throwable candidate = suppressed[i];
+                if (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;
+    }
+
+    /**
+     * Construct the CacheEntry from the Class's information.
+     *
+     * @param callerClass       The Class.
+     * @param exact             True if the class was obtained via 
Reflection.getCallerClass.
+     * @return The CacheEntry.
+     */
+    private static CacheEntry toCacheEntry(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;
+                }
+            }
+            try {
+                lastLoader = callerClass.getClassLoader();
+            } catch (final SecurityException e) {
+                lastLoader = null;
+            }
+        }
+        return new CacheEntry(new ExtendedClassInfo(exact, location, version), 
lastLoader);
+    }
+
+
+    /**
+     * 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 static Class<?> loadClass(final ClassLoader lastLoader, final 
String className) {
+        // XXX: this is overly complicated
+        Class<?> clazz;
+        if (lastLoader != null) {
+            try {
+                clazz = lastLoader.loadClass(className);
+                if (clazz != null) {
+                    return clazz;
+                }
+            } catch (final Throwable ignore) {
+                // Ignore exception.
+            }
+        }
+        try {
+            clazz = LoaderUtil.loadClass(className);
+        } catch (final ClassNotFoundException | NoClassDefFoundError e) {
+            return loadClass(className);
+        } catch (final SecurityException e) {
+            return null;
+        }
+        return clazz;
+    }
+
+    private static Class<?> loadClass(final String className) {
+        try {
+            return Loader.loadClass(className, 
ThrowableProxyHelper.class.getClassLoader());
+        } catch (final ClassNotFoundException | NoClassDefFoundError | 
SecurityException e) {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de6593f5/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyRenderer.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyRenderer.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyRenderer.java
new file mode 100644
index 0000000..d86d2bb
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxyRenderer.java
@@ -0,0 +1,217 @@
+/*
+ * 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 org.apache.logging.log4j.core.pattern.TextRenderer;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.List;
+
+/**
+ * {@link ThrowableProxyRenderer} is an internal utility providing the code to 
render a {@link ThrowableProxy}
+ * to a {@link StringBuilder}.
+ */
+class ThrowableProxyRenderer {
+
+    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: ";
+
+    private ThrowableProxyRenderer() {
+        // Utility Class
+    }
+
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    static void formatWrapper(final StringBuilder sb, final ThrowableProxy 
cause, final List<String> ignorePackages,
+                              final TextRenderer textRenderer, final String 
suffix, final String lineSeparator) {
+        final Throwable caused = cause.getCauseProxy() != null ? 
cause.getCauseProxy().getThrowable() : null;
+        if (caused != null) {
+            formatWrapper(sb, cause.getCauseProxy(), ignorePackages, 
textRenderer, suffix, lineSeparator);
+            sb.append(WRAPPED_BY_LABEL);
+            renderSuffix(suffix, sb, textRenderer);
+        }
+        renderOn(cause, sb, textRenderer);
+        renderSuffix(suffix, sb, textRenderer);
+        textRenderer.render(lineSeparator, sb, "Text");
+        formatElements(sb, Strings.EMPTY, cause.getCommonElementCount(),
+                cause.getThrowable().getStackTrace(), 
cause.getExtendedStackTrace(), ignorePackages, textRenderer, suffix, 
lineSeparator);
+    }
+
+    private static void formatCause(final StringBuilder sb, final String 
prefix, final ThrowableProxy cause,
+                                    final List<String> ignorePackages, final 
TextRenderer textRenderer, final String suffix, String lineSeparator) {
+        formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, 
ignorePackages, textRenderer, suffix, lineSeparator);
+    }
+
+    private static void formatThrowableProxy(final StringBuilder sb, final 
String prefix, final String causeLabel,
+                                             final ThrowableProxy 
throwableProxy, final List<String> ignorePackages,
+                                             final TextRenderer textRenderer, 
final String suffix, String lineSeparator) {
+        if (throwableProxy == null) {
+            return;
+        }
+        textRenderer.render(prefix, sb, "Prefix");
+        textRenderer.render(causeLabel, sb, "CauseLabel");
+        renderOn(throwableProxy, sb, textRenderer);
+        renderSuffix(suffix, sb, textRenderer);
+        textRenderer.render(lineSeparator, sb, "Text");
+        formatElements(sb, prefix, throwableProxy.getCommonElementCount(),
+                throwableProxy.getStackTrace(), 
throwableProxy.getExtendedStackTrace(), ignorePackages, textRenderer, suffix, 
lineSeparator);
+        formatSuppressed(sb, prefix + TAB, 
throwableProxy.getSuppressedProxies(), ignorePackages, textRenderer, suffix, 
lineSeparator);
+        formatCause(sb, prefix, throwableProxy.getCauseProxy(), 
ignorePackages, textRenderer, suffix, lineSeparator);
+    }
+
+    private static void formatSuppressed(final StringBuilder sb, final String 
prefix, final ThrowableProxy[] suppressedProxies,
+                                         final List<String> ignorePackages, 
final TextRenderer textRenderer, final String suffix, String lineSeparator) {
+        if (suppressedProxies == null) {
+            return;
+        }
+        for (final ThrowableProxy suppressedProxy : suppressedProxies) {
+            formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, 
suppressedProxy, ignorePackages, textRenderer, suffix, lineSeparator);
+        }
+    }
+
+    private static void formatElements(final StringBuilder sb, final String 
prefix, final int commonCount,
+                                       final StackTraceElement[] causedTrace, 
final ExtendedStackTraceElement[] extStackTrace,
+                                       final List<String> ignorePackages, 
final TextRenderer textRenderer, final String suffix, String lineSeparator) {
+        if (ignorePackages == null || ignorePackages.isEmpty()) {
+            for (final ExtendedStackTraceElement element : extStackTrace) {
+                formatEntry(element, sb, prefix, textRenderer, suffix, 
lineSeparator);
+            }
+        } else {
+            int count = 0;
+            for (int i = 0; i < extStackTrace.length; ++i) {
+                if (!ignoreElement(causedTrace[i], ignorePackages)) {
+                    if (count > 0) {
+                        appendSuppressedCount(sb, prefix, count, textRenderer, 
suffix, lineSeparator);
+                        count = 0;
+                    }
+                    formatEntry(extStackTrace[i], sb, prefix, textRenderer, 
suffix, lineSeparator);
+                } else {
+                    ++count;
+                }
+            }
+            if (count > 0) {
+                appendSuppressedCount(sb, prefix, count, textRenderer, suffix, 
lineSeparator);
+            }
+        }
+        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");
+            renderSuffix(suffix, sb, textRenderer);
+            textRenderer.render(lineSeparator, sb, "Text");
+        }
+    }
+
+    private static void renderSuffix(final String suffix, final StringBuilder 
sb, final TextRenderer textRenderer) {
+        if (!suffix.isEmpty()) {
+            textRenderer.render(" ", sb, "Suffix");
+            textRenderer.render(suffix, sb, "Suffix");
+        }
+    }
+
+    private static void appendSuppressedCount(final StringBuilder sb, final 
String prefix, final int count,
+                                              final TextRenderer textRenderer, 
final String suffix, String lineSeparator) {
+        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");
+        }
+        renderSuffix(suffix, sb, textRenderer);
+        textRenderer.render(lineSeparator, sb, "Text");
+    }
+
+    private static void formatEntry(final ExtendedStackTraceElement 
extStackTraceElement, final StringBuilder sb,
+                                    final String prefix, final TextRenderer 
textRenderer, final String suffix, String lineSeparator) {
+        textRenderer.render(prefix, sb, "Prefix");
+        textRenderer.render("\tat ", sb, "At");
+        extStackTraceElement.renderOn(sb, textRenderer);
+        renderSuffix(suffix, sb, textRenderer);
+        textRenderer.render(lineSeparator, sb, "Text");
+    }
+
+    private static 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;
+    }
+
+    /**
+     * Formats the stack trace including packaging information.
+     *
+     * @param src            ThrowableProxy instance to format
+     * @param sb             Destination.
+     * @param ignorePackages List of packages to be ignored in the trace.
+     * @param textRenderer   The message renderer.
+     * @param suffix         Append this to the end of each stack frame.
+     * @param lineSeparator  The end-of-line separator.
+     */
+    static void formatExtendedStackTraceTo(ThrowableProxy src, final 
StringBuilder sb, final List<String> ignorePackages, final TextRenderer 
textRenderer, final String suffix, final String lineSeparator) {
+        textRenderer.render(src.getName(), sb, "Name");
+        textRenderer.render(": ", sb, "NameMessageSeparator");
+        textRenderer.render(src.getMessage(), sb, "Message");
+        renderSuffix(suffix, sb, textRenderer);
+        textRenderer.render(lineSeparator, sb, "Text");
+        final StackTraceElement[] causedTrace = src.getThrowable() != null ? 
src.getThrowable().getStackTrace() : null;
+        formatElements(sb, Strings.EMPTY, 0, causedTrace, 
src.getExtendedStackTrace(), ignorePackages, textRenderer, suffix, 
lineSeparator);
+        formatSuppressed(sb, TAB, src.getSuppressedProxies(), ignorePackages, 
textRenderer, suffix, lineSeparator);
+        formatCause(sb, Strings.EMPTY, src.getCauseProxy(), ignorePackages, 
textRenderer, suffix, lineSeparator);
+    }
+
+    /**
+     * Formats the Throwable that is the cause of the <pre>src</pre> Throwable.
+     *
+     * @param src            Throwable whose cause to render
+     * @param sb             Destination to render the formatted Throwable 
that caused this Throwable onto.
+     * @param ignorePackages The List of packages to be suppressed from the 
stack trace.
+     * @param textRenderer   The text renderer.
+     * @param suffix         Append this to the end of each stack frame.
+     * @param lineSeparator  The end-of-line separator.
+     */
+    static void formatCauseStackTrace(final ThrowableProxy src, final 
StringBuilder sb, final List<String> ignorePackages, final TextRenderer 
textRenderer, final String suffix, final String lineSeparator) {
+        ThrowableProxy causeProxy = src.getCauseProxy();
+        if (causeProxy != null) {
+            formatWrapper(sb, causeProxy, ignorePackages, textRenderer, 
suffix, lineSeparator);
+            sb.append(WRAPPED_BY_LABEL);
+            ThrowableProxyRenderer.renderSuffix(suffix, sb, textRenderer);
+        }
+        renderOn(src, sb, textRenderer);
+        ThrowableProxyRenderer.renderSuffix(suffix, sb, textRenderer);
+        textRenderer.render(lineSeparator, sb, "Text");
+        ThrowableProxyRenderer.formatElements(sb, Strings.EMPTY, 0, 
src.getStackTrace(), src.getExtendedStackTrace(),
+                ignorePackages, textRenderer, suffix, lineSeparator);
+    }
+
+    private static void renderOn(final ThrowableProxy src, final StringBuilder 
output, final TextRenderer textRenderer) {
+        final String msg = src.getMessage();
+        textRenderer.render(src.getName(), output, "Name");
+        if (msg != null) {
+            textRenderer.render(": ", output, "NameMessageSeparator");
+            textRenderer.render(msg, output, "Message");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de6593f5/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 ba09c99..8a9b83e 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
@@ -362,11 +362,11 @@ public class ThrowableProxyTest {
 
     @Test
     public void testStack() {
-        final Map<String, ThrowableProxy.CacheEntry> map = new HashMap<>();
+        final Map<String, ThrowableProxyHelper.CacheEntry> map = new 
HashMap<>();
         final Stack<Class<?>> stack = new Stack<>();
         final Throwable throwable = new IllegalStateException("This is a 
test");
         final ThrowableProxy proxy = new ThrowableProxy(throwable);
-        final ExtendedStackTraceElement[] callerPackageData = 
proxy.toExtendedStackTrace(stack, map, null,
+        final ExtendedStackTraceElement[] callerPackageData = 
ThrowableProxyHelper.toExtendedStackTrace(proxy, stack, map, null,
                 throwable.getStackTrace());
         assertNotNull("No package data returned", callerPackageData);
     }
@@ -379,7 +379,7 @@ public class ThrowableProxyTest {
     @Test
     public void testStackWithUnloadableClass() throws Exception {
         final Stack<Class<?>> stack = new Stack<>();
-        final Map<String, ThrowableProxy.CacheEntry> map = new HashMap<>();
+        final Map<String, ThrowableProxyHelper.CacheEntry> map = new 
HashMap<>();
 
         final String runtimeExceptionThrownAtUnloadableClass_base64 = 
"rO0ABXNyABpqYXZhLmxhbmcuUnVudGltZUV4Y2VwdGlvbp5fBkcKNIPlAgAAeHIAE2phdmEubGFuZy5FeGNlcHRpb27Q/R8+GjscxAIAAHhyABNqYXZhLmxhbmcuVGhyb3dhYmxl1cY1Jzl3uMsDAANMAAVjYXVzZXQAFUxqYXZhL2xhbmcvVGhyb3dhYmxlO0wADWRldGFpbE1lc3NhZ2V0ABJMamF2YS9sYW5nL1N0cmluZztbAApzdGFja1RyYWNldAAeW0xqYXZhL2xhbmcvU3RhY2tUcmFjZUVsZW1lbnQ7eHBxAH4ABnB1cgAeW0xqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnQ7AkYqPDz9IjkCAAB4cAAAAAFzcgAbamF2YS5sYW5nLlN0YWNrVHJhY2VFbGVtZW50YQnFmiY23YUCAARJAApsaW5lTnVtYmVyTAAOZGVjbGFyaW5nQ2xhc3NxAH4ABEwACGZpbGVOYW1lcQB+AARMAAptZXRob2ROYW1lcQB+AAR4cAAAAAZ0ADxvcmcuYXBhY2hlLmxvZ2dpbmcubG9nNGouY29yZS5pbXBsLkZvcmNlTm9EZWZDbGFzc0ZvdW5kRXJyb3J0AB5Gb3JjZU5vRGVmQ2xhc3NGb3VuZEVycm9yLmphdmF0AARtYWlueA==";
         final byte[] binaryDecoded = Base64Converter
@@ -389,7 +389,7 @@ public class ThrowableProxyTest {
         final Throwable throwable = (Throwable) in.readObject();
         final ThrowableProxy subject = new ThrowableProxy(throwable);
 
-        subject.toExtendedStackTrace(stack, map, null, 
throwable.getStackTrace());
+        ThrowableProxyHelper.toExtendedStackTrace(subject, stack, map, null, 
throwable.getStackTrace());
     }
 
     /**

Reply via email to