You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ra...@apache.org on 2026/10/01 17:50:51 UTC

(logging-log4j2) 22/33: Fix predefined ANSI styles in JAnsiTextRenderer (#4259)

This is an automated email from the ASF dual-hosted git repository.

ramanathan1504 pushed a commit to branch port/2.27.0-fixes-to-2.26.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 3ecdaf25ea59af27c75a0e5eaba4920cb7281f67
Author: Burak Kalaycı <ka...@gmail.com>
AuthorDate: Thu Sep 17 12:37:01 2026 +0300

    Fix predefined ANSI styles in JAnsiTextRenderer (#4259)
    
    Signed-off-by: Burak KALAYCI <ka...@gmail.com>
    (cherry picked from commit 89db25a2b7a231795da6209667ab5a162d987bb5)
---
 .../log4j/core/pattern/JAnsiTextRendererTest.java    |  8 +++++++-
 .../logging/log4j/core/pattern/AnsiEscape.java       |  3 +++
 .../log4j/core/pattern/JAnsiTextRenderer.java        | 20 ++++++++++----------
 .../.2.x.x/4254_fix_predefined_ansi_styles.xml       | 13 +++++++++++++
 4 files changed, 33 insertions(+), 11 deletions(-)

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 0595f68533..bbdc7a3c02 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 @@ class JAnsiTextRendererTest {
                         "\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 34213373df..a2fb515571 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 enum AnsiEscape {
     static Map<String, String> 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<String, String> 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 072dfeb87f..88e38781d8 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<String, String> 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 final class JAnsiTextRenderer implements TextRenderer {
         if (formats.length > 1) {
             final String stylesStr = formats[1];
             final Map<String, String> 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<String, String> styleMap = new HashMap<>(map.size() + defaultStyleMap.size());
@@ -214,7 +214,7 @@ public final class JAnsiTextRenderer implements TextRenderer {
             if (predefinedStyle != null) {
                 final Map<String, String> 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 0000000000..8a99ea2cb0
--- /dev/null
+++ b/src/changelog/.2.x.x/4254_fix_predefined_ansi_styles.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+           https://logging.apache.org/xml/ns
+           https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+       type="fixed">
+  <issue id="4254" link="https://github.com/apache/logging-log4j2/issues/4254"/>
+  <issue id="4259" link="https://github.com/apache/logging-log4j2/pull/4259"/>
+  <description format="asciidoc">
+    Apply predefined ANSI styles (`Style=Spock`, `Style=Kirk`) in `JAnsiTextRenderer` and emit valid SGR sequences for those styles.
+  </description>
+</entry>