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/09/08 14:53:09 UTC
[09/35] logging-log4j2 git commit: Deprecate
org.apache.logging.log4j.core.util.NullOutputStream.NULL_OUTPUT_STREAM in
favor of org.apache.logging.log4j.core.util.NullOutputStream.getInstance().
Deprecate
org.apache.logging.log4j.core.util.NullOutputStream.NULL_OUTPUT_STREAM
in favor of
org.apache.logging.log4j.core.util.NullOutputStream.getInstance().
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/f73f89b1
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/f73f89b1
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/f73f89b1
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: f73f89b14731a0bf8e8d89053509056f1cc43075
Parents: d85a058
Author: Gary Gregory <gg...@apache.org>
Authored: Wed Sep 7 20:54:19 2016 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Wed Sep 7 20:54:19 2016 -0700
----------------------------------------------------------------------
.../core/appender/MemoryMappedFileManager.java | 718 +++++++++----------
.../core/appender/RandomAccessFileManager.java | 432 +++++------
.../rolling/RollingRandomAccessFileManager.java | 530 +++++++-------
.../log4j/core/net/TcpSocketManager.java | 654 ++++++++---------
.../log4j/core/util/NullOutputStream.java | 9 +-
.../appender/RandomAccessFileManagerTest.java | 8 +-
.../RollingRandomAccessFileManagerTest.java | 6 +-
7 files changed, 1182 insertions(+), 1175 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f73f89b1/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 332e8e4..13c5294 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,359 +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 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;
- }
- }
-}
+/*
+ * 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.getInstance();
+ 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/f73f89b1/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 4cd5459..45740f1 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,216 +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 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;
- }
- }
-
-}
+/*
+ * 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.getInstance();
+ 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;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/f73f89b1/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
----------------------------------------------------------------------
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 9be448b..6481972 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
@@ -1,265 +1,265 @@
-/*
- * 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.rolling;
-
-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 org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.appender.AppenderLoggingException;
-import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.util.NullOutputStream;
-
-/**
- * Extends RollingFileManager 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 RollingRandomAccessFileManager extends RollingFileManager {
- /**
- * The default buffer size.
- */
- public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
-
- private static final RollingRandomAccessFileManagerFactory FACTORY = new RollingRandomAccessFileManagerFactory();
-
- private RandomAccessFile randomAccessFile;
- private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
-
- public RollingRandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile raf,
- final String fileName, final String pattern, final OutputStream os, final boolean append,
- final boolean immediateFlush, final int bufferSize, final long size, final long time,
- final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
- final Layout<? extends Serializable> layout, final boolean writeHeader) {
- super(loggerContext, fileName, pattern, os, append, false, size, time, policy, strategy, advertiseURI, layout,
- writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
- this.randomAccessFile = raf;
- isEndOfBatch.set(Boolean.FALSE);
- writeHeader();
- }
-
- /**
- * Writes the layout's header to the file if it exists.
- */
- private void writeHeader() {
- if (layout == null) {
- return;
- }
- final byte[] header = layout.getHeader();
- if (header == null) {
- return;
- }
- try {
- if (randomAccessFile.length() == 0) {
- // write to the file, not to the buffer: the buffer may not be empty
- randomAccessFile.write(header, 0, header.length);
- }
- } catch (final IOException e) {
- logError("Unable to write header", e);
- }
- }
-
- public static RollingRandomAccessFileManager getRollingRandomAccessFileManager(final String fileName,
- final String filePattern, final boolean isAppend, final boolean immediateFlush, final int bufferSize,
- final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
- final Layout<? extends Serializable> layout, final Configuration configuration) {
- return (RollingRandomAccessFileManager) getManager(fileName, new FactoryData(filePattern, isAppend,
- immediateFlush, bufferSize, policy, strategy, advertiseURI, layout, configuration), FACTORY);
- }
-
- public Boolean isEndOfBatch() {
- return isEndOfBatch.get();
- }
-
- public void setEndOfBatch(final boolean endOfBatch) {
- this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
- }
-
- // override to make visible for unit tests
- @Override
- protected synchronized void write(final byte[] bytes, final int offset, final int length,
- final boolean immediateFlush) {
- super.write(bytes, offset, length, immediateFlush);
- }
-
- @Override
- protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
- try {
- randomAccessFile.write(bytes, offset, length);
- size += length;
- } catch (final IOException ex) {
- final String msg = "Error writing to RandomAccessFile " + getName();
- throw new AppenderLoggingException(msg, ex);
- }
- }
-
- @Override
- protected void createFileAfterRollover() throws IOException {
- this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
- if (isAppend()) {
- randomAccessFile.seek(randomAccessFile.length());
- }
- writeHeader();
- }
-
- @Override
- public synchronized void flush() {
- flushBuffer(byteBuffer);
- }
-
- @Override
- public synchronized boolean closeOutputStream() {
- flush();
- try {
- randomAccessFile.close();
- return true;
- } catch (final IOException e) {
- logError("Unable to close RandomAccessFile", e);
- return false;
- }
- }
-
- /**
- * Returns the buffer capacity.
- *
- * @return the buffer size
- */
- @Override
- public int getBufferSize() {
- return byteBuffer.capacity();
- }
-
- /**
- * Factory to create a RollingRandomAccessFileManager.
- */
- private static class RollingRandomAccessFileManagerFactory implements
- ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
-
- /**
- * Create the RollingRandomAccessFileManager.
- *
- * @param name The name of the entity to manage.
- * @param data The data required to create the entity.
- * @return a RollingFileManager.
- */
- @Override
- public RollingRandomAccessFileManager 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 long size = data.append ? file.length() : 0;
- final long time = file.exists() ? file.lastModified() : System.currentTimeMillis();
-
- final boolean writeHeader = !data.append || !file.exists();
- RandomAccessFile raf = null;
- try {
- raf = new RandomAccessFile(name, "rw");
- if (data.append) {
- final long length = raf.length();
- LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
- raf.seek(length);
- } else {
- LOGGER.trace("RandomAccessFile {} set length to 0", name);
- raf.setLength(0);
- }
- return new RollingRandomAccessFileManager(data.getLoggerContext(), raf, name, data.pattern,
- NullOutputStream.NULL_OUTPUT_STREAM, data.append, data.immediateFlush, data.bufferSize, size, time, data.policy,
- data.strategy, data.advertiseURI, data.layout, writeHeader);
- } catch (final IOException ex) {
- LOGGER.error("Cannot access RandomAccessFile " + ex, ex);
- if (raf != null) {
- try {
- raf.close();
- } catch (final IOException e) {
- LOGGER.error("Cannot close RandomAccessFile {}", name, e);
- }
- }
- }
- return null;
- }
- }
-
- /**
- * Factory data.
- */
- private static class FactoryData extends ConfigurationFactoryData {
- private final String pattern;
- private final boolean append;
- private final boolean immediateFlush;
- private final int bufferSize;
- private final TriggeringPolicy policy;
- private final RolloverStrategy strategy;
- private final String advertiseURI;
- private final Layout<? extends Serializable> layout;
-
- /**
- * Create the data for the factory.
- *
- * @param pattern The pattern.
- * @param append The append flag.
- * @param immediateFlush
- * @param bufferSize
- * @param policy
- * @param strategy
- * @param advertiseURI
- * @param layout
- * @param configuration
- */
- public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
- final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
- final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
- super(configuration);
- this.pattern = pattern;
- this.append = append;
- this.immediateFlush = immediateFlush;
- this.bufferSize = bufferSize;
- this.policy = policy;
- this.strategy = strategy;
- this.advertiseURI = advertiseURI;
- this.layout = layout;
- }
-
- public TriggeringPolicy getTriggeringPolicy()
- {
- return this.policy;
- }
-
- public RolloverStrategy getRolloverStrategy()
- {
- return this.strategy;
- }
- }
-
- @Override
- public void updateData(final Object data) {
- final FactoryData factoryData = (FactoryData) data;
- setRolloverStrategy(factoryData.getRolloverStrategy());
- setTriggeringPolicy(factoryData.getTriggeringPolicy());
- }
-}
+/*
+ * 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.rolling;
+
+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 org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.util.NullOutputStream;
+
+/**
+ * Extends RollingFileManager 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 RollingRandomAccessFileManager extends RollingFileManager {
+ /**
+ * The default buffer size.
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+
+ private static final RollingRandomAccessFileManagerFactory FACTORY = new RollingRandomAccessFileManagerFactory();
+
+ private RandomAccessFile randomAccessFile;
+ private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
+
+ public RollingRandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile raf,
+ final String fileName, final String pattern, final OutputStream os, final boolean append,
+ final boolean immediateFlush, final int bufferSize, final long size, final long time,
+ final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
+ final Layout<? extends Serializable> layout, final boolean writeHeader) {
+ super(loggerContext, fileName, pattern, os, append, false, size, time, policy, strategy, advertiseURI, layout,
+ writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
+ this.randomAccessFile = raf;
+ isEndOfBatch.set(Boolean.FALSE);
+ writeHeader();
+ }
+
+ /**
+ * Writes the layout's header to the file if it exists.
+ */
+ private void writeHeader() {
+ if (layout == null) {
+ return;
+ }
+ final byte[] header = layout.getHeader();
+ if (header == null) {
+ return;
+ }
+ try {
+ if (randomAccessFile.length() == 0) {
+ // write to the file, not to the buffer: the buffer may not be empty
+ randomAccessFile.write(header, 0, header.length);
+ }
+ } catch (final IOException e) {
+ logError("Unable to write header", e);
+ }
+ }
+
+ public static RollingRandomAccessFileManager getRollingRandomAccessFileManager(final String fileName,
+ final String filePattern, final boolean isAppend, final boolean immediateFlush, final int bufferSize,
+ final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
+ final Layout<? extends Serializable> layout, final Configuration configuration) {
+ return (RollingRandomAccessFileManager) getManager(fileName, new FactoryData(filePattern, isAppend,
+ immediateFlush, bufferSize, policy, strategy, advertiseURI, layout, configuration), FACTORY);
+ }
+
+ public Boolean isEndOfBatch() {
+ return isEndOfBatch.get();
+ }
+
+ public void setEndOfBatch(final boolean endOfBatch) {
+ this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
+ }
+
+ // override to make visible for unit tests
+ @Override
+ protected synchronized void write(final byte[] bytes, final int offset, final int length,
+ final boolean immediateFlush) {
+ super.write(bytes, offset, length, immediateFlush);
+ }
+
+ @Override
+ protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
+ try {
+ randomAccessFile.write(bytes, offset, length);
+ size += length;
+ } catch (final IOException ex) {
+ final String msg = "Error writing to RandomAccessFile " + getName();
+ throw new AppenderLoggingException(msg, ex);
+ }
+ }
+
+ @Override
+ protected void createFileAfterRollover() throws IOException {
+ this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
+ if (isAppend()) {
+ randomAccessFile.seek(randomAccessFile.length());
+ }
+ writeHeader();
+ }
+
+ @Override
+ public synchronized void flush() {
+ flushBuffer(byteBuffer);
+ }
+
+ @Override
+ public synchronized boolean closeOutputStream() {
+ flush();
+ try {
+ randomAccessFile.close();
+ return true;
+ } catch (final IOException e) {
+ logError("Unable to close RandomAccessFile", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the buffer capacity.
+ *
+ * @return the buffer size
+ */
+ @Override
+ public int getBufferSize() {
+ return byteBuffer.capacity();
+ }
+
+ /**
+ * Factory to create a RollingRandomAccessFileManager.
+ */
+ private static class RollingRandomAccessFileManagerFactory implements
+ ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
+
+ /**
+ * Create the RollingRandomAccessFileManager.
+ *
+ * @param name The name of the entity to manage.
+ * @param data The data required to create the entity.
+ * @return a RollingFileManager.
+ */
+ @Override
+ public RollingRandomAccessFileManager 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 long size = data.append ? file.length() : 0;
+ final long time = file.exists() ? file.lastModified() : System.currentTimeMillis();
+
+ final boolean writeHeader = !data.append || !file.exists();
+ RandomAccessFile raf = null;
+ try {
+ raf = new RandomAccessFile(name, "rw");
+ if (data.append) {
+ final long length = raf.length();
+ LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
+ raf.seek(length);
+ } else {
+ LOGGER.trace("RandomAccessFile {} set length to 0", name);
+ raf.setLength(0);
+ }
+ return new RollingRandomAccessFileManager(data.getLoggerContext(), raf, name, data.pattern,
+ NullOutputStream.getInstance(), data.append, data.immediateFlush, data.bufferSize, size, time, data.policy,
+ data.strategy, data.advertiseURI, data.layout, writeHeader);
+ } catch (final IOException ex) {
+ LOGGER.error("Cannot access RandomAccessFile " + ex, ex);
+ if (raf != null) {
+ try {
+ raf.close();
+ } catch (final IOException e) {
+ LOGGER.error("Cannot close RandomAccessFile {}", name, e);
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Factory data.
+ */
+ private static class FactoryData extends ConfigurationFactoryData {
+ private final String pattern;
+ private final boolean append;
+ private final boolean immediateFlush;
+ private final int bufferSize;
+ private final TriggeringPolicy policy;
+ private final RolloverStrategy strategy;
+ private final String advertiseURI;
+ private final Layout<? extends Serializable> layout;
+
+ /**
+ * Create the data for the factory.
+ *
+ * @param pattern The pattern.
+ * @param append The append flag.
+ * @param immediateFlush
+ * @param bufferSize
+ * @param policy
+ * @param strategy
+ * @param advertiseURI
+ * @param layout
+ * @param configuration
+ */
+ public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
+ final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
+ final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
+ super(configuration);
+ this.pattern = pattern;
+ this.append = append;
+ this.immediateFlush = immediateFlush;
+ this.bufferSize = bufferSize;
+ this.policy = policy;
+ this.strategy = strategy;
+ this.advertiseURI = advertiseURI;
+ this.layout = layout;
+ }
+
+ public TriggeringPolicy getTriggeringPolicy()
+ {
+ return this.policy;
+ }
+
+ public RolloverStrategy getRolloverStrategy()
+ {
+ return this.strategy;
+ }
+ }
+
+ @Override
+ public void updateData(final Object data) {
+ final FactoryData factoryData = (FactoryData) data;
+ setRolloverStrategy(factoryData.getRolloverStrategy());
+ setTriggeringPolicy(factoryData.getTriggeringPolicy());
+ }
+}