| /* |
| * 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"); |
| } |
| } |
| } |