diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractOutputStreamAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractOutputStreamAppender.java index 5ab28d5..9a4cfde 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractOutputStreamAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractOutputStreamAppender.java @@ -44,9 +44,6 @@ private final M manager; - private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); - private final Lock readLock = rwLock.readLock(); - /** * Instantiates a WriterAppender and set the output destination to a new {@link java.io.OutputStreamWriter} * initialized with os as its {@link java.io.OutputStream}. @@ -107,20 +104,14 @@ */ @Override public void append(final LogEvent event) { - readLock.lock(); try { final byte[] bytes = getLayout().toByteArray(event); - if (bytes.length > 0) { - manager.write(bytes); - if (this.immediateFlush || event.isEndOfBatch()) { - manager.flush(); - } + if (bytes != null && bytes.length > 0) { + manager.write(bytes, this.immediateFlush || event.isEndOfBatch()); } } catch (final AppenderLoggingException ex) { error("Unable to write to stream " + manager.getName() + " for appender " + getName()); throw ex; - } finally { - readLock.unlock(); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java index 827a9c0..4de34ad 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FileManager.java @@ -76,7 +76,7 @@ } @Override - protected synchronized void write(final byte[] bytes, final int offset, final int length) { + protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) { if (isLocking) { final FileChannel channel = ((FileOutputStream) getOutputStream()).getChannel(); @@ -90,7 +90,7 @@ files strings are configured that somehow map to the same file.*/ final FileLock lock = channel.lock(0, Long.MAX_VALUE, false); try { - super.write(bytes, offset, length); + super.write(bytes, offset, length, immediateFlush); } finally { lock.release(); } @@ -99,7 +99,7 @@ } } else { - super.write(bytes, offset, length); + super.write(bytes, offset, length, immediateFlush); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java index 7bd0eb5..9335ddb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java @@ -111,8 +111,8 @@ } @Override - protected synchronized void write(final byte[] bytes, int offset, int length) { - super.write(bytes, offset, length); // writes to dummy output stream + protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) { + super.write(bytes, offset, length, immediateFlush); // writes to dummy output stream while (length > mappedBuffer.remaining()) { final int chunk = mappedBuffer.remaining(); 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 99c3321..42eaa34 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 @@ -117,10 +117,26 @@ * @param length The number of bytes to write. * @throws AppenderLoggingException if an error occurs. */ - protected synchronized void write(final byte[] bytes, final int offset, final int length) { - //System.out.println("write " + count); + protected void write(final byte[] bytes, final int offset, final int length) { + write(bytes, offset, length, false); + } + + /** + * 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. + * @param offset The offset into the byte array. + * @param length The number of bytes to write. + * @param immediateFlush flushes immediately after writing. + * @throws AppenderLoggingException if an error occurs. + */ + protected synchronized void write(final byte[] bytes, final int offset, final int length, boolean immediateFlush) { + // System.out.println("write " + count); 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); @@ -133,7 +149,17 @@ * @throws AppenderLoggingException if an error occurs. */ protected void write(final byte[] bytes) { - write(bytes, 0, bytes.length); + write(bytes, 0, bytes.length, false); + } + + /** + * Some output streams synchronize writes while others do not. + * @param bytes The serialized Log event. + * @param writeAndFlush 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); } protected synchronized void close() { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java index c6627df..12af7ee 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java @@ -85,8 +85,8 @@ } @Override - protected synchronized void write(final byte[] bytes, int offset, int length) { - super.write(bytes, offset, length); // writes to dummy output stream + protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) { + super.write(bytes, offset, length, immediateFlush); // writes to dummy output stream int chunk = 0; do { @@ -99,7 +99,7 @@ length -= chunk; } while (length > 0); - if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) { + if (immediateFlush || isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) { flush(); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java index 48ff472..fc77a98 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java @@ -89,9 +89,9 @@ } @Override - protected synchronized void write(final byte[] bytes, final int offset, final int length) { + protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) { size += length; - super.write(bytes, offset, length); + super.write(bytes, offset, length, immediateFlush); } /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java index b732feb..56a1b47 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java @@ -94,8 +94,8 @@ } @Override - protected synchronized void write(final byte[] bytes, int offset, int length) { - super.write(bytes, offset, length); // writes to dummy output stream, needed to track file size + protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) { + super.write(bytes, offset, length, immediateFlush); // writes to dummy output stream, needed to track file size int chunk = 0; do { @@ -108,7 +108,7 @@ length -= chunk; } while (length > 0); - if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) { + if (immediateFlush || isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) { flush(); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java index a487438..d518aee 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/TcpSocketManager.java @@ -116,7 +116,7 @@ } @Override - protected void write(final byte[] bytes, final int offset, final int length) { + protected void write(final byte[] bytes, final int offset, final int length, boolean immediateFlush) { if (socket == null) { if (connector != null && !immediateFail) { connector.latch(); @@ -128,7 +128,11 @@ } synchronized (this) { try { - getOutputStream().write(bytes, offset, length); + final OutputStream outputStream = getOutputStream(); + outputStream.write(bytes, offset, length); + if (immediateFlush) { + outputStream.flush(); + } } catch (final IOException ex) { if (retry && connector == null) { connector = new Reconnector(this); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/RandomAccessFileManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/RandomAccessFileManagerTest.java index f127db3..07e8ab2 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/RandomAccessFileManagerTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/RandomAccessFileManagerTest.java @@ -130,7 +130,7 @@ final RandomAccessFileManager manager = RandomAccessFileManager.getFileManager( file.getAbsolutePath(), isAppend, true, RandomAccessFileManager.DEFAULT_BUFFER_SIZE, null, null); - manager.write(bytes, 0, bytes.length); + manager.write(bytes, 0, bytes.length, false); final int expected = bytes.length * 2; assertEquals("appended, not overwritten", expected, file.length()); } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java index d96d75e..4baddac 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManagerTest.java @@ -66,7 +66,7 @@ final int size = RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE * 3; final byte[] data = new byte[size]; - manager.write(data, 0, data.length); // no buffer overflow exception + manager.write(data, 0, data.length, flushNow); // no buffer overflow exception // buffer is full but not flushed yet assertEquals(RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE * 2, @@ -97,7 +97,7 @@ final int size = RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE * 3 + 1; final byte[] data = new byte[size]; - manager.write(data, 0, data.length); // no exception + manager.write(data, 0, data.length, flushNow); // no exception assertEquals(RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE * 3, raf.length()); @@ -146,13 +146,14 @@ } assertThat("all flushed to disk", file, hasLength(bytes.length)); + final boolean immediateFlush = true; final RollingRandomAccessFileManager manager = RollingRandomAccessFileManager .getRollingRandomAccessFileManager( // - file.getAbsolutePath(), Strings.EMPTY, isAppend, true, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, + file.getAbsolutePath(), Strings.EMPTY, isAppend, immediateFlush, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE, new SizeBasedTriggeringPolicy(Long.MAX_VALUE), // null, null, null); - manager.write(bytes, 0, bytes.length); + manager.write(bytes, 0, bytes.length, immediateFlush); final int expected = bytes.length * 2; assertThat("appended, not overwritten", file, hasLength(expected)); }