From 62187262cbb198411f352fb1e3428b8bdd0dc714 Mon Sep 17 00:00:00 2001 From: Burak KALAYCI Date: Thu, 27 Aug 2026 17:52:53 +0300 Subject: [PATCH 1/2] Fix predefined ANSI styles in JAnsiTextRenderer Style=Spock and Style=Kirk never applied because special-token keys were mixed-case while createMap stores upper-case keys, and predefined style maps kept mixed-case names. Also emit valid SGR sequences from entry(). Fixes #4254 Signed-off-by: Burak KALAYCI --- .../core/pattern/JAnsiTextRendererTest.java | 8 +++++++- .../log4j/core/pattern/AnsiEscape.java | 3 +++ .../log4j/core/pattern/JAnsiTextRenderer.java | 20 +++++++++---------- .../4254_fix_predefined_ansi_styles.xml | 12 +++++++++++ 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/JAnsiTextRendererTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/JAnsiTextRendererTest.java index 0595f68533d..bbdc7a3c02f 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/JAnsiTextRendererTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/JAnsiTextRendererTest.java @@ -41,7 +41,13 @@ public static Stream testRendering() { "\u001b[37mkey\u001b[m = \u001b[36;1msome value\u001b[m"), // Return broken escapes as is Arguments.of("", "Hello @|crazy|@ world!", "Hello @|crazy|@ world!"), - Arguments.of("", "Hello @|world!", "Hello @|world!")); + Arguments.of("", "Hello @|world!", "Hello @|world!"), + // Predefined Style=Spock: Name is BG_RED + WHITE + Arguments.of("Style=Spock", "@|Name XYZ|@", "\u001b[41;37mXYZ\u001b[m"), + // Predefined Style=Kirk: Name is BG_RED + YELLOW + BOLD + Arguments.of("Style=Kirk", "@|Name XYZ|@", "\u001b[41;33;1mXYZ\u001b[m"), + // User-supplied ANSI names still work when a predefined style is selected + Arguments.of("Style=Spock", "@|bg_red,white XYZ|@", "\u001b[41;37mXYZ\u001b[m")); } @ParameterizedTest diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AnsiEscape.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AnsiEscape.java index 34213373dfb..a2fb5155718 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AnsiEscape.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AnsiEscape.java @@ -427,6 +427,9 @@ public static Map createMap(final String[] values, final String[ static Map createMap( final String[] values, final String[] dontEscapeKeys, final String separatorRegex) { final String[] sortedIgnoreKeys = dontEscapeKeys != null ? dontEscapeKeys.clone() : Strings.EMPTY_ARRAY; + for (int i = 0; i < sortedIgnoreKeys.length; i++) { + sortedIgnoreKeys[i] = toRootUpperCase(sortedIgnoreKeys[i]); + } Arrays.sort(sortedIgnoreKeys); final Map map = new HashMap<>(); for (final String string : values) { 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 072dfeb87ff..88e38781d85 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 @@ -95,11 +95,11 @@ public final class JAnsiTextRenderer implements TextRenderer { private static final int CSI_LENGTH = 2; private static Map.Entry entry(final String name, final AnsiEscape... codes) { - final StringBuilder sb = new StringBuilder(AnsiEscape.CSI.getCode()); - for (final AnsiEscape code : codes) { - sb.append(code.getCode()); + final String[] names = new String[codes.length]; + for (int i = 0; i < codes.length; i++) { + names[i] = codes[i].name(); } - return new AbstractMap.SimpleImmutableEntry<>(name, sb.toString()); + return new AbstractMap.SimpleImmutableEntry<>(name, AnsiEscape.createSequence(names)); } @SafeVarargs @@ -201,12 +201,12 @@ public JAnsiTextRenderer(final String[] formats, final Map defau if (formats.length > 1) { final String stylesStr = formats[1]; final Map map = AnsiEscape.createMap( - stylesStr.split("\\s", -1), new String[] {"BeginToken", "EndToken", "Style"}, ","); + stylesStr.split("\\s", -1), new String[] {"BEGINTOKEN", "ENDTOKEN", "STYLE"}, ","); - // Handle the special tokens - beginToken = Objects.toString(map.remove("BeginToken"), BEGIN_TOKEN); - endToken = Objects.toString(map.remove("EndToken"), END_TOKEN); - final String predefinedStyle = map.remove("Style"); + // Handle the special tokens. createMap stores keys in root upper-case. + beginToken = Objects.toString(map.remove("BEGINTOKEN"), BEGIN_TOKEN); + endToken = Objects.toString(map.remove("ENDTOKEN"), END_TOKEN); + final String predefinedStyle = map.remove("STYLE"); // Create style map final Map styleMap = new HashMap<>(map.size() + defaultStyleMap.size()); @@ -214,7 +214,7 @@ public JAnsiTextRenderer(final String[] formats, final Map defau if (predefinedStyle != null) { final Map predefinedMap = PREFEDINED_STYLE_MAPS.get(predefinedStyle); if (predefinedMap != null) { - map.putAll(predefinedMap); + predefinedMap.forEach((k, v) -> map.put(toRootUpperCase(k), v)); } else { LOGGER.warn( "Unknown predefined map name {}, pick one of {}", diff --git a/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml b/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml new file mode 100644 index 00000000000..f3f4c1cee44 --- /dev/null +++ b/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml @@ -0,0 +1,12 @@ + + + + + Apply predefined ANSI styles (`Style=Spock`, `Style=Kirk`) in `JAnsiTextRenderer` and emit valid SGR sequences for those styles. + + From eb88ee7a2b338adf83c3805384e2494f990e75cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 15 Sep 2026 17:02:53 +0200 Subject: [PATCH 2/2] Add PR to the changelog --- src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml b/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml index f3f4c1cee44..8a99ea2cb01 100644 --- a/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml +++ b/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml @@ -6,6 +6,7 @@ https://logging.apache.org/xml/ns/log4j-changelog-0.xsd" type="fixed"> + Apply predefined ANSI styles (`Style=Spock`, `Style=Kirk`) in `JAnsiTextRenderer` and emit valid SGR sequences for those styles.