http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/aed68ee9/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 index 9b48b27..6c26e29 100644 --- 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 @@ -1,312 +1,320 @@ -/* - * 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; - static final Map<String, Code[]> DefaultMessageStyleMap; - private static final Map<String,Map<String, Code[]>> PrefedinedStyleMaps; - - static { - Map<String,Map<String, Code[]>> tempPreDefs = new HashMap<>(); - // Default: Spock - { - // 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.BG_RED, Code.WHITE }); - temp.put("Message", new Code[] { Code.BG_RED, Code.WHITE, Code.BOLD }); - 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.CYAN }); - temp.put("StackTraceElement.LineNumber", new Code[] { Code.CYAN }); - temp.put("StackTraceElement.Container", new Code[] { Code.CYAN }); - temp.put("StackTraceElement.ContainerSeparator", new Code[] { Code.WHITE}); - temp.put("StackTraceElement.UnknownSource", new Code[] { Code.CYAN }); - // ExtraClassInfo - temp.put("ExtraClassInfo.Inexact", new Code[] { Code.CYAN }); - temp.put("ExtraClassInfo.Container", new Code[] { Code.GREEN }); - temp.put("ExtraClassInfo.ContainerSeparator", new Code[] { Code.WHITE }); - temp.put("ExtraClassInfo.Location", new Code[] { Code.GREEN }); - temp.put("ExtraClassInfo.Version", new Code[] { Code.GREEN }); - // Save - DefaultExceptionStyleMap = Collections.unmodifiableMap(temp); - tempPreDefs.put("Spock", DefaultExceptionStyleMap); - } - // Kirk - { - // 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, Code.BOLD }); - 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.MAGENTA }); - temp.put("StackTraceElement.ClassMethodSeparator", new Code[] { Code.MAGENTA }); - temp.put("StackTraceElement.MethodName", new Code[] { Code.YELLOW }); - temp.put("StackTraceElement.NativeMethod", new Code[] { Code.MAGENTA }); - temp.put("StackTraceElement.FileName", new Code[] { Code.CYAN }); - temp.put("StackTraceElement.LineNumber", new Code[] { Code.CYAN }); - 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.BG_CYAN }); - temp.put("ExtraClassInfo.Container", new Code[] { Code.BG_CYAN }); - temp.put("ExtraClassInfo.ContainerSeparator", new Code[] { Code.BG_CYAN }); - temp.put("ExtraClassInfo.Location", new Code[] { Code.BG_CYAN }); - temp.put("ExtraClassInfo.Version", new Code[] { Code.BG_CYAN }); - // Save - tempPreDefs.put("Kirk", Collections.unmodifiableMap(temp)); - } - { - Map<String, Code[]> temp = new HashMap<>(); - // TODO - DefaultMessageStyleMap = Collections.unmodifiableMap(temp); - } - PrefedinedStyleMaps = Collections.unmodifiableMap(tempPreDefs); - } - - 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; - case "StyleMapName": - final String predefinedMapName = codeNames[0]; - Map<String, Code[]> predefinedMap = PrefedinedStyleMaps.get(predefinedMapName); - if (predefinedMap != null) { - map.putAll(predefinedMap); - } else { - StatusLogger.getLogger().warn("Unknown predefined map name {}, pick one of {}", - predefinedMapName, null); - } - 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 + "]"; - } - -} +/* + * 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; + static final Map<String, Code[]> DefaultMessageStyleMap; + private static final Map<String,Map<String, Code[]>> PrefedinedStyleMaps; + + static { + Map<String,Map<String, Code[]>> tempPreDefs = new HashMap<>(); + // Default style: Spock + { + // 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.BG_RED, Code.WHITE }); + temp.put("Message", new Code[] { Code.BG_RED, Code.WHITE, Code.BOLD }); + temp.put("At", new Code[] { Code.WHITE }); + temp.put("CauseLabel", new Code[] { Code.WHITE }); + temp.put("Text", new Code[] { Code.WHITE }); + temp.put("More", new Code[] { Code.WHITE }); + temp.put("Suppressed", 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.CYAN }); + temp.put("StackTraceElement.LineNumber", new Code[] { Code.CYAN }); + temp.put("StackTraceElement.Container", new Code[] { Code.CYAN }); + temp.put("StackTraceElement.ContainerSeparator", new Code[] { Code.WHITE}); + temp.put("StackTraceElement.UnknownSource", new Code[] { Code.CYAN }); + // ExtraClassInfo + temp.put("ExtraClassInfo.Inexact", new Code[] { Code.CYAN }); + temp.put("ExtraClassInfo.Container", new Code[] { Code.GREEN }); + temp.put("ExtraClassInfo.ContainerSeparator", new Code[] { Code.WHITE }); + temp.put("ExtraClassInfo.Location", new Code[] { Code.GREEN }); + temp.put("ExtraClassInfo.Version", new Code[] { Code.GREEN }); + // Save + DefaultExceptionStyleMap = Collections.unmodifiableMap(temp); + tempPreDefs.put("Spock", DefaultExceptionStyleMap); + } + // Style: Kirk + { + // 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.BG_RED, Code.YELLOW }); + temp.put("Message", new Code[] { Code.BG_RED, Code.WHITE, Code.BOLD }); + temp.put("At", new Code[] { Code.WHITE }); + temp.put("CauseLabel", new Code[] { Code.WHITE }); + temp.put("Text", new Code[] { Code.WHITE }); + temp.put("More", new Code[] { Code.WHITE }); + temp.put("Suppressed", new Code[] { Code.WHITE }); + // StackTraceElement + temp.put("StackTraceElement.ClassName", new Code[] { Code.BG_RED, Code.WHITE }); + temp.put("StackTraceElement.ClassMethodSeparator", new Code[] { Code.BG_RED, Code.YELLOW }); + temp.put("StackTraceElement.MethodName", new Code[] { Code.BG_RED, Code.YELLOW }); + temp.put("StackTraceElement.NativeMethod", new Code[] { Code.BG_RED, Code.YELLOW }); + temp.put("StackTraceElement.FileName", new Code[] { Code.CYAN }); + temp.put("StackTraceElement.LineNumber", new Code[] { Code.CYAN }); + temp.put("StackTraceElement.Container", new Code[] { Code.CYAN }); + temp.put("StackTraceElement.ContainerSeparator", new Code[] { Code.WHITE}); + temp.put("StackTraceElement.UnknownSource", new Code[] { Code.CYAN }); + // ExtraClassInfo + temp.put("ExtraClassInfo.Inexact", new Code[] { Code.CYAN }); + temp.put("ExtraClassInfo.Container", new Code[] { Code.GREEN }); + temp.put("ExtraClassInfo.ContainerSeparator", new Code[] { Code.WHITE }); + temp.put("ExtraClassInfo.Location", new Code[] { Code.GREEN }); + temp.put("ExtraClassInfo.Version", new Code[] { Code.GREEN }); + // Save + tempPreDefs.put("Kirk", Collections.unmodifiableMap(temp)); + } + { + Map<String, Code[]> temp = new HashMap<>(); + // TODO + DefaultMessageStyleMap = Collections.unmodifiableMap(temp); + } + PrefedinedStyleMaps = Collections.unmodifiableMap(tempPreDefs); + } + + 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; + case "StyleMapName": + final String predefinedMapName = codeNames[0]; + Map<String, Code[]> predefinedMap = PrefedinedStyleMaps.get(predefinedMapName); + if (predefinedMap != null) { + map.putAll(predefinedMap); + } else { + StatusLogger.getLogger().warn("Unknown predefined map name {}, pick one of {}", + predefinedMapName, null); + } + 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(); + } + + public Map<String, Code[]> getStyleMap() { + return styleMap; + } + + 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(); + } + + // 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)); + } + + @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/aed68ee9/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptionsTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptionsTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptionsTest.java index 80373a3..c3de0d3 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptionsTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ThrowableFormatOptionsTest.java @@ -16,15 +16,21 @@ */ package org.apache.logging.log4j.core.impl; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import java.util.Arrays; import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.core.pattern.JAnsiTextRenderer; +import org.apache.logging.log4j.core.pattern.TextRenderer; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.util.Strings; +import org.fusesource.jansi.AnsiRenderer.Code; +import org.junit.Assert; import org.junit.Test; -import static org.junit.Assert.*; - /** * Unit tests for {@code ThrowableFormatOptions}. */ @@ -32,22 +38,29 @@ public final class ThrowableFormatOptionsTest { /** * Runs a given test comparing against the expected values. - * @param options The list of options to parse. - * @param expectedLines The expected lines. - * @param expectedPackages The expected package filters. - * @param expectedSeparator The expected seperator. - */ - private static void test(final String[] options, final int expectedLines, final String expectedSeparator, final List<String> expectedPackages) { - final ThrowableFormatOptions o = ThrowableFormatOptions.newInstance(options); - assertEquals("getLines", expectedLines, o.getLines()); - assertEquals("getSeparator", expectedSeparator, o.getSeparator()); - assertEquals("getPackages", expectedPackages, o.getIgnorePackages()); - assertEquals("allLines", expectedLines == Integer.MAX_VALUE, o.allLines()); - assertEquals("anyLines", expectedLines != 0, o.anyLines()); - assertEquals("minLines", 0, o.minLines(0)); - assertEquals("minLines", expectedLines, o.minLines(Integer.MAX_VALUE)); - assertEquals("hasPackages", expectedPackages != null && !expectedPackages.isEmpty(), o.hasPackages()); - assertNotNull("toString", o.toString()); + * + * @param options + * The list of options to parse. + * @param expectedLines + * The expected lines. + * @param expectedPackages + * The expected package filters. + * @param expectedSeparator + * The expected separator. + */ + private static ThrowableFormatOptions test(final String[] options, final int expectedLines, + final String expectedSeparator, final List<String> expectedPackages) { + final ThrowableFormatOptions tfo = ThrowableFormatOptions.newInstance(options); + assertEquals("getLines", expectedLines, tfo.getLines()); + assertEquals("getSeparator", expectedSeparator, tfo.getSeparator()); + assertEquals("getPackages", expectedPackages, tfo.getIgnorePackages()); + assertEquals("allLines", expectedLines == Integer.MAX_VALUE, tfo.allLines()); + assertEquals("anyLines", expectedLines != 0, tfo.anyLines()); + assertEquals("minLines", 0, tfo.minLines(0)); + assertEquals("minLines", expectedLines, tfo.minLines(Integer.MAX_VALUE)); + assertEquals("hasPackages", expectedPackages != null && !expectedPackages.isEmpty(), tfo.hasPackages()); + assertNotNull("toString", tfo.toString()); + return tfo; } /** @@ -63,7 +76,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testEmpty() { - test(new String[]{}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + test(new String[] {}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); } /** @@ -71,7 +84,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testOneNullElement() { - test(new String[]{null}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + test(new String[] { null }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); } /** @@ -79,7 +92,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testOneEmptyElement() { - test(new String[]{""}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + test(new String[] { "" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); } /** @@ -87,7 +100,88 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFull() { - test(new String[]{"full"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + test(new String[] { "full" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + } + + /** + * Test {@code %throwable{full}{ansi} } + */ + @Test + public void testFullAnsi() { + ThrowableFormatOptions tfo = test(new String[] { "full", "ansi" }, + Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + testFullAnsiEmptyConfig(tfo); + } + + /** + * Test {@code %throwable{full}{ansi} } + */ + @Test + public void testFullAnsiEmptyConfig() { + ThrowableFormatOptions tfo = test(new String[] { "full", "ansi()" }, + Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + testFullAnsiEmptyConfig(tfo); + } + + private void testFullAnsiEmptyConfig(ThrowableFormatOptions tfo) { + TextRenderer textRenderer = tfo.getTextRenderer(); + Assert.assertNotNull(textRenderer); + Assert.assertTrue(textRenderer instanceof JAnsiTextRenderer); + JAnsiTextRenderer jansiRenderer = (JAnsiTextRenderer) textRenderer; + Map<String, Code[]> styleMap = jansiRenderer.getStyleMap(); + // We have defaults + Assert.assertFalse(styleMap.isEmpty()); + Assert.assertNotNull(styleMap.get("Name")); + } + + /** + * Test {@code %throwable{full}{ansi(ansi(Warning=red))} } + */ + @Test + public void testFullAnsiWithCustomStyle() { + ThrowableFormatOptions tfo = test(new String[] { "full", "ansi(Warning=red)" }, + Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + TextRenderer textRenderer = tfo.getTextRenderer(); + Assert.assertNotNull(textRenderer); + Assert.assertTrue(textRenderer instanceof JAnsiTextRenderer); + JAnsiTextRenderer jansiRenderer = (JAnsiTextRenderer) textRenderer; + Map<String, Code[]> styleMap = jansiRenderer.getStyleMap(); + Assert.assertArrayEquals(new Code[] { Code.RED }, styleMap.get("Warning")); + } + + /** + * Test {@code %throwable{full}{ansi(ansi(Warning=red Key=blue Value=cyan))} } + */ + @Test + public void testFullAnsiWithCustomStyles() { + ThrowableFormatOptions tfo = test(new String[] { "full", "ansi(Warning=red Key=blue Value=cyan)" }, + Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + TextRenderer textRenderer = tfo.getTextRenderer(); + Assert.assertNotNull(textRenderer); + Assert.assertTrue(textRenderer instanceof JAnsiTextRenderer); + JAnsiTextRenderer jansiRenderer = (JAnsiTextRenderer) textRenderer; + Map<String, Code[]> styleMap = jansiRenderer.getStyleMap(); + Assert.assertArrayEquals(new Code[] { Code.RED }, styleMap.get("Warning")); + Assert.assertArrayEquals(new Code[] { Code.BLUE }, styleMap.get("Key")); + Assert.assertArrayEquals(new Code[] { Code.CYAN }, styleMap.get("Value")); + } + + /** + * Test {@code %throwable{full}{ansi(Warning=red Key=blue,bg_red Value=cyan,bg_black,underline)} } + */ + @Test + public void testFullAnsiWithCustomComplexStyles() { + ThrowableFormatOptions tfo = test( + new String[] { "full", "ansi(Warning=red Key=blue,bg_red Value=cyan,bg_black,underline)" }, Integer.MAX_VALUE, + Constants.LINE_SEPARATOR, null); + TextRenderer textRenderer = tfo.getTextRenderer(); + Assert.assertNotNull(textRenderer); + Assert.assertTrue(textRenderer instanceof JAnsiTextRenderer); + JAnsiTextRenderer jansiRenderer = (JAnsiTextRenderer) textRenderer; + Map<String, Code[]> styleMap = jansiRenderer.getStyleMap(); + Assert.assertArrayEquals(new Code[] { Code.RED }, styleMap.get("Warning")); + Assert.assertArrayEquals(new Code[] { Code.BLUE, Code.BG_RED }, styleMap.get("Key")); + Assert.assertArrayEquals(new Code[] { Code.CYAN, Code.BG_BLACK, Code.UNDERLINE }, styleMap.get("Value")); } /** @@ -95,7 +189,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testNone() { - test(new String[]{"none"}, 0, Constants.LINE_SEPARATOR, null); + test(new String[] { "none" }, 0, Constants.LINE_SEPARATOR, null); } /** @@ -103,7 +197,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testShort() { - test(new String[]{"short"}, 2, Constants.LINE_SEPARATOR, null); + test(new String[] { "short" }, 2, Constants.LINE_SEPARATOR, null); } /** @@ -111,7 +205,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testDepth() { - test(new String[]{"10"}, 10, Constants.LINE_SEPARATOR, null); + test(new String[] { "10" }, 10, Constants.LINE_SEPARATOR, null); } /** @@ -119,7 +213,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSeparator() { - test(new String[]{"separator(|)"}, Integer.MAX_VALUE, "|", null); + test(new String[] { "separator(|)" }, Integer.MAX_VALUE, "|", null); } /** @@ -127,7 +221,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSeparatorAsEmpty() { - test(new String[]{"separator()"}, Integer.MAX_VALUE, Strings.EMPTY, null); + test(new String[] { "separator()" }, Integer.MAX_VALUE, Strings.EMPTY, null); } /** @@ -135,7 +229,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSeparatorAsDefaultLineSeparator() { - test(new String[]{"separator(" + Constants.LINE_SEPARATOR + ')'}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + test(new String[] { "separator(" + Constants.LINE_SEPARATOR + ')' }, Integer.MAX_VALUE, + Constants.LINE_SEPARATOR, null); } /** @@ -143,7 +238,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSeparatorAsMultipleCharacters() { - test(new String[]{"separator( | )"}, Integer.MAX_VALUE, " | ", null); + test(new String[] { "separator( | )" }, Integer.MAX_VALUE, " | ", null); } /** @@ -151,7 +246,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFullAndSeparator() { - test(new String[]{"full","separator(|)"}, Integer.MAX_VALUE, "|", null); + test(new String[] { "full", "separator(|)" }, Integer.MAX_VALUE, "|", null); } /** @@ -159,7 +254,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testNoneAndSeparator() { - test(new String[]{"none","separator(|)"}, 0, "|", null); + test(new String[] { "none", "separator(|)" }, 0, "|", null); } /** @@ -167,7 +262,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testShortAndSeparator() { - test(new String[]{"short","separator(|)"}, 2, "|", null); + test(new String[] { "short", "separator(|)" }, 2, "|", null); } /** @@ -175,7 +270,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testDepthAndSeparator() { - test(new String[]{"10","separator(|)"}, 10, "|", null); + test(new String[] { "10", "separator(|)" }, 10, "|", null); } /** @@ -183,7 +278,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFilters() { - test(new String[]{"filters(packages)"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "filters(packages)" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, + Arrays.asList("packages")); } /** @@ -191,7 +287,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFiltersAsEmpty() { - test(new String[]{"filters()"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); + test(new String[] { "filters()" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, null); } /** @@ -199,7 +295,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFiltersAsMultiplePackages() { - test(new String[]{"filters(package1,package2)"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, Arrays.asList("package1","package2")); + test(new String[] { "filters(package1,package2)" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, + Arrays.asList("package1", "package2")); } /** @@ -207,7 +304,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFullAndFilters() { - test(new String[]{"full","filters(packages)"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "full", "filters(packages)" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, + Arrays.asList("packages")); } /** @@ -215,7 +313,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testNoneAndFilters() { - test(new String[]{"none","filters(packages)"}, 0, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "none", "filters(packages)" }, 0, Constants.LINE_SEPARATOR, Arrays.asList("packages")); } /** @@ -223,7 +321,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testShortAndFilters() { - test(new String[]{"short","filters(packages)"}, 2, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "short", "filters(packages)" }, 2, Constants.LINE_SEPARATOR, Arrays.asList("packages")); } /** @@ -231,7 +329,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testDepthAndFilters() { - test(new String[]{"10","filters(packages)"}, 10, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "10", "filters(packages)" }, 10, Constants.LINE_SEPARATOR, Arrays.asList("packages")); } /** @@ -239,7 +337,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testFullAndSeparatorAndFilters() { - test(new String[]{"full","separator(|)","filters(packages)"}, Integer.MAX_VALUE, "|", Arrays.asList("packages")); + test(new String[] { "full", "separator(|)", "filters(packages)" }, Integer.MAX_VALUE, "|", + Arrays.asList("packages")); } /** @@ -247,7 +346,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testNoneAndSeparatorAndFilters() { - test(new String[]{"none","separator(|)","filters(packages)"}, 0, "|", Arrays.asList("packages")); + test(new String[] { "none", "separator(|)", "filters(packages)" }, 0, "|", Arrays.asList("packages")); } /** @@ -255,7 +354,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testShortAndSeparatorAndFilters() { - test(new String[]{"short","separator(|)","filters(packages)"}, 2, "|", Arrays.asList("packages")); + test(new String[] { "short", "separator(|)", "filters(packages)" }, 2, "|", Arrays.asList("packages")); } /** @@ -263,7 +362,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testDepthAndSeparatorAndFilters() { - test(new String[]{"10","separator(|)","filters(packages)"}, 10, "|", Arrays.asList("packages")); + test(new String[] { "10", "separator(|)", "filters(packages)" }, 10, "|", Arrays.asList("packages")); } /** @@ -271,7 +370,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionFullAndFilters() { - test(new String[]{"full,filters(packages)"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "full,filters(packages)" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, + Arrays.asList("packages")); } /** @@ -279,7 +379,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionNoneAndFilters() { - test(new String[]{"none,filters(packages)"}, 0, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "none,filters(packages)" }, 0, Constants.LINE_SEPARATOR, Arrays.asList("packages")); } /** @@ -287,7 +387,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionShortAndFilters() { - test(new String[]{"short,filters(packages)"}, 2, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "short,filters(packages)" }, 2, Constants.LINE_SEPARATOR, Arrays.asList("packages")); } /** @@ -295,7 +395,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionDepthAndFilters() { - test(new String[]{"10,filters(packages)"}, 10, Constants.LINE_SEPARATOR, Arrays.asList("packages")); + test(new String[] { "10,filters(packages)" }, 10, Constants.LINE_SEPARATOR, Arrays.asList("packages")); } /** @@ -303,7 +403,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionFullAndMultipleFilters() { - test(new String[]{"full,filters(package1,package2)"}, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, Arrays.asList("package1","package2")); + test(new String[] { "full,filters(package1,package2)" }, Integer.MAX_VALUE, Constants.LINE_SEPARATOR, + Arrays.asList("package1", "package2")); } /** @@ -311,7 +412,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionNoneAndMultipleFilters() { - test(new String[]{"none,filters(package1,package2)"}, 0, Constants.LINE_SEPARATOR, Arrays.asList("package1","package2")); + test(new String[] { "none,filters(package1,package2)" }, 0, Constants.LINE_SEPARATOR, + Arrays.asList("package1", "package2")); } /** @@ -319,7 +421,8 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionShortAndMultipleFilters() { - test(new String[]{"short,filters(package1,package2)"}, 2, Constants.LINE_SEPARATOR, Arrays.asList("package1","package2")); + test(new String[] { "short,filters(package1,package2)" }, 2, Constants.LINE_SEPARATOR, + Arrays.asList("package1", "package2")); } /** @@ -327,6 +430,7 @@ public final class ThrowableFormatOptionsTest { */ @Test public void testSingleOptionDepthAndMultipleFilters() { - test(new String[]{"10,filters(package1,package2)"}, 10, Constants.LINE_SEPARATOR, Arrays.asList("package1","package2")); + test(new String[] { "10,filters(package1,package2)" }, 10, Constants.LINE_SEPARATOR, + Arrays.asList("package1", "package2")); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/aed68ee9/log4j-core/src/test/resources/log4j2-console-xex-ansi-custom.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j2-console-xex-ansi-custom.xml b/log4j-core/src/test/resources/log4j2-console-xex-ansi-custom.xml new file mode 100644 index 0000000..5d54f15 --- /dev/null +++ b/log4j-core/src/test/resources/log4j2-console-xex-ansi-custom.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> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/aed68ee9/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 index d28b169..b4143f9 100644 --- a/log4j-core/src/test/resources/log4j2-console-xex-ansi.xml +++ b/log4j-core/src/test/resources/log4j2-console-xex-ansi.xml @@ -1,31 +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> +<?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%n%xThrowable{full}{ansi}" /> + </Console> + </Appenders> + <Loggers> + <Logger name="org.foo" level="DEBUG" /> + <Root level="TRACE"> + <AppenderRef ref="Console" /> + </Root> + </Loggers> +</Configuration>