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/06 19:35:40 UTC

(logging-log4j2) branch main updated: [main] Honor bufferSize and bufferedIo in ConsoleAppender (#4341 port) (#4342)

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

ramanathan1504 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/main by this push:
     new 23bfb298cd [main] Honor bufferSize and bufferedIo in ConsoleAppender (#4341 port) (#4342)
23bfb298cd is described below

commit 23bfb298cd4cbf5a05d82b9d03a1ef66c35a2ba6
Author: Abhinav <al...@gmail.com>
AuthorDate: Wed Oct 7 01:05:34 2026 +0530

    [main] Honor bufferSize and bufferedIo in ConsoleAppender (#4341 port) (#4342)
    
    * [main] Honor bufferSize and bufferedIo in ConsoleAppender (#4338)
    
    * Port review fixes from #4341 to main
    
    The 2.x PR picked up three review changes after this port was opened, so
    main was left holding the pre-review revision:
    
    - drop the bufferSize/bufferedIo warning: bufferSize defaults to 8192, so
      it fired for every bufferedIo="false" console that never set a buffer
      size, and the Console manager uses bufferSize regardless
    - keep bufferedIo out of the manager name: the manager does not vary with
      bufferedIo, only the appender's flush flag does, so including it split
      identical managers
    - cover the flush derivation with testUnbufferedIoForcesImmediateFlush,
      which fails if !bufferedIo || isImmediateFlush() regresses to
      isImmediateFlush()
    
    * Update src/changelog/.3.x.x/4338_console_buffer_size.xml
    
    ---------
    Co-authored-by: Ramanathan <ra...@apache.org>
---
 .../log4j/core/appender/ConsoleAppenderTest.java   | 63 ++++++++++++++++++++++
 .../log4j/core/appender/ConsoleAppender.java       | 21 +++++---
 src/changelog/.3.x.x/4338_console_buffer_size.xml  | 13 +++++
 .../modules/ROOT/pages/manual/appenders.adoc       | 20 +++++++
 4 files changed, 110 insertions(+), 7 deletions(-)

diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
index e9959b1c04..93d409af92 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.appender;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,6 +34,7 @@ import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.Constants;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.jupiter.api.BeforeEach;
@@ -139,6 +141,67 @@ public class ConsoleAppenderTest {
         }
     }
 
+    @Test
+    public void testBufferSizeHonored() {
+        final ConsoleAppender app = ConsoleAppender.newBuilder()
+                .setName("testBufferSizeHonored")
+                .setBufferSize(16384)
+                .build();
+        try {
+            assertNotNull(app.getManager());
+            assertEquals(16384, app.getManager().getByteBuffer().capacity());
+        } finally {
+            app.stop();
+        }
+    }
+
+    @Test
+    public void testDefaultBufferSize() {
+        final ConsoleAppender app =
+                ConsoleAppender.newBuilder().setName("testDefaultBufferSize").build();
+        try {
+            assertEquals(
+                    Constants.ENCODER_BYTE_BUFFER_SIZE,
+                    app.getManager().getByteBuffer().capacity());
+        } finally {
+            app.stop();
+        }
+    }
+
+    @Test
+    public void testUnbufferedIoForcesImmediateFlush() {
+        final ConsoleAppender app = ConsoleAppender.newBuilder()
+                .setName("testUnbufferedIoForcesImmediateFlush")
+                .setBufferedIo(false)
+                .setImmediateFlush(false)
+                .build();
+        try {
+            assertTrue(app.getImmediateFlush());
+        } finally {
+            app.stop();
+        }
+    }
+
+    @Test
+    public void testDifferentBufferSizesUseDifferentManagers() {
+        final ConsoleAppender app1 = ConsoleAppender.newBuilder()
+                .setName("testBufferSizeManager1")
+                .setTarget(Target.SYSTEM_ERR)
+                .setBufferSize(8192)
+                .build();
+        final ConsoleAppender app2 = ConsoleAppender.newBuilder()
+                .setName("testBufferSizeManager2")
+                .setTarget(Target.SYSTEM_ERR)
+                .setBufferSize(16384)
+                .build();
+        try {
+            assertNotSame(app1.getManager(), app2.getManager());
+        } finally {
+            app1.stop();
+            app2.stop();
+        }
+    }
+
     @Test
     public void testFollowSystemErr() {
         testFollowSystemPrintStream(System.err, Target.SYSTEM_ERR, SystemSetter.SYSTEM_ERR);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java
index 6d4216c9fd..f19943625a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java
@@ -28,6 +28,7 @@ import org.apache.logging.log4j.core.Filter;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.config.Property;
 import org.apache.logging.log4j.core.util.CloseShieldOutputStream;
+import org.apache.logging.log4j.core.util.Constants;
 import org.apache.logging.log4j.plugins.Configurable;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
@@ -173,16 +174,18 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt
                     ? getDirectOutputStream(target)
                     : follow ? getFollowOutputStream(target) : getDefaultOutputStream(target);
 
-            final String managerName = target.name() + '.' + follow + '.' + direct;
-            final OutputStreamManager manager =
-                    OutputStreamManager.getManager(managerName, new FactoryData(stream, managerName, layout), factory);
+            final boolean bufferedIo = isBufferedIo();
+            final int bufferSize = getBufferSize();
+            final String managerName = target.name() + '.' + follow + '.' + direct + '.' + bufferSize;
+            final OutputStreamManager manager = OutputStreamManager.getManager(
+                    managerName, new FactoryData(stream, managerName, layout, bufferSize), factory);
             return new ConsoleAppender(
                     getName(),
                     layout,
                     getFilter(),
                     manager,
                     isIgnoreExceptions(),
-                    isImmediateFlush(),
+                    !bufferedIo || isImmediateFlush(),
                     target,
                     getPropertyArray());
         }
@@ -193,7 +196,8 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt
                 ConsoleAppender.DEFAULT_TARGET == Target.SYSTEM_ERR ? System.err : System.out);
         // LOG4J2-1176 DefaultConfiguration should not share OutputStreamManager instances to avoid memory leaks.
         final String managerName = ConsoleAppender.DEFAULT_TARGET.name() + ".false.false-" + COUNT.get();
-        return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory);
+        return OutputStreamManager.getManager(
+                managerName, new FactoryData(os, managerName, layout, Constants.ENCODER_BYTE_BUFFER_SIZE), factory);
     }
 
     private static OutputStream getDefaultOutputStream(Target target) {
@@ -280,6 +284,7 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt
         private final OutputStream os;
         private final String name;
         private final Layout layout;
+        private final int bufferSize;
 
         /**
          * Constructor.
@@ -287,11 +292,13 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt
          * @param os The OutputStream.
          * @param type The name of the target.
          * @param layout A layout
+         * @param bufferSize The buffer size.
          */
-        public FactoryData(final OutputStream os, final String type, final Layout layout) {
+        public FactoryData(final OutputStream os, final String type, final Layout layout, final int bufferSize) {
             this.os = os;
             this.name = type;
             this.layout = layout;
+            this.bufferSize = bufferSize;
         }
     }
 
@@ -309,7 +316,7 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt
          */
         @Override
         public OutputStreamManager createManager(final String name, final FactoryData data) {
-            return new OutputStreamManager(data.os, data.name, data.layout, true);
+            return new OutputStreamManager(data.os, data.name, data.layout, true, data.bufferSize);
         }
     }
 
diff --git a/src/changelog/.3.x.x/4338_console_buffer_size.xml b/src/changelog/.3.x.x/4338_console_buffer_size.xml
new file mode 100644
index 0000000000..ac3fa6dfe4
--- /dev/null
+++ b/src/changelog/.3.x.x/4338_console_buffer_size.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="4338" link="https://github.com/apache/logging-log4j2/issues/4338"/>
+  <issue id="4342" link="https://github.com/apache/logging-log4j2/pull/4342"/>                                                                                                  
+  <description format="asciidoc">
+    Honor the `bufferSize` and `bufferedIo` settings in `ConsoleAppender`.
+  </description>
+</entry>
diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc
index c7cb659092..d3660c7387 100644
--- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc
@@ -249,6 +249,26 @@ Some application servers modify `System.out` and `System.err` to always point to
 
 4+h| Optional
 
+| [[ConsoleAppender-attr-bufferSize]]bufferSize
+| `int`
+| xref:manual/systemproperties.adoc#log4j2.encoderByteBufferSize[`8192`]
+a|
+The size of the
+https://docs.oracle.com/javase/{java-target-version}/docs/api/java/nio/ByteBuffer.html[`ByteBuffer`]
+internally used by the appender.
+
+Appenders with different `bufferSize` values do not share the same manager.
+
+See <<buffering,Buffering>> for more details.
+
+| [[ConsoleAppender-attr-bufferedIo]]bufferedIo
+| `boolean`
+| `true`
+|
+If set to `true`, Log4j Core will format each log event in an internal buffer, before sending it to the underlying resource.
+
+See <<buffering,Buffering>> for more details.
+
 | [[ConsoleAppender-attr-direct]]direct
 | `boolean`
 | `false`