You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2016/07/20 21:35:07 UTC
[04/16] logging-log4j2 git commit: Add ANSI style option to extended
throwable converter.
Add ANSI style option to extended throwable converter.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/d7f9d951
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/d7f9d951
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/d7f9d951
Branch: refs/heads/StyledXException
Commit: d7f9d951911f5fcbc75c776a092677a005b0226e
Parents: b364a32
Author: ggregory <gg...@apache.org>
Authored: Tue Jul 12 19:50:34 2016 -0700
Committer: ggregory <gg...@apache.org>
Committed: Tue Jul 12 19:50:34 2016 -0700
----------------------------------------------------------------------
.../log4j/core/impl/ExtendedClassInfo.java | 23 +-
.../core/impl/ExtendedStackTraceElement.java | 46 +++-
.../log4j/core/impl/ThrowableFormatOptions.java | 104 +++++--
.../logging/log4j/core/impl/ThrowableProxy.java | 184 +++++++++----
.../ExtendedThrowablePatternConverter.java | 2 +-
.../log4j/core/pattern/HtmlMessageRenderer.java | 33 ---
.../log4j/core/pattern/HtmlTextRenderer.java | 38 +++
.../core/pattern/JAnsiMessageRenderer.java | 211 ---------------
.../log4j/core/pattern/JAnsiTextRenderer.java | 268 +++++++++++++++++++
.../core/pattern/MessagePatternConverter.java | 16 +-
.../log4j/core/pattern/MessageRenderer.java | 32 ---
.../log4j/core/pattern/PlainTextRenderer.java | 40 +++
.../log4j/core/pattern/TextRenderer.java | 46 ++++
.../apache/logging/log4j/core/util/Loader.java | 5 +
.../ConsoleAppenderJAnsiXExceptionMain.java | 74 +++++
.../test/resources/log4j2-console-xex-ansi.xml | 31 +++
16 files changed, 770 insertions(+), 383 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedClassInfo.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedClassInfo.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedClassInfo.java
index 1c7e686..280a574 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedClassInfo.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedClassInfo.java
@@ -18,6 +18,9 @@ package org.apache.logging.log4j.core.impl;
import java.io.Serializable;
+import org.apache.logging.log4j.core.pattern.PlainTextRenderer;
+import org.apache.logging.log4j.core.pattern.TextRenderer;
+
/**
* Class and package data used with a {@link StackTraceElement} in a {@link ExtendedStackTraceElement}.
*/
@@ -99,17 +102,21 @@ public final class ExtendedClassInfo implements Serializable {
return result;
}
+ public void renderOn(StringBuilder output, TextRenderer textRenderer) {
+ if (!this.exact) {
+ textRenderer.render("~", output, "ExtraClassInfo.Inexact");
+ }
+ textRenderer.render("[", output, "ExtraClassInfo.Container");
+ textRenderer.render(this.location, output, "ExtraClassInfo.Location");
+ textRenderer.render(":", output, "ExtraClassInfo.ContainerSeparator");
+ textRenderer.render(this.version, output, "ExtraClassInfo.Version");
+ textRenderer.render("]", output, "ExtraClassInfo.Container");
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
- if (!this.exact) {
- sb.append('~');
- }
- sb.append('[');
- sb.append(this.location);
- sb.append(':');
- sb.append(this.version);
- sb.append(']');
+ renderOn(sb, PlainTextRenderer.getInstance());
return sb.toString();
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedStackTraceElement.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedStackTraceElement.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedStackTraceElement.java
index 9ced720..4648ed8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedStackTraceElement.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ExtendedStackTraceElement.java
@@ -18,6 +18,9 @@ package org.apache.logging.log4j.core.impl;
import java.io.Serializable;
+import org.apache.logging.log4j.core.pattern.PlainTextRenderer;
+import org.apache.logging.log4j.core.pattern.TextRenderer;
+
/**
* Wraps and extends the concept of the JRE's final class {@link StackTraceElement} by adding more location information.
* <p>
@@ -37,7 +40,8 @@ public final class ExtendedStackTraceElement implements Serializable {
private final StackTraceElement stackTraceElement;
- public ExtendedStackTraceElement(final StackTraceElement stackTraceElement, final ExtendedClassInfo extraClassInfo) {
+ public ExtendedStackTraceElement(final StackTraceElement stackTraceElement,
+ final ExtendedClassInfo extraClassInfo) {
this.stackTraceElement = stackTraceElement;
this.extraClassInfo = extraClassInfo;
}
@@ -47,8 +51,8 @@ public final class ExtendedStackTraceElement implements Serializable {
*/
public ExtendedStackTraceElement(final String declaringClass, final String methodName, final String fileName,
final int lineNumber, final boolean exact, final String location, final String version) {
- this(new StackTraceElement(declaringClass, methodName, fileName, lineNumber), new ExtendedClassInfo(exact,
- location, version));
+ this(new StackTraceElement(declaringClass, methodName, fileName, lineNumber),
+ new ExtendedClassInfo(exact, location, version));
}
@Override
@@ -129,12 +133,42 @@ public final class ExtendedStackTraceElement implements Serializable {
return this.stackTraceElement.isNativeMethod();
}
+ void renderOn(StringBuilder output, TextRenderer textRenderer) {
+ render(this.stackTraceElement, output, textRenderer);
+ textRenderer.render(" ", output, "Text");
+ this.extraClassInfo.renderOn(output, textRenderer);
+ }
+
+ private void render(StackTraceElement stElement, StringBuilder output, TextRenderer textRenderer) {
+ final String fileName = stElement.getFileName();
+ final int lineNumber = stElement.getLineNumber();
+ textRenderer.render(getClassName(), output, "StackTraceElement.ClassName");
+ textRenderer.render(".", output, "StackTraceElement.ClassMethodSeparator");
+ textRenderer.render(stElement.getMethodName(), output, "StackTraceElement.MethodName");
+ if (stElement.isNativeMethod()) {
+ textRenderer.render("(Native Method)", output, "StackTraceElement.NativeMethod");
+ } else if (fileName != null && lineNumber >= 0) {
+ textRenderer.render("(", output, "StackTraceElement.Container");
+ textRenderer.render(fileName, output, "StackTraceElement.FileName");
+ textRenderer.render(":", output, "StackTraceElement.ContainerSeparator");
+ textRenderer.render(Integer.toString(lineNumber), output, "StackTraceElement.LineNumber");
+ textRenderer.render(")", output, "StackTraceElement.Container");
+ } else if (fileName != null) {
+ textRenderer.render("(", output, "StackTraceElement.Container");
+ textRenderer.render(fileName, output, "StackTraceElement.FileName");
+ textRenderer.render(")", output, "StackTraceElement.Container");
+ } else {
+ textRenderer.render("(", output, "StackTraceElement.Container");
+ textRenderer.render("Unknown Source", output, "StackTraceElement.UnknownSource");
+ textRenderer.render(")", output, "StackTraceElement.Container");
+ }
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
- sb.append(this.stackTraceElement);
- sb.append(" ");
- sb.append(this.extraClassInfo);
+ renderOn(sb, PlainTextRenderer.getInstance());
return sb.toString();
}
+
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptions.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptions.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptions.java
index d5e0285..7a10d0c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptions.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptions.java
@@ -20,8 +20,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
+import org.apache.logging.log4j.core.pattern.JAnsiTextRenderer;
+import org.apache.logging.log4j.core.pattern.TextRenderer;
+import org.apache.logging.log4j.core.pattern.PlainTextRenderer;
import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.Patterns;
+import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
/**
@@ -52,6 +57,11 @@ public final class ThrowableFormatOptions {
private static final String SHORT = "short";
/**
+ * ANSI renderer
+ */
+ private final TextRenderer textRenderer;
+
+ /**
* The number of lines to write.
*/
private final int lines;
@@ -74,34 +84,45 @@ public final class ThrowableFormatOptions {
public static final String LOCALIZED_MESSAGE = "short.localizedMessage";
/**
- * Construct the options for printing stack trace.
- * @param lines The number of lines.
- * @param separator The stack trace separator.
- * @param packages The packages to filter.
+ * Constructs the options for printing stack trace.
+ *
+ * @param lines
+ * The number of lines.
+ * @param separator
+ * The stack trace separator.
+ * @param packages
+ * The packages to filter.
+ * @param textRenderer
+ * The ANSI renderer
*/
- protected ThrowableFormatOptions(final int lines, final String separator, final List<String> packages) {
+ protected ThrowableFormatOptions(final int lines, final String separator, final List<String> packages,
+ TextRenderer textRenderer) {
this.lines = lines;
this.separator = separator == null ? Constants.LINE_SEPARATOR : separator;
this.packages = packages;
+ this.textRenderer = textRenderer == null ? PlainTextRenderer.getInstance() : textRenderer;
}
/**
- * Construct the options for printing stack trace.
- * @param packages The packages to filter.
+ * Constructs the options for printing stack trace.
+ *
+ * @param packages
+ * The packages to filter.
*/
protected ThrowableFormatOptions(final List<String> packages) {
- this(DEFAULT_LINES, null, packages);
+ this(DEFAULT_LINES, null, packages, null);
}
/**
- * Construct the options for printing stack trace.
+ * Constructs the options for printing stack trace.
*/
protected ThrowableFormatOptions() {
- this(DEFAULT_LINES, null, null);
+ this(DEFAULT_LINES, null, null, null);
}
/**
* Returns the number of lines to write.
+ *
* @return The number of lines to write.
*/
public int getLines() {
@@ -110,6 +131,7 @@ public final class ThrowableFormatOptions {
/**
* Returns the stack trace separator.
+ *
* @return The stack trace separator.
*/
public String getSeparator() {
@@ -117,7 +139,17 @@ public final class ThrowableFormatOptions {
}
/**
+ * Returns the message rendered.
+ *
+ * @return the message rendered.
+ */
+ public TextRenderer getTextRenderer() {
+ return textRenderer;
+ }
+
+ /**
* Returns the list of packages to filter.
+ *
* @return The list of packages to filter.
*/
public List<String> getPackages() {
@@ -126,6 +158,7 @@ public final class ThrowableFormatOptions {
/**
* Determines if all lines should be printed.
+ *
* @return true for all lines, false otherwise.
*/
public boolean allLines() {
@@ -134,6 +167,7 @@ public final class ThrowableFormatOptions {
/**
* Determines if any lines should be printed.
+ *
* @return true for any lines, false otherwise.
*/
public boolean anyLines() {
@@ -142,7 +176,9 @@ public final class ThrowableFormatOptions {
/**
* Returns the minimum between the lines and the max lines.
- * @param maxLines The maximum number of lines.
+ *
+ * @param maxLines
+ * The maximum number of lines.
* @return The number of lines to print.
*/
public int minLines(final int maxLines) {
@@ -151,6 +187,7 @@ public final class ThrowableFormatOptions {
/**
* Determines if there are any packages to filter.
+ *
* @return true if there are packages, false otherwise.
*/
public boolean hasPackages() {
@@ -163,7 +200,9 @@ public final class ThrowableFormatOptions {
@Override
public String toString() {
final StringBuilder s = new StringBuilder();
- s.append('{').append(allLines() ? FULL : this.lines == 2 ? SHORT : anyLines() ? String.valueOf(this.lines) : NONE).append('}');
+ s.append('{')
+ .append(allLines() ? FULL : this.lines == 2 ? SHORT : anyLines() ? String.valueOf(this.lines) : NONE)
+ .append('}');
s.append("{separator(").append(this.separator).append(")}");
if (hasPackages()) {
s.append("{filters(");
@@ -177,8 +216,11 @@ public final class ThrowableFormatOptions {
}
/**
- * Create a new instance based on the array of options.
- * @param options The array of options.
+ * Creates a new instance based on the array of options.
+ *
+ * @param options
+ * The array of options.
+ * @return A new initialized instance.
*/
public static ThrowableFormatOptions newInstance(String[] options) {
if (options == null || options.length == 0) {
@@ -187,19 +229,16 @@ public final class ThrowableFormatOptions {
// NOTE: The following code is present for backward compatibility
// and was copied from Extended/RootThrowablePatternConverter.
// This supports a single option with the format:
- // %xEx{["none"|"short"|"full"|depth],[filters(packages)}
+ // %xEx{["none"|"short"|"full"|depth],[filters(packages)}
// However, the convention for multiple options should be:
- // %xEx{["none"|"short"|"full"|depth]}[{filters(packages)}]
+ // %xEx{["none"|"short"|"full"|depth]}[{filters(packages)}]
if (options.length == 1 && Strings.isNotEmpty(options[0])) {
final String[] opts = options[0].split(Patterns.COMMA_SEPARATOR, 2);
final String first = opts[0].trim();
try (final Scanner scanner = new Scanner(first)) {
- if (opts.length > 1
- && (first.equalsIgnoreCase(FULL) || first.equalsIgnoreCase(SHORT)
- || first.equalsIgnoreCase(NONE) || scanner.hasNextInt())) {
- options = new String[] {
- first,
- opts[1].trim() };
+ if (opts.length > 1 && (first.equalsIgnoreCase(FULL) || first.equalsIgnoreCase(SHORT)
+ || first.equalsIgnoreCase(NONE) || scanner.hasNextInt())) {
+ options = new String[] { first, opts[1].trim() };
}
}
}
@@ -207,6 +246,7 @@ public final class ThrowableFormatOptions {
int lines = DEFAULT.lines;
String separator = DEFAULT.separator;
List<String> packages = DEFAULT.packages;
+ TextRenderer ansiRenderer = DEFAULT.textRenderer;
for (final String rawOption : options) {
if (rawOption != null) {
final String option = rawOption.trim();
@@ -230,16 +270,26 @@ public final class ThrowableFormatOptions {
}
} else if (option.equalsIgnoreCase(NONE)) {
lines = 0;
- } else if (option.equalsIgnoreCase(SHORT) || option.equalsIgnoreCase(CLASS_NAME) ||
- option.equalsIgnoreCase(METHOD_NAME) || option.equalsIgnoreCase(LINE_NUMBER) ||
- option.equalsIgnoreCase(FILE_NAME) || option.equalsIgnoreCase(MESSAGE) ||
- option.equalsIgnoreCase(LOCALIZED_MESSAGE)) {
+ } else if (option.equalsIgnoreCase(SHORT) || option.equalsIgnoreCase(CLASS_NAME)
+ || option.equalsIgnoreCase(METHOD_NAME) || option.equalsIgnoreCase(LINE_NUMBER)
+ || option.equalsIgnoreCase(FILE_NAME) || option.equalsIgnoreCase(MESSAGE)
+ || option.equalsIgnoreCase(LOCALIZED_MESSAGE)) {
lines = 2;
+ } else if (option.startsWith("ansi(") && option.endsWith(")")) {
+ if (Loader.isJansiAvailable()) {
+ ansiRenderer = new JAnsiTextRenderer(
+ new String[] { null, option.substring("ansi(".length(), option.length() - 1) },
+ JAnsiTextRenderer.DefaultExceptionStyleMap);
+ } else {
+ StatusLogger.getLogger()
+ .warn("You requested ANSI exception rendering but JANSI is not on the classpath.");
+ }
} else if (!option.equalsIgnoreCase(FULL)) {
lines = Integer.parseInt(option);
}
}
}
- return new ThrowableFormatOptions(lines, separator, packages);
+ return new ThrowableFormatOptions(lines, separator, packages, ansiRenderer);
}
+
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/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 28f8e56..7d30a6f 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
@@ -28,6 +28,8 @@ 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;
@@ -51,7 +53,8 @@ import org.apache.logging.log4j.util.Strings;
*/
public class ThrowableProxy implements Serializable {
- private static final String CAUSED_BY_LABEL = "Caused by: ";
+ private static final String 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: ";
@@ -75,6 +78,8 @@ public class ThrowableProxy implements Serializable {
private static final char EOL = '\n';
+ private static final String EOL_STR = "\n";
+
private static final long serialVersionUID = -2752771578252251910L;
private final ThrowableProxy causeProxy;
@@ -205,76 +210,95 @@ public class ThrowableProxy implements Serializable {
return true;
}
- private void formatCause(final StringBuilder sb, final String prefix, final ThrowableProxy cause, final List<String> ignorePackages) {
- formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages);
- }
+ 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) {
+ final ThrowableProxy throwableProxy, final List<String> ignorePackages, TextRenderer textRenderer) {
if (throwableProxy == null) {
return;
}
- sb.append(prefix).append(causeLabel).append(throwableProxy).append(EOL);
+ 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);
- this.formatSuppressed(sb, prefix + "\t", throwableProxy.suppressedProxies, ignorePackages);
- this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages);
+ throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, ignorePackages, textRenderer);
+ this.formatSuppressed(sb, prefix + TAB, throwableProxy.suppressedProxies, ignorePackages, textRenderer);
+ this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages, textRenderer);
}
- private void formatSuppressed(final StringBuilder sb, final String prefix, final ThrowableProxy[] suppressedProxies,
- final List<String> ignorePackages) {
+ void renderOn(StringBuilder output, TextRenderer textRenderer) {
+ final String msg = this.message;
+ textRenderer.render(this.name, output, "Name");
+ if (msg != null) {
+ textRenderer.render(": ", output, "Text");
+ 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) {
- final ThrowableProxy cause = suppressedProxy;
- formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, cause, ignorePackages);
+ 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) {
+ final List<String> ignorePackages, TextRenderer textRenderer) {
if (ignorePackages == null || ignorePackages.isEmpty()) {
for (final ExtendedStackTraceElement element : extStackTrace) {
- this.formatEntry(element, sb, prefix);
+ 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);
+ appendSuppressedCount(sb, prefix, count, textRenderer);
count = 0;
}
- this.formatEntry(extStackTrace[i], sb, prefix);
+ this.formatEntry(extStackTrace[i], sb, prefix, textRenderer);
} else {
++count;
}
}
if (count > 0) {
- appendSuppressedCount(sb, prefix, count);
+ appendSuppressedCount(sb, prefix, count, textRenderer);
}
}
if (commonCount != 0) {
- sb.append(prefix).append("\t... ").append(commonCount).append(" more").append(EOL);
+ textRenderer.render(prefix, sb, "Prefix");
+ textRenderer.render("\t... ", sb, "Text");
+ textRenderer.render(Integer.toString(commonCount), sb, "Text");
+ textRenderer.render(" more", sb, "Text");
+ textRenderer.render(EOL_STR, sb, "Text");
}
}
- private void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count) {
- sb.append(prefix);
+ private void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count, TextRenderer textRenderer) {
+ textRenderer.render(prefix, sb, "Prefix");
if (count == 1) {
- sb.append("\t....").append(EOL);
+ textRenderer.render("\t... ", sb, "Text");
} else {
- sb.append("\t... suppressed ").append(count).append(" lines").append(EOL);
+ textRenderer.render("\t... suppressed ", sb, "Text");
+ textRenderer.render(Integer.toString(count), sb, "Text");
+ textRenderer.render(" lines", sb, "Text");
}
+ textRenderer.render(EOL_STR, sb, "Text");
}
- private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb, final String prefix) {
- sb.append(prefix);
- sb.append("\tat ");
- sb.append(extStackTraceElement);
- sb.append(EOL);
+ 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");
}
/**
@@ -286,7 +310,7 @@ public class ThrowableProxy implements Serializable {
* The Throwable to format.
*/
public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause) {
- this.formatWrapper(sb, cause, null);
+ this.formatWrapper(sb, cause, null, PlainTextRenderer.getInstance());
}
/**
@@ -296,19 +320,37 @@ public class ThrowableProxy implements Serializable {
* StringBuilder to contain the formatted Throwable.
* @param cause
* The Throwable to format.
- * @param packages
+ * @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> packages) {
+ 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);
+ this.formatWrapper(sb, cause.causeProxy, ignorePackages, textRenderer);
sb.append(WRAPPED_BY_LABEL);
}
- sb.append(cause).append(EOL);
- this.formatElements(sb, "", cause.commonElementCount,
- cause.getThrowable().getStackTrace(), cause.extendedStackTrace, packages);
+ 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() {
@@ -316,35 +358,49 @@ public class ThrowableProxy implements Serializable {
}
/**
- * Format the Throwable that is the cause of this Throwable.
+ * 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);
+ return this.getCauseStackTraceAsString(null, PlainTextRenderer.getInstance());
}
/**
- * Format the Throwable that is the cause of this Throwable.
+ * 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(null, 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);
+ this.formatWrapper(sb, this.causeProxy, ignorePackages, textRenderer);
sb.append(WRAPPED_BY_LABEL);
}
- sb.append(this.toString());
- sb.append(EOL);
- this.formatElements(sb, "", 0, this.throwable.getStackTrace(), this.extendedStackTrace, packages);
+ 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();
}
/**
- * Return the number of elements that are being omitted because they are common with the parent Throwable's stack
+ * 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.
@@ -368,7 +424,7 @@ public class ThrowableProxy implements Serializable {
* @return The formatted stack trace including packaging information.
*/
public String getExtendedStackTraceAsString() {
- return this.getExtendedStackTraceAsString(null);
+ return this.getExtendedStackTraceAsString(null, PlainTextRenderer.getInstance());
}
/**
@@ -379,16 +435,28 @@ public class ThrowableProxy implements Serializable {
* @return The formatted stack trace including packaging information.
*/
public String getExtendedStackTraceAsString(final List<String> ignorePackages) {
- final StringBuilder sb = new StringBuilder(this.name);
- final String msg = this.message;
- if (msg != null) {
- sb.append(": ").append(msg);
- }
- sb.append(EOL);
+ 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, "Name");
+ 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, "", 0, causedTrace, this.extendedStackTrace, ignorePackages);
- this.formatSuppressed(sb, "\t", this.suppressedProxies, ignorePackages);
- this.formatCause(sb, "", this.causeProxy, ignorePackages);
+ 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();
}
@@ -461,10 +529,12 @@ public class ThrowableProxy implements Serializable {
}
private boolean ignoreElement(final StackTraceElement element, final List<String> ignorePackages) {
- final String className = element.getClassName();
- for (final String pkg : ignorePackages) {
- if (className.startsWith(pkg)) {
- return true;
+ if (ignorePackages != null) {
+ final String className = element.getClassName();
+ for (final String pkg : ignorePackages) {
+ if (className.startsWith(pkg)) {
+ return true;
+ }
}
}
return false;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
index 8e781b6..01ec640 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
@@ -65,7 +65,7 @@ public final class ExtendedThrowablePatternConverter extends ThrowablePatternCon
super.format(event, toAppendTo);
return;
}
- final String extStackTrace = proxy.getExtendedStackTraceAsString(options.getPackages());
+ final String extStackTrace = proxy.getExtendedStackTraceAsString(options.getPackages(), options.getTextRenderer());
final int len = toAppendTo.length();
if (len > 0 && !Character.isWhitespace(toAppendTo.charAt(len - 1))) {
toAppendTo.append(' ');
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlMessageRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlMessageRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlMessageRenderer.java
deleted file mode 100644
index be62365..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlMessageRenderer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.pattern;
-
-/**
- * TODO Renders an input as HTML output.
- */
-public final class HtmlMessageRenderer implements MessageRenderer {
-
- public HtmlMessageRenderer(String[] formats) {
- // TODO Auto-generated constructor stub
- }
-
- @Override
- public void render(StringBuilder input, StringBuilder output) {
- // TODO Auto-generated method stub
- }
-
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlTextRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlTextRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlTextRenderer.java
new file mode 100644
index 0000000..e13bae6
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HtmlTextRenderer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.pattern;
+
+/**
+ * TODO Renders an input as HTML output.
+ */
+public final class HtmlTextRenderer implements TextRenderer {
+
+ public HtmlTextRenderer(String[] formats) {
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public void render(String input, StringBuilder output, String styleName) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void render(StringBuilder input, StringBuilder output) {
+ // TODO Auto-generated method stub
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiMessageRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiMessageRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiMessageRenderer.java
deleted file mode 100644
index 039275a..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiMessageRenderer.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.pattern;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.logging.log4j.status.StatusLogger;
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.AnsiRenderer;
-import org.fusesource.jansi.AnsiRenderer.Code;
-
-/**
- * Renders an input as ANSI escaped output.
- *
- * Uses the JAnsi rendering syntax as the default to render a message into an ANSI escaped string.
- *
- * The default syntax for embedded ANSI codes is:
- *
- * <pre>
- * @|<em>code</em>(,<em>code</em>)* <em>text</em>|@
- * </pre>
- *
- * For example, to render the message {@code "Hello"} in green, use:
- *
- * <pre>
- * @|green Hello|@
- * </pre>
- *
- * To render the message {@code "Hello"} in bold and red, use:
- *
- * <pre>
- * @|bold,red Warning!|@
- * </pre>
- *
- * You can also define custom style names in the configuration with the syntax:
- *
- * <pre>
- * %message{ansi}{StyleName=value(,value)*( StyleName=value(,value)*)*}%n
- * </pre>
- *
- * For example:
- *
- * <pre>
- * %message{ansi}{WarningStyle=red,bold KeyStyle=white ValueStyle=blue}%n
- * </pre>
- *
- * The call site can look like this:
- *
- * <pre>
- * logger.info("@|KeyStyle {}|@ = @|ValueStyle {}|@", entry.getKey(), entry.getValue());
- * </pre>
- *
- * Note: This class originally copied and then heavily modified code from JAnsi's AnsiRenderer (which is licensed as
- * Apache 2.0.)
- *
- * @see AnsiRenderer
- */
-public final class JAnsiMessageRenderer implements MessageRenderer {
-
- private final String beginToken;
- private final int beginTokenLen;
- private final String endToken;
- private final int endTokenLen;
- private final Map<String, Code[]> styleMap;
-
- public JAnsiMessageRenderer(final String[] formats) {
- String tempBeginToken = AnsiRenderer.BEGIN_TOKEN;
- String tempEndToken = AnsiRenderer.END_TOKEN;
- Map<String, Code[]> map;
- if (formats.length > 1) {
- final String allStylesStr = formats[1];
- // Style def split
- final String[] allStyleAssignmentsArr = allStylesStr.split(" ");
- map = new HashMap<>(allStyleAssignmentsArr.length);
- for (final String styleAssignmentStr : allStyleAssignmentsArr) {
- final String[] styleAssignmentArr = styleAssignmentStr.split("=");
- if (styleAssignmentArr.length != 2) {
- StatusLogger.getLogger().warn("{} parsing style \"{}\", expected format: StyleName=Code(,Code)*",
- getClass().getSimpleName(), styleAssignmentStr);
- } else {
- final String styleName = styleAssignmentArr[0];
- final String codeListStr = styleAssignmentArr[1];
- final String[] codeNames = codeListStr.split(",");
- if (codeNames.length == 0) {
- StatusLogger.getLogger().warn(
- "{} parsing style \"{}\", expected format: StyleName=Code(,Code)*",
- getClass().getSimpleName(), styleAssignmentStr);
- } else {
- switch (styleName) {
- case "BeginToken":
- tempBeginToken = codeNames[0];
- break;
- case "EndToken":
- tempEndToken = codeNames[0];
- break;
- default:
- final Code[] codes = new Code[codeNames.length];
- for (int i = 0; i < codes.length; i++) {
- codes[i] = toCode(codeNames[i]);
- }
- map.put(styleName, codes);
- }
- }
- }
- }
- } else {
- map = new HashMap<>(0);
- }
- styleMap = map;
- beginToken = tempBeginToken;
- endToken = tempEndToken;
- beginTokenLen = tempBeginToken.length();
- endTokenLen = tempEndToken.length();
- }
-
- private void render(final Ansi ansi, final Code code) {
- if (code.isColor()) {
- if (code.isBackground()) {
- ansi.bg(code.getColor());
- } else {
- ansi.fg(code.getColor());
- }
- } else if (code.isAttribute()) {
- ansi.a(code.getAttribute());
- }
- }
-
- private void render(final Ansi ansi, final Code... codes) {
- for (final Code code : codes) {
- render(ansi, code);
- }
- }
-
- private String render(final String text, final String... names) {
- final Ansi ansi = Ansi.ansi();
- for (final String name : names) {
- final Code[] codes = styleMap.get(name);
- if (codes != null) {
- render(ansi, codes);
- } else {
- render(ansi, toCode(name));
- }
- }
- return ansi.a(text).reset().toString();
- }
-
- @Override
- public void render(final StringBuilder input, final StringBuilder output) throws IllegalArgumentException {
- int i = 0;
- int j, k;
-
- while (true) {
- j = input.indexOf(beginToken, i);
- if (j == -1) {
- if (i == 0) {
- output.append(input);
- return;
- }
- output.append(input.substring(i, input.length()));
- return;
- }
- output.append(input.substring(i, j));
- k = input.indexOf(endToken, j);
-
- if (k == -1) {
- output.append(input);
- return;
- }
- j += beginTokenLen;
- final String spec = input.substring(j, k);
-
- final String[] items = spec.split(AnsiRenderer.CODE_TEXT_SEPARATOR, 2);
- if (items.length == 1) {
- output.append(input);
- return;
- }
- final String replacement = render(items[1], items[0].split(","));
-
- output.append(replacement);
-
- i = k + endTokenLen;
- }
- }
-
- private Code toCode(final String name) {
- return Code.valueOf(name.toUpperCase(Locale.ENGLISH));
- }
-
- @Override
- public String toString() {
- return "JAnsiMessageRenderer [beginToken=" + beginToken + ", beginTokenLen=" + beginTokenLen + ", endToken="
- + endToken + ", endTokenLen=" + endTokenLen + ", styleMap=" + styleMap + "]";
- }
-
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java
new file mode 100644
index 0000000..21788ed
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java
@@ -0,0 +1,268 @@
+/*
+ * 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.pattern;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.logging.log4j.status.StatusLogger;
+import org.fusesource.jansi.Ansi;
+import org.fusesource.jansi.AnsiRenderer;
+import org.fusesource.jansi.AnsiRenderer.Code;
+
+/**
+ * Renders an input as ANSI escaped output.
+ *
+ * Uses the JAnsi rendering syntax as the default to render a message into an ANSI escaped string.
+ *
+ * The default syntax for embedded ANSI codes is:
+ *
+ * <pre>
+ * @|<em>code</em>(,<em>code</em>)* <em>text</em>|@
+ * </pre>
+ *
+ * For example, to render the message {@code "Hello"} in green, use:
+ *
+ * <pre>
+ * @|green Hello|@
+ * </pre>
+ *
+ * To render the message {@code "Hello"} in bold and red, use:
+ *
+ * <pre>
+ * @|bold,red Warning!|@
+ * </pre>
+ *
+ * You can also define custom style names in the configuration with the syntax:
+ *
+ * <pre>
+ * %message{ansi}{StyleName=value(,value)*( StyleName=value(,value)*)*}%n
+ * </pre>
+ *
+ * For example:
+ *
+ * <pre>
+ * %message{ansi}{WarningStyle=red,bold KeyStyle=white ValueStyle=blue}%n
+ * </pre>
+ *
+ * The call site can look like this:
+ *
+ * <pre>
+ * logger.info("@|KeyStyle {}|@ = @|ValueStyle {}|@", entry.getKey(), entry.getValue());
+ * </pre>
+ *
+ * Note: This class originally copied and then heavily modified code from JAnsi's AnsiRenderer (which is licensed as
+ * Apache 2.0.)
+ *
+ * @see AnsiRenderer
+ */
+public final class JAnsiTextRenderer implements TextRenderer {
+
+ public static final Map<String, Code[]> DefaultExceptionStyleMap;
+ public static final Map<String, Code[]> DefaultMessageStyleMap;
+
+ static {
+ {
+ // TODO Should the keys be in an enum?
+ Map<String, Code[]> temp = new HashMap<>();
+ temp.put("Prefix", new Code[] { Code.WHITE });
+ temp.put("Name", new Code[] { Code.RED });
+ temp.put("Message", new Code[] { Code.RED });
+ temp.put("At", new Code[] { Code.WHITE });
+ temp.put("CauseLabel", new Code[] { Code.WHITE });
+ temp.put("Text", new Code[] { Code.WHITE });
+ // StackTraceElement
+ temp.put("StackTraceElement.ClassName", new Code[] { Code.YELLOW });
+ temp.put("StackTraceElement.ClassMethodSeparator", new Code[] { Code.YELLOW });
+ temp.put("StackTraceElement.MethodName", new Code[] { Code.YELLOW });
+ temp.put("StackTraceElement.NativeMethod", new Code[] { Code.YELLOW });
+ temp.put("StackTraceElement.FileName", new Code[] { Code.MAGENTA });
+ temp.put("StackTraceElement.LineNumber", new Code[] { Code.MAGENTA });
+ temp.put("StackTraceElement.Container", new Code[] { Code.MAGENTA });
+ temp.put("StackTraceElement.ContainerSeparator", new Code[] { Code.MAGENTA});
+ temp.put("StackTraceElement.UnknownSource", new Code[] { Code.MAGENTA });
+ // ExtraClassInfo
+ temp.put("ExtraClassInfo.Inexact", new Code[] { Code.GREEN });
+ temp.put("ExtraClassInfo.Container", new Code[] { Code.GREEN, Code.INTENSITY_FAINT });
+ temp.put("ExtraClassInfo.ContainerSeparator", new Code[] { Code.GREEN, Code.INTENSITY_FAINT });
+ temp.put("ExtraClassInfo.Location", new Code[] { Code.GREEN });
+ temp.put("ExtraClassInfo.Version", new Code[] { Code.GREEN });
+ // Save
+ DefaultExceptionStyleMap = Collections.unmodifiableMap(temp);
+ }
+ {
+ Map<String, Code[]> temp = new HashMap<>();
+ // TODO
+ DefaultMessageStyleMap = Collections.unmodifiableMap(temp);
+ }
+ }
+
+ private final String beginToken;
+ private final int beginTokenLen;
+ private final String endToken;
+ private final int endTokenLen;
+ private final Map<String, Code[]> styleMap;
+
+ public JAnsiTextRenderer(final String[] formats, Map<String, Code[]> defaultStyleMap) {
+ String tempBeginToken = AnsiRenderer.BEGIN_TOKEN;
+ String tempEndToken = AnsiRenderer.END_TOKEN;
+ Map<String, Code[]> map;
+ if (formats.length > 1) {
+ final String allStylesStr = formats[1];
+ // Style def split
+ final String[] allStyleAssignmentsArr = allStylesStr.split(" ");
+ map = new HashMap<>(allStyleAssignmentsArr.length + defaultStyleMap.size());
+ map.putAll(defaultStyleMap);
+ for (final String styleAssignmentStr : allStyleAssignmentsArr) {
+ final String[] styleAssignmentArr = styleAssignmentStr.split("=");
+ if (styleAssignmentArr.length != 2) {
+ StatusLogger.getLogger().warn("{} parsing style \"{}\", expected format: StyleName=Code(,Code)*",
+ getClass().getSimpleName(), styleAssignmentStr);
+ } else {
+ final String styleName = styleAssignmentArr[0];
+ final String codeListStr = styleAssignmentArr[1];
+ final String[] codeNames = codeListStr.split(",");
+ if (codeNames.length == 0) {
+ StatusLogger.getLogger().warn(
+ "{} parsing style \"{}\", expected format: StyleName=Code(,Code)*",
+ getClass().getSimpleName(), styleAssignmentStr);
+ } else {
+ switch (styleName) {
+ case "BeginToken":
+ tempBeginToken = codeNames[0];
+ break;
+ case "EndToken":
+ tempEndToken = codeNames[0];
+ break;
+ default:
+ final Code[] codes = new Code[codeNames.length];
+ for (int i = 0; i < codes.length; i++) {
+ codes[i] = toCode(codeNames[i]);
+ }
+ map.put(styleName, codes);
+ }
+ }
+ }
+ }
+ } else {
+ map = defaultStyleMap;
+ }
+ styleMap = map;
+ beginToken = tempBeginToken;
+ endToken = tempEndToken;
+ beginTokenLen = tempBeginToken.length();
+ endTokenLen = tempEndToken.length();
+ }
+
+ private void render(final Ansi ansi, final Code code) {
+ if (code.isColor()) {
+ if (code.isBackground()) {
+ ansi.bg(code.getColor());
+ } else {
+ ansi.fg(code.getColor());
+ }
+ } else if (code.isAttribute()) {
+ ansi.a(code.getAttribute());
+ }
+ }
+
+ private void render(final Ansi ansi, final Code... codes) {
+ for (final Code code : codes) {
+ render(ansi, code);
+ }
+ }
+
+ /**
+ * Renders the given text with the given names which can be ANSI code names or Log4j style names.
+ *
+ * @param text
+ * The text to render
+ * @param names
+ * ANSI code names or Log4j style names.
+ * @return A rendered string containing ANSI codes.
+ */
+ private String render(final String text, final String... names) {
+ final Ansi ansi = Ansi.ansi();
+ for (final String name : names) {
+ final Code[] codes = styleMap.get(name);
+ if (codes != null) {
+ render(ansi, codes);
+ } else {
+ render(ansi, toCode(name));
+ }
+ }
+ return ansi.a(text).reset().toString();
+ }
+
+ @Override
+ public void render(final StringBuilder input, final StringBuilder output) throws IllegalArgumentException {
+ int i = 0;
+ int j, k;
+
+ while (true) {
+ j = input.indexOf(beginToken, i);
+ if (j == -1) {
+ if (i == 0) {
+ output.append(input);
+ return;
+ }
+ output.append(input.substring(i, input.length()));
+ return;
+ }
+ output.append(input.substring(i, j));
+ k = input.indexOf(endToken, j);
+
+ if (k == -1) {
+ output.append(input);
+ return;
+ }
+ j += beginTokenLen;
+ final String spec = input.substring(j, k);
+
+ final String[] items = spec.split(AnsiRenderer.CODE_TEXT_SEPARATOR, 2);
+ if (items.length == 1) {
+ output.append(input);
+ return;
+ }
+ final String replacement = render(items[1], items[0].split(","));
+
+ output.append(replacement);
+
+ i = k + endTokenLen;
+ }
+ }
+
+ // EXACT COPY OF StringBuilder version of the method but typed as String for input
+ @Override
+ public void render(final String input, final StringBuilder output, final String styleName)
+ throws IllegalArgumentException {
+ output.append(render(input, styleName));
+ }
+
+ private Code toCode(final String name) {
+ return Code.valueOf(name.toUpperCase(Locale.ENGLISH));
+ }
+
+ @Override
+ public String toString() {
+ return "JAnsiMessageRenderer [beginToken=" + beginToken + ", beginTokenLen=" + beginTokenLen + ", endToken="
+ + endToken + ", endTokenLen=" + endTokenLen + ", styleMap=" + styleMap + "]";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
index a5058b0..75af44c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
@@ -36,7 +36,7 @@ public final class MessagePatternConverter extends LogEventPatternConverter {
private final String[] formats;
private final Configuration config;
- private final MessageRenderer messageRenderer;
+ private final TextRenderer textRenderer;
/**
* Private constructor.
@@ -48,22 +48,22 @@ public final class MessagePatternConverter extends LogEventPatternConverter {
super("Message", "message");
this.formats = options;
this.config = config;
- this.messageRenderer = loadMessageRenderer(options);
+ this.textRenderer = loadMessageRenderer(options);
}
- private MessageRenderer loadMessageRenderer(String[] options) {
+ private TextRenderer loadMessageRenderer(String[] options) {
if (formats != null && formats.length > 0) {
final String format = formats[0].toUpperCase(Locale.ROOT);
switch (format) {
case "ANSI":
- if (Loader.isClassAvailable("org.fusesource.jansi.AnsiRenderer")) {
- return new JAnsiMessageRenderer(formats);
+ if (Loader.isJansiAvailable()) {
+ return new JAnsiTextRenderer(formats, JAnsiTextRenderer.DefaultMessageStyleMap);
}
StatusLogger.getLogger()
.warn("You requested ANSI message rendering but JANSI is not on the classpath.");
return null;
case "HTML":
- return new HtmlMessageRenderer(formats);
+ return new HtmlTextRenderer(formats);
}
}
return null;
@@ -90,7 +90,7 @@ public final class MessagePatternConverter extends LogEventPatternConverter {
final Message msg = event.getMessage();
if (msg instanceof StringBuilderFormattable) {
- boolean doRender = messageRenderer != null;
+ boolean doRender = textRenderer != null;
StringBuilder workingBuilder = doRender ? new StringBuilder(80) : toAppendTo;
StringBuilderFormattable stringBuilderFormattable = (StringBuilderFormattable) msg;
@@ -108,7 +108,7 @@ public final class MessagePatternConverter extends LogEventPatternConverter {
}
}
if (doRender) {
- messageRenderer.render(workingBuilder, toAppendTo);
+ textRenderer.render(workingBuilder, toAppendTo);
}
return;
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessageRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessageRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessageRenderer.java
deleted file mode 100644
index 3612403..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessageRenderer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.pattern;
-
-/**
- * Renders an input to an output.
- */
-public interface MessageRenderer {
-
- /**
- * Renders an input to an output.
- *
- * @param input
- * @param output
- */
- void render(StringBuilder input, StringBuilder output);
-
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PlainTextRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PlainTextRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PlainTextRenderer.java
new file mode 100644
index 0000000..9e26bba
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PlainTextRenderer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.pattern;
+
+/**
+ Renders input unchanged.
+ */
+public final class PlainTextRenderer implements TextRenderer {
+
+ private static final PlainTextRenderer INSTANCE = new PlainTextRenderer();
+
+ public static PlainTextRenderer getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void render(String input, StringBuilder output, String styleName) {
+ output.append(input);
+ }
+
+ @Override
+ public void render(StringBuilder input, StringBuilder output) {
+ output.append(input);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/TextRenderer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/TextRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/TextRenderer.java
new file mode 100644
index 0000000..2355c88
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/TextRenderer.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pattern;
+
+/**
+ * Renders an input to an output.
+ */
+public interface TextRenderer {
+
+ /**
+ * Renders input text to an output.
+ *
+ * @param input
+ * The input
+ * @param output
+ * The output
+ * @param styleName
+ * The style name to use to render the input on the output.
+ */
+ void render(String input, StringBuilder output, String styleName);
+
+ /**
+ * Renders input text to an output.
+ *
+ * @param input
+ * The input
+ * @param output
+ * The output
+ */
+ void render(StringBuilder input, StringBuilder output);
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/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 bb35752..9f402cd 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
@@ -318,4 +318,9 @@ public final class Loader {
return false;
}
}
+
+ public static boolean isJansiAvailable() {
+ return isClassAvailable("org.fusesource.jansi.AnsiRenderer");
+ }
+
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderJAnsiXExceptionMain.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderJAnsiXExceptionMain.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderJAnsiXExceptionMain.java
new file mode 100644
index 0000000..d85c5b0
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderJAnsiXExceptionMain.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.junit.Test;
+
+/**
+ * Shows how to use ANSI escape codes to color messages. Each message is printed to the console in color, but the rest
+ * of the log entry (time stamp for example) is in the default color for that console.
+ * <p>
+ * Running from a Windows command line from the root of the project:
+ * </p>
+ *
+ * <pre>
+ * mvn -Dtest=org.apache.logging.log4j.core.appender.ConsoleAppenderJAnsiXExceptionMain test
+ * </pre>
+ *
+ * or, on Windows:
+ *
+ * <pre>
+ * java -classpath log4j-core\target\test-classes;log4j-core\target\classes;log4j-api\target\classes;%USERPROFILE%\.m2\repository\org\fusesource\jansi\jansi\1.13\jansi-1.13.jar; org.apache.logging.log4j.core.appender.ConsoleAppenderJAnsiXExceptionMain log4j-core/src/test/resources/log4j2-console-xex-ansi.xml
+ * </pre>
+ *
+ */
+public class ConsoleAppenderJAnsiXExceptionMain {
+
+ public static void main(final String[] args) {
+ new ConsoleAppenderJAnsiXExceptionMain().test(args);
+ }
+
+ /**
+ * This is a @Test method to make it easy to run from a command line with {@code mvn -Dtest=FQCN test}
+ */
+ @Test
+ public void test() {
+ test(null);
+ }
+
+ public void test(final String[] args) {
+ // System.out.println(System.getProperty("java.class.path"));
+ final String config = args == null || args.length == 0 ? "target/test-classes/log4j2-console-xex-ansi.xml"
+ : args[0];
+ final LoggerContext ctx = Configurator.initialize(ConsoleAppenderAnsiMessagesMain.class.getName(), config);
+ final Logger logger = LogManager.getLogger(ConsoleAppenderJAnsiXExceptionMain.class);
+ try {
+ IllegalArgumentException e = new IllegalArgumentException("Bad argument foo");
+ e.fillInStackTrace();
+ throw new IllegalStateException("You're going down!", e);
+ } catch (Exception e) {
+ logger.info("Gotcha!", e);
+ } finally {
+ Configurator.shutdown(ctx);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d7f9d951/log4j-core/src/test/resources/log4j2-console-xex-ansi.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j2-console-xex-ansi.xml b/log4j-core/src/test/resources/log4j2-console-xex-ansi.xml
new file mode 100644
index 0000000..d28b169
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j2-console-xex-ansi.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration status="OFF">
+ <Appenders>
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="%d [%t] %-5level: %msg{ansi}{CaughtMsg=red}%n%xThrowable{full}{ansi(CaughtMsg=red)}" />
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Logger name="org.foo" level="DEBUG" />
+ <Root level="TRACE">
+ <AppenderRef ref="Console" />
+ </Root>
+ </Loggers>
+</Configuration>