You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2016/09/07 17:02:59 UTC
[2/2] logging-log4j2 git commit:
org.apache.logging.log4j.core.appender.OutputStreamManager.closeOutputStream()
now returns a boolean to signal whether there was a problem closing any
resouce.
org.apache.logging.log4j.core.appender.OutputStreamManager.closeOutputStream()
now returns a boolean to signal whether there was a problem closing any
resouce.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/97e45ed6
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/97e45ed6
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/97e45ed6
Branch: refs/heads/master
Commit: 97e45ed63b19eb1bc99facacd2b9be01c003c335
Parents: 41ac242
Author: ggregory <gg...@US-L-GG02.rocketsoftware.com>
Authored: Wed Sep 7 13:02:54 2016 -0400
Committer: ggregory <gg...@US-L-GG02.rocketsoftware.com>
Committed: Wed Sep 7 13:02:54 2016 -0400
----------------------------------------------------------------------
.../core/appender/MemoryMappedFileManager.java | 716 ++++++++++---------
.../core/appender/OutputStreamManager.java | 686 +++++++++---------
.../core/appender/RandomAccessFileManager.java | 430 +++++------
.../rolling/RollingRandomAccessFileManager.java | 528 +++++++-------
.../log4j/core/net/TcpSocketManager.java | 652 ++++++++---------
5 files changed, 1511 insertions(+), 1501 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/97e45ed6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java
----------------------------------------------------------------------
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 a856610..332e8e4 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
@@ -1,357 +1,359 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.util.Closer;
-import org.apache.logging.log4j.core.util.NullOutputStream;
-
-//Lines too long...
-//CHECKSTYLE:OFF
-/**
- * Extends OutputStreamManager but instead of using a buffered output stream, this class maps a region of a file into
- * memory and writes to this memory region.
- * <p>
- *
- * @see <a href="http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java">
- * http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java</a>
- * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6893654">http://bugs.java.com/view_bug.do?bug_id=6893654</a>
- * @see <a href="http://bugs.java.com/view_bug.do?bug_id=4724038">http://bugs.java.com/view_bug.do?bug_id=4724038</a>
- * @see <a
- * href="http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation">
- * http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation</a>
- *
- * @since 2.1
- */
-//CHECKSTYLE:ON
-public class MemoryMappedFileManager extends OutputStreamManager {
- /**
- * Default length of region to map.
- */
- static final int DEFAULT_REGION_LENGTH = 32 * 1024 * 1024;
- private static final int MAX_REMAP_COUNT = 10;
- private static final MemoryMappedFileManagerFactory FACTORY = new MemoryMappedFileManagerFactory();
- private static final double NANOS_PER_MILLISEC = 1000.0 * 1000.0;
-
- private final boolean isForce;
- private final int regionLength;
- private final String advertiseURI;
- private final RandomAccessFile randomAccessFile;
- private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
- private MappedByteBuffer mappedBuffer;
- private long mappingOffset;
-
- protected MemoryMappedFileManager(final RandomAccessFile file, final String fileName, final OutputStream os,
- final boolean force, final long position, final int regionLength, final String advertiseURI,
- final Layout<? extends Serializable> layout, final boolean writeHeader) throws IOException {
- super(os, fileName, layout, writeHeader, ByteBuffer.wrap(new byte[0]));
- this.isForce = force;
- this.randomAccessFile = Objects.requireNonNull(file, "RandomAccessFile");
- this.regionLength = regionLength;
- this.advertiseURI = advertiseURI;
- this.isEndOfBatch.set(Boolean.FALSE);
- this.mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), position, regionLength);
- this.byteBuffer = mappedBuffer;
- this.mappingOffset = position;
- }
-
- /**
- * Returns the MemoryMappedFileManager.
- *
- * @param fileName The name of the file to manage.
- * @param append true if the file should be appended to, false if it should be overwritten.
- * @param isForce true if the contents should be flushed to disk on every write
- * @param regionLength The mapped region length.
- * @param advertiseURI the URI to use when advertising the file
- * @param layout The layout.
- * @return A MemoryMappedFileManager for the File.
- */
- public static MemoryMappedFileManager getFileManager(final String fileName, final boolean append,
- final boolean isForce, final int regionLength, final String advertiseURI,
- final Layout<? extends Serializable> layout) {
- return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isForce, regionLength,
- advertiseURI, layout), FACTORY);
- }
-
- public Boolean isEndOfBatch() {
- return isEndOfBatch.get();
- }
-
- public void setEndOfBatch(final boolean endOfBatch) {
- this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
- }
-
- @Override
- protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) {
- while (length > mappedBuffer.remaining()) {
- final int chunk = mappedBuffer.remaining();
- mappedBuffer.put(bytes, offset, chunk);
- offset += chunk;
- length -= chunk;
- remap();
- }
- mappedBuffer.put(bytes, offset, length);
-
- // no need to call flush() if force is true,
- // already done in AbstractOutputStreamAppender.append
- }
-
- private synchronized void remap() {
- final long offset = this.mappingOffset + mappedBuffer.position();
- final int length = mappedBuffer.remaining() + regionLength;
- try {
- unsafeUnmap(mappedBuffer);
- final long fileLength = randomAccessFile.length() + regionLength;
- LOGGER.debug("{} {} extending {} by {} bytes to {}", getClass().getSimpleName(), getName(), getFileName(),
- regionLength, fileLength);
-
- final long startNanos = System.nanoTime();
- randomAccessFile.setLength(fileLength);
- final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
- LOGGER.debug("{} {} extended {} OK in {} millis", getClass().getSimpleName(), getName(), getFileName(),
- millis);
-
- mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), offset, length);
- this.byteBuffer = mappedBuffer;
- mappingOffset = offset;
- } catch (final Exception ex) {
- logError("Unable to remap", ex);
- }
- }
-
- @Override
- public synchronized void flush() {
- mappedBuffer.force();
- }
-
- @Override
- public synchronized void closeOutputStream() {
- final long position = mappedBuffer.position();
- final long length = mappingOffset + position;
- try {
- unsafeUnmap(mappedBuffer);
- } catch (final Exception ex) {
- logError("Unable to unmap MappedBuffer", ex);
- }
- try {
- LOGGER.debug("MMapAppender closing. Setting {} length to {} (offset {} + position {})", getFileName(),
- length, mappingOffset, position);
- randomAccessFile.setLength(length);
- randomAccessFile.close();
- } catch (final IOException ex) {
- logError("Unable to close MemoryMappedFile", ex);
- }
- }
-
- public static MappedByteBuffer mmap(final FileChannel fileChannel, final String fileName, final long start,
- final int size) throws IOException {
- for (int i = 1;; i++) {
- try {
- LOGGER.debug("MMapAppender remapping {} start={}, size={}", fileName, start, size);
-
- final long startNanos = System.nanoTime();
- final MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size);
- map.order(ByteOrder.nativeOrder());
-
- final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
- LOGGER.debug("MMapAppender remapped {} OK in {} millis", fileName, millis);
-
- return map;
- } catch (final IOException e) {
- if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) {
- throw e;
- }
- LOGGER.debug("Remap attempt {}/{} failed. Retrying...", i, MAX_REMAP_COUNT, e);
- if (i < MAX_REMAP_COUNT) {
- Thread.yield();
- } else {
- try {
- Thread.sleep(1);
- } catch (final InterruptedException ignored) {
- Thread.currentThread().interrupt();
- throw e;
- }
- }
- }
- }
- }
-
- private static void unsafeUnmap(final MappedByteBuffer mbb) throws PrivilegedActionException {
- LOGGER.debug("MMapAppender unmapping old buffer...");
- final long startNanos = System.nanoTime();
- AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- @Override
- public Object run() throws Exception {
- final Method getCleanerMethod = mbb.getClass().getMethod("cleaner");
- getCleanerMethod.setAccessible(true);
- final Object cleaner = getCleanerMethod.invoke(mbb); // sun.misc.Cleaner instance
- final Method cleanMethod = cleaner.getClass().getMethod("clean");
- cleanMethod.invoke(cleaner);
- return null;
- }
- });
- final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
- LOGGER.debug("MMapAppender unmapped buffer OK in {} millis", millis);
- }
-
- /**
- * Returns the name of the File being managed.
- *
- * @return The name of the File being managed.
- */
- public String getFileName() {
- return getName();
- }
-
- /**
- * Returns the length of the memory mapped region.
- *
- * @return the length of the mapped region
- */
- public int getRegionLength() {
- return regionLength;
- }
-
- /**
- * Returns {@code true} if the content of the buffer should be forced to the storage device on every write,
- * {@code false} otherwise.
- *
- * @return whether each write should be force-sync'ed
- */
- public boolean isImmediateFlush() {
- return isForce;
- }
-
- /**
- * Gets this FileManager's content format specified by:
- * <p>
- * Key: "fileURI" Value: provided "advertiseURI" param.
- * </p>
- *
- * @return Map of content format keys supporting FileManager
- */
- @Override
- public Map<String, String> getContentFormat() {
- final Map<String, String> result = new HashMap<>(super.getContentFormat());
- result.put("fileURI", advertiseURI);
- return result;
- }
-
- @Override
- protected void flushBuffer(final ByteBuffer buffer) {
- // do nothing (do not call drain() to avoid spurious remapping)
- }
-
- @Override
- public ByteBuffer getByteBuffer() {
- return mappedBuffer;
- }
-
- @Override
- public ByteBuffer drain(final ByteBuffer buf) {
- remap();
- return mappedBuffer;
- }
-
- /**
- * Factory Data.
- */
- private static class FactoryData {
- private final boolean append;
- private final boolean force;
- private final int regionLength;
- private final String advertiseURI;
- private final Layout<? extends Serializable> layout;
-
- /**
- * Constructor.
- *
- * @param append Append to existing file or truncate.
- * @param force forces the memory content to be written to the storage device on every event
- * @param regionLength length of the mapped region
- * @param advertiseURI the URI to use when advertising the file
- * @param layout The layout.
- */
- public FactoryData(final boolean append, final boolean force, final int regionLength,
- final String advertiseURI, final Layout<? extends Serializable> layout) {
- this.append = append;
- this.force = force;
- this.regionLength = regionLength;
- this.advertiseURI = advertiseURI;
- this.layout = layout;
- }
- }
-
- /**
- * Factory to create a MemoryMappedFileManager.
- */
- private static class MemoryMappedFileManagerFactory
- implements ManagerFactory<MemoryMappedFileManager, FactoryData> {
-
- /**
- * Create a MemoryMappedFileManager.
- *
- * @param name The name of the File.
- * @param data The FactoryData
- * @return The MemoryMappedFileManager for the File.
- */
- @SuppressWarnings("resource")
- @Override
- public MemoryMappedFileManager createManager(final String name, final FactoryData data) {
- final File file = new File(name);
- final File parent = file.getParentFile();
- if (null != parent && !parent.exists()) {
- parent.mkdirs();
- }
- if (!data.append) {
- file.delete();
- }
-
- final boolean writeHeader = !data.append || !file.exists();
- final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
- RandomAccessFile raf = null;
- try {
- raf = new RandomAccessFile(name, "rw");
- final long position = (data.append) ? raf.length() : 0;
- raf.setLength(position + data.regionLength);
- return new MemoryMappedFileManager(raf, name, os, data.force, position, data.regionLength,
- data.advertiseURI, data.layout, writeHeader);
- } catch (final Exception ex) {
- LOGGER.error("MemoryMappedFileManager (" + name + ") " + ex, ex);
- Closer.closeSilently(raf);
- }
- return null;
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.util.Closer;
+import org.apache.logging.log4j.core.util.NullOutputStream;
+
+//Lines too long...
+//CHECKSTYLE:OFF
+/**
+ * Extends OutputStreamManager but instead of using a buffered output stream, this class maps a region of a file into
+ * memory and writes to this memory region.
+ * <p>
+ *
+ * @see <a href="http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java">
+ * http://www.codeproject.com/Tips/683614/Things-to-Know-about-Memory-Mapped-File-in-Java</a>
+ * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6893654">http://bugs.java.com/view_bug.do?bug_id=6893654</a>
+ * @see <a href="http://bugs.java.com/view_bug.do?bug_id=4724038">http://bugs.java.com/view_bug.do?bug_id=4724038</a>
+ * @see <a
+ * href="http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation">
+ * http://stackoverflow.com/questions/9261316/memory-mapped-mappedbytebuffer-or-direct-bytebuffer-for-db-implementation</a>
+ *
+ * @since 2.1
+ */
+//CHECKSTYLE:ON
+public class MemoryMappedFileManager extends OutputStreamManager {
+ /**
+ * Default length of region to map.
+ */
+ static final int DEFAULT_REGION_LENGTH = 32 * 1024 * 1024;
+ private static final int MAX_REMAP_COUNT = 10;
+ private static final MemoryMappedFileManagerFactory FACTORY = new MemoryMappedFileManagerFactory();
+ private static final double NANOS_PER_MILLISEC = 1000.0 * 1000.0;
+
+ private final boolean isForce;
+ private final int regionLength;
+ private final String advertiseURI;
+ private final RandomAccessFile randomAccessFile;
+ private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
+ private MappedByteBuffer mappedBuffer;
+ private long mappingOffset;
+
+ protected MemoryMappedFileManager(final RandomAccessFile file, final String fileName, final OutputStream os,
+ final boolean force, final long position, final int regionLength, final String advertiseURI,
+ final Layout<? extends Serializable> layout, final boolean writeHeader) throws IOException {
+ super(os, fileName, layout, writeHeader, ByteBuffer.wrap(new byte[0]));
+ this.isForce = force;
+ this.randomAccessFile = Objects.requireNonNull(file, "RandomAccessFile");
+ this.regionLength = regionLength;
+ this.advertiseURI = advertiseURI;
+ this.isEndOfBatch.set(Boolean.FALSE);
+ this.mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), position, regionLength);
+ this.byteBuffer = mappedBuffer;
+ this.mappingOffset = position;
+ }
+
+ /**
+ * Returns the MemoryMappedFileManager.
+ *
+ * @param fileName The name of the file to manage.
+ * @param append true if the file should be appended to, false if it should be overwritten.
+ * @param isForce true if the contents should be flushed to disk on every write
+ * @param regionLength The mapped region length.
+ * @param advertiseURI the URI to use when advertising the file
+ * @param layout The layout.
+ * @return A MemoryMappedFileManager for the File.
+ */
+ public static MemoryMappedFileManager getFileManager(final String fileName, final boolean append,
+ final boolean isForce, final int regionLength, final String advertiseURI,
+ final Layout<? extends Serializable> layout) {
+ return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isForce, regionLength,
+ advertiseURI, layout), FACTORY);
+ }
+
+ public Boolean isEndOfBatch() {
+ return isEndOfBatch.get();
+ }
+
+ public void setEndOfBatch(final boolean endOfBatch) {
+ this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
+ }
+
+ @Override
+ protected synchronized void write(final byte[] bytes, int offset, int length, final boolean immediateFlush) {
+ while (length > mappedBuffer.remaining()) {
+ final int chunk = mappedBuffer.remaining();
+ mappedBuffer.put(bytes, offset, chunk);
+ offset += chunk;
+ length -= chunk;
+ remap();
+ }
+ mappedBuffer.put(bytes, offset, length);
+
+ // no need to call flush() if force is true,
+ // already done in AbstractOutputStreamAppender.append
+ }
+
+ private synchronized void remap() {
+ final long offset = this.mappingOffset + mappedBuffer.position();
+ final int length = mappedBuffer.remaining() + regionLength;
+ try {
+ unsafeUnmap(mappedBuffer);
+ final long fileLength = randomAccessFile.length() + regionLength;
+ LOGGER.debug("{} {} extending {} by {} bytes to {}", getClass().getSimpleName(), getName(), getFileName(),
+ regionLength, fileLength);
+
+ final long startNanos = System.nanoTime();
+ randomAccessFile.setLength(fileLength);
+ final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
+ LOGGER.debug("{} {} extended {} OK in {} millis", getClass().getSimpleName(), getName(), getFileName(),
+ millis);
+
+ mappedBuffer = mmap(randomAccessFile.getChannel(), getFileName(), offset, length);
+ this.byteBuffer = mappedBuffer;
+ mappingOffset = offset;
+ } catch (final Exception ex) {
+ logError("Unable to remap", ex);
+ }
+ }
+
+ @Override
+ public synchronized void flush() {
+ mappedBuffer.force();
+ }
+
+ @Override
+ public synchronized boolean closeOutputStream() {
+ final long position = mappedBuffer.position();
+ final long length = mappingOffset + position;
+ try {
+ unsafeUnmap(mappedBuffer);
+ } catch (final Exception ex) {
+ logError("Unable to unmap MappedBuffer", ex);
+ }
+ try {
+ LOGGER.debug("MMapAppender closing. Setting {} length to {} (offset {} + position {})", getFileName(),
+ length, mappingOffset, position);
+ randomAccessFile.setLength(length);
+ randomAccessFile.close();
+ return true;
+ } catch (final IOException ex) {
+ logError("Unable to close MemoryMappedFile", ex);
+ return false;
+ }
+ }
+
+ public static MappedByteBuffer mmap(final FileChannel fileChannel, final String fileName, final long start,
+ final int size) throws IOException {
+ for (int i = 1;; i++) {
+ try {
+ LOGGER.debug("MMapAppender remapping {} start={}, size={}", fileName, start, size);
+
+ final long startNanos = System.nanoTime();
+ final MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size);
+ map.order(ByteOrder.nativeOrder());
+
+ final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
+ LOGGER.debug("MMapAppender remapped {} OK in {} millis", fileName, millis);
+
+ return map;
+ } catch (final IOException e) {
+ if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) {
+ throw e;
+ }
+ LOGGER.debug("Remap attempt {}/{} failed. Retrying...", i, MAX_REMAP_COUNT, e);
+ if (i < MAX_REMAP_COUNT) {
+ Thread.yield();
+ } else {
+ try {
+ Thread.sleep(1);
+ } catch (final InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ throw e;
+ }
+ }
+ }
+ }
+ }
+
+ private static void unsafeUnmap(final MappedByteBuffer mbb) throws PrivilegedActionException {
+ LOGGER.debug("MMapAppender unmapping old buffer...");
+ final long startNanos = System.nanoTime();
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ final Method getCleanerMethod = mbb.getClass().getMethod("cleaner");
+ getCleanerMethod.setAccessible(true);
+ final Object cleaner = getCleanerMethod.invoke(mbb); // sun.misc.Cleaner instance
+ final Method cleanMethod = cleaner.getClass().getMethod("clean");
+ cleanMethod.invoke(cleaner);
+ return null;
+ }
+ });
+ final float millis = (float) ((System.nanoTime() - startNanos) / NANOS_PER_MILLISEC);
+ LOGGER.debug("MMapAppender unmapped buffer OK in {} millis", millis);
+ }
+
+ /**
+ * Returns the name of the File being managed.
+ *
+ * @return The name of the File being managed.
+ */
+ public String getFileName() {
+ return getName();
+ }
+
+ /**
+ * Returns the length of the memory mapped region.
+ *
+ * @return the length of the mapped region
+ */
+ public int getRegionLength() {
+ return regionLength;
+ }
+
+ /**
+ * Returns {@code true} if the content of the buffer should be forced to the storage device on every write,
+ * {@code false} otherwise.
+ *
+ * @return whether each write should be force-sync'ed
+ */
+ public boolean isImmediateFlush() {
+ return isForce;
+ }
+
+ /**
+ * Gets this FileManager's content format specified by:
+ * <p>
+ * Key: "fileURI" Value: provided "advertiseURI" param.
+ * </p>
+ *
+ * @return Map of content format keys supporting FileManager
+ */
+ @Override
+ public Map<String, String> getContentFormat() {
+ final Map<String, String> result = new HashMap<>(super.getContentFormat());
+ result.put("fileURI", advertiseURI);
+ return result;
+ }
+
+ @Override
+ protected void flushBuffer(final ByteBuffer buffer) {
+ // do nothing (do not call drain() to avoid spurious remapping)
+ }
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return mappedBuffer;
+ }
+
+ @Override
+ public ByteBuffer drain(final ByteBuffer buf) {
+ remap();
+ return mappedBuffer;
+ }
+
+ /**
+ * Factory Data.
+ */
+ private static class FactoryData {
+ private final boolean append;
+ private final boolean force;
+ private final int regionLength;
+ private final String advertiseURI;
+ private final Layout<? extends Serializable> layout;
+
+ /**
+ * Constructor.
+ *
+ * @param append Append to existing file or truncate.
+ * @param force forces the memory content to be written to the storage device on every event
+ * @param regionLength length of the mapped region
+ * @param advertiseURI the URI to use when advertising the file
+ * @param layout The layout.
+ */
+ public FactoryData(final boolean append, final boolean force, final int regionLength,
+ final String advertiseURI, final Layout<? extends Serializable> layout) {
+ this.append = append;
+ this.force = force;
+ this.regionLength = regionLength;
+ this.advertiseURI = advertiseURI;
+ this.layout = layout;
+ }
+ }
+
+ /**
+ * Factory to create a MemoryMappedFileManager.
+ */
+ private static class MemoryMappedFileManagerFactory
+ implements ManagerFactory<MemoryMappedFileManager, FactoryData> {
+
+ /**
+ * Create a MemoryMappedFileManager.
+ *
+ * @param name The name of the File.
+ * @param data The FactoryData
+ * @return The MemoryMappedFileManager for the File.
+ */
+ @SuppressWarnings("resource")
+ @Override
+ public MemoryMappedFileManager createManager(final String name, final FactoryData data) {
+ final File file = new File(name);
+ final File parent = file.getParentFile();
+ if (null != parent && !parent.exists()) {
+ parent.mkdirs();
+ }
+ if (!data.append) {
+ file.delete();
+ }
+
+ final boolean writeHeader = !data.append || !file.exists();
+ final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
+ RandomAccessFile raf = null;
+ try {
+ raf = new RandomAccessFile(name, "rw");
+ final long position = (data.append) ? raf.length() : 0;
+ raf.setLength(position + data.regionLength);
+ return new MemoryMappedFileManager(raf, name, os, data.force, position, data.regionLength,
+ data.advertiseURI, data.layout, writeHeader);
+ } catch (final Exception ex) {
+ LOGGER.error("MemoryMappedFileManager (" + name + ") " + ex, ex);
+ Closer.closeSilently(raf);
+ }
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/97e45ed6/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 af4e11e..c5b7a45 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
@@ -1,342 +1,344 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LoggerContext;
-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 implements ByteBufferDestination {
- protected final Layout<?> layout;
- protected ByteBuffer byteBuffer;
- private volatile OutputStream os;
- private boolean skipFooter;
-
- protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
- final boolean writeHeader) {
- // Can't use new ctor because it throws an exception
- this(os, streamName, layout, writeHeader, Constants.ENCODER_BYTE_BUFFER_SIZE);
- }
-
- protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
- final boolean writeHeader, final int bufferSize) {
- // Can't use new ctor because it throws an exception
- this(os, streamName, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
- }
-
- /**
- * @since 2.6
- * @deprecated
- */
- @Deprecated
- protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
- final boolean writeHeader, final ByteBuffer byteBuffer) {
- super(null, streamName);
- this.os = os;
- this.layout = layout;
- if (writeHeader && layout != null) {
- final byte[] header = layout.getHeader();
- if (header != null) {
- try {
- getOutputStream().write(header, 0, header.length);
- } catch (final IOException e) {
- logError("Unable to write header", e);
- }
- }
- }
- this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
- }
-
- /**
- * @since 2.7
- */
- protected OutputStreamManager(final LoggerContext loggerContext, final OutputStream os, final String streamName,
- final boolean createOnDemand, final Layout<? extends Serializable> layout, final boolean writeHeader,
- final ByteBuffer byteBuffer) {
- super(loggerContext, streamName);
- if (createOnDemand && os != null) {
- LOGGER.error(
- "Invalid OutputStreamManager configuration for '{}': You cannot both set the OutputStream and request on-demand.",
- streamName);
- }
- this.layout = layout;
- this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
- this.os = os;
- if (writeHeader && layout != null) {
- final byte[] header = layout.getHeader();
- if (header != null) {
- try {
- getOutputStream().write(header, 0, header.length);
- } catch (final IOException e) {
- logError("Unable to write header for " + streamName, e);
- }
- }
- }
- }
-
- /**
- * Creates a Manager.
- *
- * @param name The name of the stream to manage.
- * @param data The data to pass to the Manager.
- * @param factory The factory to use to create the Manager.
- * @param <T> The type of the OutputStreamManager.
- * @return An OutputStreamManager.
- */
- public static <T> OutputStreamManager getManager(final String name, final T data,
- final ManagerFactory<? extends OutputStreamManager, T> factory) {
- return AbstractManager.getManager(name, factory, data);
- }
-
- @SuppressWarnings("unused")
- protected OutputStream createOutputStream() throws IOException {
- throw new IllegalStateException(getClass().getCanonicalName() + " must implement createOutputStream()");
- }
-
- /**
- * Indicate whether the footer should be skipped or not.
- * @param skipFooter true if the footer should be skipped.
- */
- public void skipFooter(final boolean skipFooter) {
- this.skipFooter = skipFooter;
- }
-
- /**
- * Default hook to write footer during close.
- */
- @Override
- public void releaseSub(final long timeout, final TimeUnit timeUnit) {
- writeFooter();
- closeOutputStream();
- }
-
- /**
- * Writes the footer.
- */
- protected void writeFooter() {
- if (layout == null || skipFooter) {
- return;
- }
- final byte[] footer = layout.getFooter();
- if (footer != null) {
- write(footer);
- }
- }
-
- /**
- * Returns the status of the stream.
- * @return true if the stream is open, false if it is not.
- */
- public boolean isOpen() {
- return getCount() > 0;
- }
-
- protected OutputStream getOutputStream() throws IOException {
- if (os == null) {
- os = createOutputStream();
- }
- return os;
- }
-
- protected void setOutputStream(final OutputStream os) {
- final byte[] header = layout.getHeader();
- if (header != null) {
- try {
- os.write(header, 0, header.length);
- this.os = os; // only update field if os.write() succeeded
- } catch (final IOException ioe) {
- logError("Unable to write header", ioe);
- }
- } else {
- this.os = os;
- }
- }
-
- /**
- * 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, final 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.
- * @param offset The offset into the byte array.
- * @param length The number of bytes to write.
- * @throws AppenderLoggingException if an error occurs.
- */
- 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, final boolean immediateFlush) {
- if (immediateFlush && byteBuffer.position() == 0) {
- writeToDestination(bytes, offset, length);
- flushDestination();
- return;
- }
- 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 {
- getOutputStream().write(bytes, offset, length);
- } catch (final IOException ex) {
- throw new AppenderLoggingException("Error writing to stream " + getName(), ex);
- }
- }
-
- /**
- * Calls {@code flush()} on the underlying output stream.
- * @since 2.6
- */
- protected synchronized void flushDestination() {
- final OutputStream stream = os; // access volatile field only once per method
- if (stream != null) {
- try {
- stream.flush();
- } catch (final IOException ex) {
- throw new AppenderLoggingException("Error flushing stream " + getName(), ex);
- }
- }
- }
-
- /**
- * 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 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 closeOutputStream() {
- flush();
- final OutputStream stream = os; // access volatile field only once per method
- if (stream == null || stream == System.out || stream == System.err) {
- return;
- }
- try {
- stream.close();
- } catch (final IOException ex) {
- logError("Unable to close stream", ex);
- }
- }
-
- /**
- * Returns this {@code ByteBufferDestination}'s buffer.
- * @return the buffer
- * @since 2.6
- */
- @Override
- public ByteBuffer getByteBuffer() {
- return byteBuffer;
- }
-
- /**
- * Drains the ByteBufferDestination's buffer into the destination. By default this calls
- * {@link #flushBuffer(ByteBuffer)} with the specified buffer. Subclasses may override.
- * <p>
- * 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>
- *
- * @param buf the buffer whose contents to write the the destination
- * @return the specified buffer
- * @since 2.6
- */
- @Override
- public ByteBuffer drain(final ByteBuffer buf) {
- flushBuffer(buf);
- return buf;
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+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 implements ByteBufferDestination {
+ protected final Layout<?> layout;
+ protected ByteBuffer byteBuffer;
+ private volatile OutputStream os;
+ private boolean skipFooter;
+
+ protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+ final boolean writeHeader) {
+ // Can't use new ctor because it throws an exception
+ this(os, streamName, layout, writeHeader, Constants.ENCODER_BYTE_BUFFER_SIZE);
+ }
+
+ protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+ final boolean writeHeader, final int bufferSize) {
+ // Can't use new ctor because it throws an exception
+ this(os, streamName, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
+ }
+
+ /**
+ * @since 2.6
+ * @deprecated
+ */
+ @Deprecated
+ protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
+ final boolean writeHeader, final ByteBuffer byteBuffer) {
+ super(null, streamName);
+ this.os = os;
+ this.layout = layout;
+ if (writeHeader && layout != null) {
+ final byte[] header = layout.getHeader();
+ if (header != null) {
+ try {
+ getOutputStream().write(header, 0, header.length);
+ } catch (final IOException e) {
+ logError("Unable to write header", e);
+ }
+ }
+ }
+ this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
+ }
+
+ /**
+ * @since 2.7
+ */
+ protected OutputStreamManager(final LoggerContext loggerContext, final OutputStream os, final String streamName,
+ final boolean createOnDemand, final Layout<? extends Serializable> layout, final boolean writeHeader,
+ final ByteBuffer byteBuffer) {
+ super(loggerContext, streamName);
+ if (createOnDemand && os != null) {
+ LOGGER.error(
+ "Invalid OutputStreamManager configuration for '{}': You cannot both set the OutputStream and request on-demand.",
+ streamName);
+ }
+ this.layout = layout;
+ this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
+ this.os = os;
+ if (writeHeader && layout != null) {
+ final byte[] header = layout.getHeader();
+ if (header != null) {
+ try {
+ getOutputStream().write(header, 0, header.length);
+ } catch (final IOException e) {
+ logError("Unable to write header for " + streamName, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a Manager.
+ *
+ * @param name The name of the stream to manage.
+ * @param data The data to pass to the Manager.
+ * @param factory The factory to use to create the Manager.
+ * @param <T> The type of the OutputStreamManager.
+ * @return An OutputStreamManager.
+ */
+ public static <T> OutputStreamManager getManager(final String name, final T data,
+ final ManagerFactory<? extends OutputStreamManager, T> factory) {
+ return AbstractManager.getManager(name, factory, data);
+ }
+
+ @SuppressWarnings("unused")
+ protected OutputStream createOutputStream() throws IOException {
+ throw new IllegalStateException(getClass().getCanonicalName() + " must implement createOutputStream()");
+ }
+
+ /**
+ * Indicate whether the footer should be skipped or not.
+ * @param skipFooter true if the footer should be skipped.
+ */
+ public void skipFooter(final boolean skipFooter) {
+ this.skipFooter = skipFooter;
+ }
+
+ /**
+ * Default hook to write footer during close.
+ */
+ @Override
+ public void releaseSub(final long timeout, final TimeUnit timeUnit) {
+ writeFooter();
+ closeOutputStream();
+ }
+
+ /**
+ * Writes the footer.
+ */
+ protected void writeFooter() {
+ if (layout == null || skipFooter) {
+ return;
+ }
+ final byte[] footer = layout.getFooter();
+ if (footer != null) {
+ write(footer);
+ }
+ }
+
+ /**
+ * Returns the status of the stream.
+ * @return true if the stream is open, false if it is not.
+ */
+ public boolean isOpen() {
+ return getCount() > 0;
+ }
+
+ protected OutputStream getOutputStream() throws IOException {
+ if (os == null) {
+ os = createOutputStream();
+ }
+ return os;
+ }
+
+ protected void setOutputStream(final OutputStream os) {
+ final byte[] header = layout.getHeader();
+ if (header != null) {
+ try {
+ os.write(header, 0, header.length);
+ this.os = os; // only update field if os.write() succeeded
+ } catch (final IOException ioe) {
+ logError("Unable to write header", ioe);
+ }
+ } else {
+ this.os = os;
+ }
+ }
+
+ /**
+ * 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, final 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.
+ * @param offset The offset into the byte array.
+ * @param length The number of bytes to write.
+ * @throws AppenderLoggingException if an error occurs.
+ */
+ 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, final boolean immediateFlush) {
+ if (immediateFlush && byteBuffer.position() == 0) {
+ writeToDestination(bytes, offset, length);
+ flushDestination();
+ return;
+ }
+ 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 {
+ getOutputStream().write(bytes, offset, length);
+ } catch (final IOException ex) {
+ throw new AppenderLoggingException("Error writing to stream " + getName(), ex);
+ }
+ }
+
+ /**
+ * Calls {@code flush()} on the underlying output stream.
+ * @since 2.6
+ */
+ protected synchronized void flushDestination() {
+ final OutputStream stream = os; // access volatile field only once per method
+ if (stream != null) {
+ try {
+ stream.flush();
+ } catch (final IOException ex) {
+ throw new AppenderLoggingException("Error flushing stream " + getName(), ex);
+ }
+ }
+ }
+
+ /**
+ * 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 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 boolean closeOutputStream() {
+ flush();
+ final OutputStream stream = os; // access volatile field only once per method
+ if (stream == null || stream == System.out || stream == System.err) {
+ return true;
+ }
+ try {
+ stream.close();
+ } catch (final IOException ex) {
+ logError("Unable to close stream", ex);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns this {@code ByteBufferDestination}'s buffer.
+ * @return the buffer
+ * @since 2.6
+ */
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return byteBuffer;
+ }
+
+ /**
+ * Drains the ByteBufferDestination's buffer into the destination. By default this calls
+ * {@link #flushBuffer(ByteBuffer)} with the specified buffer. Subclasses may override.
+ * <p>
+ * 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>
+ *
+ * @param buf the buffer whose contents to write the the destination
+ * @return the specified buffer
+ * @since 2.6
+ */
+ @Override
+ public ByteBuffer drain(final ByteBuffer buf) {
+ flushBuffer(buf);
+ return buf;
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/97e45ed6/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RandomAccessFileManager.java
----------------------------------------------------------------------
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 68a6f81..4cd5459 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
@@ -1,214 +1,216 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.util.NullOutputStream;
-
-/**
- * Extends OutputStreamManager but instead of using a buffered output stream,
- * this class uses a {@code ByteBuffer} and a {@code RandomAccessFile} to do the
- * I/O.
- */
-public class RandomAccessFileManager extends OutputStreamManager {
- static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
-
- private static final RandomAccessFileManagerFactory FACTORY = new RandomAccessFileManagerFactory();
-
- private final String advertiseURI;
- private final RandomAccessFile randomAccessFile;
- private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
-
- protected RandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile file, final String fileName,
- final OutputStream os, final int bufferSize, final String advertiseURI,
- final Layout<? extends Serializable> layout, final boolean writeHeader) {
- super(loggerContext, os, fileName, false, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
- this.randomAccessFile = file;
- this.advertiseURI = advertiseURI;
- this.isEndOfBatch.set(Boolean.FALSE);
- }
-
- /**
- * Returns the RandomAccessFileManager.
- *
- * @param fileName The name of the file to manage.
- * @param append true if the file should be appended to, false if it should
- * be overwritten.
- * @param isFlush true if the contents should be flushed to disk on every
- * write
- * @param bufferSize The buffer size.
- * @param advertiseURI the URI to use when advertising the file
- * @param layout The layout.
- * @param configuration The configuration.
- * @return A RandomAccessFileManager for the File.
- */
- public static RandomAccessFileManager getFileManager(final String fileName, final boolean append,
- final boolean isFlush, final int bufferSize, final String advertiseURI,
- final Layout<? extends Serializable> layout, final Configuration configuration) {
- return (RandomAccessFileManager) getManager(fileName, new FactoryData(append,
- isFlush, bufferSize, advertiseURI, layout, configuration), FACTORY);
- }
-
- public Boolean isEndOfBatch() {
- return isEndOfBatch.get();
- }
-
- public void setEndOfBatch(final boolean endOfBatch) {
- this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
- }
-
- @Override
- protected void writeToDestination(final byte[] bytes, final int offset, final int length) {
- try {
- randomAccessFile.write(bytes, offset, length);
- } catch (final IOException ex) {
- final String msg = "Error writing to RandomAccessFile " + getName();
- throw new AppenderLoggingException(msg, ex);
- }
- }
-
- @Override
- public synchronized void flush() {
- flushBuffer(byteBuffer);
- }
-
- @Override
- public synchronized void closeOutputStream() {
- flush();
- try {
- randomAccessFile.close();
- } catch (final IOException ex) {
- logError("Unable to close RandomAccessFile", ex);
- }
- }
-
- /**
- * Returns the name of the File being managed.
- *
- * @return The name of the File being managed.
- */
- public String getFileName() {
- return getName();
- }
-
- /**
- * Returns the buffer capacity.
- * @return the buffer size
- */
- public int getBufferSize() {
- return byteBuffer.capacity();
- }
-
- /**
- * Gets this FileManager's content format specified by:
- * <p>
- * Key: "fileURI" Value: provided "advertiseURI" param.
- * </p>
- *
- * @return Map of content format keys supporting FileManager
- */
- @Override
- public Map<String, String> getContentFormat() {
- final Map<String, String> result = new HashMap<>(
- super.getContentFormat());
- result.put("fileURI", advertiseURI);
- return result;
- }
-
- /**
- * Factory Data.
- */
- private static class FactoryData extends ConfigurationFactoryData {
- private final boolean append;
- private final boolean immediateFlush;
- private final int bufferSize;
- private final String advertiseURI;
- private final Layout<? extends Serializable> layout;
-
- /**
- * Constructor.
- *
- * @param append Append status.
- * @param bufferSize size of the buffer
- * @param configuration The configuration.
- */
- public FactoryData(final boolean append, final boolean immediateFlush, final int bufferSize,
- final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
- super(configuration);
- this.append = append;
- this.immediateFlush = immediateFlush;
- this.bufferSize = bufferSize;
- this.advertiseURI = advertiseURI;
- this.layout = layout;
- }
- }
-
- /**
- * Factory to create a RandomAccessFileManager.
- */
- private static class RandomAccessFileManagerFactory implements
- ManagerFactory<RandomAccessFileManager, FactoryData> {
-
- /**
- * Create a RandomAccessFileManager.
- *
- * @param name The name of the File.
- * @param data The FactoryData
- * @return The RandomAccessFileManager for the File.
- */
- @Override
- public RandomAccessFileManager createManager(final String name, final FactoryData data) {
- final File file = new File(name);
- final File parent = file.getParentFile();
- if (null != parent && !parent.exists()) {
- parent.mkdirs();
- }
- if (!data.append) {
- file.delete();
- }
-
- final boolean writeHeader = !data.append || !file.exists();
- final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
- RandomAccessFile raf;
- try {
- raf = new RandomAccessFile(name, "rw");
- if (data.append) {
- raf.seek(raf.length());
- } else {
- raf.setLength(0);
- }
- return new RandomAccessFileManager(data.getLoggerContext(), raf, name,
- os, data.bufferSize, data.advertiseURI, data.layout, writeHeader);
- } catch (final Exception ex) {
- LOGGER.error("RandomAccessFileManager (" + name + ") " + ex, ex);
- }
- return null;
- }
- }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.util.NullOutputStream;
+
+/**
+ * Extends OutputStreamManager but instead of using a buffered output stream,
+ * this class uses a {@code ByteBuffer} and a {@code RandomAccessFile} to do the
+ * I/O.
+ */
+public class RandomAccessFileManager extends OutputStreamManager {
+ static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+
+ private static final RandomAccessFileManagerFactory FACTORY = new RandomAccessFileManagerFactory();
+
+ private final String advertiseURI;
+ private final RandomAccessFile randomAccessFile;
+ private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
+
+ protected RandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile file, final String fileName,
+ final OutputStream os, final int bufferSize, final String advertiseURI,
+ final Layout<? extends Serializable> layout, final boolean writeHeader) {
+ super(loggerContext, os, fileName, false, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
+ this.randomAccessFile = file;
+ this.advertiseURI = advertiseURI;
+ this.isEndOfBatch.set(Boolean.FALSE);
+ }
+
+ /**
+ * Returns the RandomAccessFileManager.
+ *
+ * @param fileName The name of the file to manage.
+ * @param append true if the file should be appended to, false if it should
+ * be overwritten.
+ * @param isFlush true if the contents should be flushed to disk on every
+ * write
+ * @param bufferSize The buffer size.
+ * @param advertiseURI the URI to use when advertising the file
+ * @param layout The layout.
+ * @param configuration The configuration.
+ * @return A RandomAccessFileManager for the File.
+ */
+ public static RandomAccessFileManager getFileManager(final String fileName, final boolean append,
+ final boolean isFlush, final int bufferSize, final String advertiseURI,
+ final Layout<? extends Serializable> layout, final Configuration configuration) {
+ return (RandomAccessFileManager) getManager(fileName, new FactoryData(append,
+ isFlush, bufferSize, advertiseURI, layout, configuration), FACTORY);
+ }
+
+ public Boolean isEndOfBatch() {
+ return isEndOfBatch.get();
+ }
+
+ public void setEndOfBatch(final boolean endOfBatch) {
+ this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
+ }
+
+ @Override
+ protected void writeToDestination(final byte[] bytes, final int offset, final int length) {
+ try {
+ randomAccessFile.write(bytes, offset, length);
+ } catch (final IOException ex) {
+ final String msg = "Error writing to RandomAccessFile " + getName();
+ throw new AppenderLoggingException(msg, ex);
+ }
+ }
+
+ @Override
+ public synchronized void flush() {
+ flushBuffer(byteBuffer);
+ }
+
+ @Override
+ public synchronized boolean closeOutputStream() {
+ flush();
+ try {
+ randomAccessFile.close();
+ return true;
+ } catch (final IOException ex) {
+ logError("Unable to close RandomAccessFile", ex);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the name of the File being managed.
+ *
+ * @return The name of the File being managed.
+ */
+ public String getFileName() {
+ return getName();
+ }
+
+ /**
+ * Returns the buffer capacity.
+ * @return the buffer size
+ */
+ public int getBufferSize() {
+ return byteBuffer.capacity();
+ }
+
+ /**
+ * Gets this FileManager's content format specified by:
+ * <p>
+ * Key: "fileURI" Value: provided "advertiseURI" param.
+ * </p>
+ *
+ * @return Map of content format keys supporting FileManager
+ */
+ @Override
+ public Map<String, String> getContentFormat() {
+ final Map<String, String> result = new HashMap<>(
+ super.getContentFormat());
+ result.put("fileURI", advertiseURI);
+ return result;
+ }
+
+ /**
+ * Factory Data.
+ */
+ private static class FactoryData extends ConfigurationFactoryData {
+ private final boolean append;
+ private final boolean immediateFlush;
+ private final int bufferSize;
+ private final String advertiseURI;
+ private final Layout<? extends Serializable> layout;
+
+ /**
+ * Constructor.
+ *
+ * @param append Append status.
+ * @param bufferSize size of the buffer
+ * @param configuration The configuration.
+ */
+ public FactoryData(final boolean append, final boolean immediateFlush, final int bufferSize,
+ final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
+ super(configuration);
+ this.append = append;
+ this.immediateFlush = immediateFlush;
+ this.bufferSize = bufferSize;
+ this.advertiseURI = advertiseURI;
+ this.layout = layout;
+ }
+ }
+
+ /**
+ * Factory to create a RandomAccessFileManager.
+ */
+ private static class RandomAccessFileManagerFactory implements
+ ManagerFactory<RandomAccessFileManager, FactoryData> {
+
+ /**
+ * Create a RandomAccessFileManager.
+ *
+ * @param name The name of the File.
+ * @param data The FactoryData
+ * @return The RandomAccessFileManager for the File.
+ */
+ @Override
+ public RandomAccessFileManager createManager(final String name, final FactoryData data) {
+ final File file = new File(name);
+ final File parent = file.getParentFile();
+ if (null != parent && !parent.exists()) {
+ parent.mkdirs();
+ }
+ if (!data.append) {
+ file.delete();
+ }
+
+ final boolean writeHeader = !data.append || !file.exists();
+ final OutputStream os = NullOutputStream.NULL_OUTPUT_STREAM;
+ RandomAccessFile raf;
+ try {
+ raf = new RandomAccessFile(name, "rw");
+ if (data.append) {
+ raf.seek(raf.length());
+ } else {
+ raf.setLength(0);
+ }
+ return new RandomAccessFileManager(data.getLoggerContext(), raf, name,
+ os, data.bufferSize, data.advertiseURI, data.layout, writeHeader);
+ } catch (final Exception ex) {
+ LOGGER.error("RandomAccessFileManager (" + name + ") " + ex, ex);
+ }
+ return null;
+ }
+ }
+
+}