blob: d86d2bbdfba9f8bd049326d586595ad7f316aa72 [file] [log] [blame]
/*
* 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");
}
}
}