You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2016/04/11 19:53:29 UTC

[02/11] logging-log4j2 git commit: LOG4J2-1343 OutputStreamManager now implements ByteBufferDestination

LOG4J2-1343 OutputStreamManager now implements ByteBufferDestination

- subclasses must pass a ByteBuffer to the constructor
- all write() operations get added to the buffer first before being flushed to the destination
- added separate methods for flushBuffer(), flushDestination() and writeToDestination(), subclasses may override


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/f29d6fd1
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/f29d6fd1
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/f29d6fd1

Branch: refs/heads/LOG4J2-1343-no-gc-os-appenders-bytebuffered
Commit: f29d6fd1e3ff852b7fa9fdf1a3b2bc2087591b31
Parents: 7c3aa06
Author: rpopma <rp...@apache.org>
Authored: Tue Apr 12 02:29:37 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Apr 12 02:29:37 2016 +0900

----------------------------------------------------------------------
 .../core/appender/OutputStreamManager.java      | 178 ++++++++++++-------
 1 file changed, 112 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f29d6fd1/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
index aff8573..7379804 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
@@ -19,23 +19,38 @@ package org.apache.logging.log4j.core.appender;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.util.Objects;
 
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.layout.ByteBufferDestination;
-import org.apache.logging.log4j.core.util.Constants;
 
 /**
  * Manages an OutputStream so that it can be shared by multiple Appenders and will
  * allow appenders to reconfigure without requiring a new stream.
  */
-public class OutputStreamManager extends AbstractManager {
+public class OutputStreamManager extends AbstractManager implements ByteBufferDestination {
+    protected static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
 
     private volatile OutputStream os;
     protected final Layout<?> layout;
-    private ByteBufferDestination byteBufferDestination;
+    protected ByteBuffer byteBuffer;
 
     protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
             final boolean writeHeader) {
+        this(os, streamName, layout, writeHeader, ByteBuffer.wrap(new byte[DEFAULT_BUFFER_SIZE]));
+    }
+
+    /**
+     *
+     * @param os
+     * @param streamName
+     * @param layout
+     * @param writeHeader
+     * @param byteBuffer
+     * @since 2.6
+     */
+    protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+            final boolean writeHeader, final ByteBuffer byteBuffer) {
         super(streamName);
         this.os = os;
         this.layout = layout;
@@ -49,6 +64,7 @@ public class OutputStreamManager extends AbstractManager {
                 }
             }
         }
+        this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
     }
 
     /**
@@ -114,6 +130,25 @@ public class OutputStreamManager extends AbstractManager {
     }
 
     /**
+     * Some output streams synchronize writes while others do not.
+     * @param bytes The serialized Log event.
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected void write(final byte[] bytes)  {
+        write(bytes, 0, bytes.length, false);
+    }
+
+    /**
+     * Some output streams synchronize writes while others do not.
+     * @param bytes The serialized Log event.
+     * @param immediateFlush If true, flushes after writing.
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected void write(final byte[] bytes, boolean immediateFlush)  {
+        write(bytes, 0, bytes.length, immediateFlush);
+    }
+
+    /**
      * Some output streams synchronize writes while others do not. Synchronizing here insures that
      * log events won't be intertwined.
      * @param bytes The serialized Log event.
@@ -122,7 +157,6 @@ public class OutputStreamManager extends AbstractManager {
      * @throws AppenderLoggingException if an error occurs.
      */
     protected void write(final byte[] bytes, final int offset, final int length) {
-        flushBuffer();
         write(bytes, offset, length, false);
     }
 
@@ -137,11 +171,32 @@ public class OutputStreamManager extends AbstractManager {
      */
     protected synchronized void write(final byte[] bytes, final int offset, final int length, boolean immediateFlush) {
         // System.out.println("write " + count);
+        if (length >= byteBuffer.capacity()) {
+            // if request length exceeds buffer capacity, flush the buffer and write the data directly
+            flush();
+            writeToDestination(bytes, offset, length);
+        } else {
+            if (length > byteBuffer.remaining()) {
+                flush();
+            }
+            byteBuffer.put(bytes, offset, length);
+        }
+        if (immediateFlush) {
+            flush();
+        }
+    }
+
+    /**
+     * Writes the specified section of the specified byte array to the stream.
+     *
+     * @param bytes the array containing data
+     * @param offset from where to write
+     * @param length how many bytes to write
+     * @since 2.6
+     */
+    protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
         try {
             os.write(bytes, offset, length);
-            if (immediateFlush) {
-                os.flush();
-            }
         } catch (final IOException ex) {
             final String msg = "Error writing to stream " + getName();
             throw new AppenderLoggingException(msg, ex);
@@ -149,24 +204,40 @@ public class OutputStreamManager extends AbstractManager {
     }
 
     /**
-     * Some output streams synchronize writes while others do not.
-     * @param bytes The serialized Log event.
-     * @throws AppenderLoggingException if an error occurs.
+     * Calls {@code flush()} on the underlying output stream.
+     * @since 2.6
      */
-    protected void write(final byte[] bytes)  {
-        flushBuffer();
-        write(bytes, 0, bytes.length, false);
+    protected synchronized void flushDestination() {
+        try {
+            os.flush();
+        } catch (final IOException ex) {
+            final String msg = "Error flushing stream " + getName();
+            throw new AppenderLoggingException(msg, ex);
+        }
     }
 
     /**
-     * Some output streams synchronize writes while others do not.
-     * @param bytes The serialized Log event.
-     * @param immediateFlush If true, flushes after writing.
-     * @throws AppenderLoggingException if an error occurs.
+     * Drains the ByteBufferDestination's buffer into the destination. By default this calls
+     * {@link OutputStreamManager#write(byte[], int, int, boolean)} with the buffer contents.
+     * The underlying stream is not {@linkplain OutputStream#flush() flushed}.
+     *
+     * @see #flushDestination()
+     * @since 2.6
      */
-    protected void write(final byte[] bytes, boolean immediateFlush)  {
-        flushBuffer();
-        write(bytes, 0, bytes.length, immediateFlush);
+    protected synchronized void flushBuffer(final ByteBuffer buf) {
+        buf.flip();
+        if (buf.limit() > 0) {
+            writeToDestination(buf.array(), 0, buf.limit());
+        }
+        buf.clear();
+    }
+
+    /**
+     * Flushes any buffers.
+     */
+    public synchronized void flush() {
+        flushBuffer(byteBuffer);
+        flushDestination();
     }
 
     protected synchronized void close() {
@@ -183,60 +254,35 @@ public class OutputStreamManager extends AbstractManager {
     }
 
     /**
-     * Flushes any buffers.
+     * Returns this {@code ByteBufferDestination}'s buffer.
+     * @return the buffer
+     * @since 2.6
      */
-    public synchronized void flush() {
-        try {
-            flushBuffer();
-            os.flush();
-        } catch (final IOException ex) {
-            final String msg = "Error flushing stream " + getName();
-            throw new AppenderLoggingException(msg, ex);
-        }
+    @Override
+    public ByteBuffer getByteBuffer() {
+        return byteBuffer;
     }
 
     /**
      * Drains the ByteBufferDestination's buffer into the destination. By default this calls
-     * {@link OutputStreamManager#write(byte[], int, int, boolean)} with the buffer contents and the Appender's
-     * {@link AbstractOutputStreamAppender#immediateFlush} value.
+     * {@link #flushBuffer(ByteBuffer)} with the specified buffer. Subclasses may override.
      * <p>
-     * This method has no effect if the garbage-free Layout encode mechanism is not enabled.
+     * Do not call this method lightly! For some subclasses this is a very expensive operation. For example,
+     * {@link MemoryMappedFileManager} will assume this method was called because the end of the mapped region
+     * was reached during a text encoding operation and will {@linkplain MemoryMappedFileManager#remap() remap} its
+     * buffer.
+     * </p><p>
+     * To just flush the buffered contents to the underlying stream, call
+     * {@link #flushBuffer(ByteBuffer)} directly instead.
      * </p>
-     */
-    protected void flushBuffer() {
-        if (Constants.ENABLE_DIRECT_ENCODERS) {
-            final ByteBufferDestination destination = getByteBufferDestination();
-            synchronized (destination) {
-                destination.drain(destination.getByteBuffer());
-            }
-        }
-    }
-
-    /**
-     * Subclasses that do buffered IO should override.
-     * @return this implementation always returns {@code false}
-     */
-    protected boolean isBufferedIO() {
-        return false;
-    }
-
-    public ByteBufferDestination getByteBufferDestination() {
-        return byteBufferDestination;
-    }
-
-    public void setByteBufferDestination(final ByteBufferDestination byteBufferDestination) {
-        this.byteBufferDestination = byteBufferDestination;
-    }
-
-    /**
-     * When the garbage-free Layout.encode mechanism is used, this method is called to create a ByteBufferDestination
-     * for this OutputStreamManager.
      *
-     * @param immediateFlush the value to pass to the {@link #write(byte[], int, int, boolean)} method when the
-     *          ByteBufferDestination is {@link ByteBufferDestination#drain(ByteBuffer) drained}
-     * @return a new ByteBufferDestination that drains into this OutputStreamManager
+     * @param buf the buffer whose contents to write the the destination
+     * @return the specified buffer
+     * @since 2.6
      */
-    protected ByteBufferDestination createByteBufferDestination(final boolean immediateFlush) {
-        return new OutputStreamManagerDestination(immediateFlush, this);
+    @Override
+    public ByteBuffer drain(final ByteBuffer buf) {
+        flushBuffer(buf);
+        return buf;
     }
 }