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 2014/09/16 17:18:05 UTC
[1/4] git commit: Initial version
Repository: logging-log4j2
Updated Branches:
refs/heads/LOG4J2-431 [created] 9063e0c57
Initial version
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/fc3d480d
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/fc3d480d
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/fc3d480d
Branch: refs/heads/LOG4J2-431
Commit: fc3d480db11df40c13b66c003ec545d3966ee86c
Parents: 82d43d0
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 14 20:45:58 2014 +0900
Committer: rpopma <rp...@apache.org>
Committed: Mon Sep 15 20:00:42 2014 +0900
----------------------------------------------------------------------
.../core/appender/MemoryMappedFileAppender.java | 177 ++++++++++++
.../core/appender/MemoryMappedFileManager.java | 283 +++++++++++++++++++
2 files changed, 460 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fc3d480d/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
new file mode 100644
index 0000000..f0910b5
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
@@ -0,0 +1,177 @@
+/*
+ * 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.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.Advertiser;
+import org.apache.logging.log4j.core.util.Booleans;
+import org.apache.logging.log4j.core.util.Integers;
+
+/**
+ * File Appender.
+ */
+@Plugin(name = "MemoryMappedFile", category = "Core", elementType = "appender", printObject = true)
+public final class MemoryMappedFileAppender extends AbstractOutputStreamAppender<RandomAccessFileManager> {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String fileName;
+ private Object advertisement;
+ private final Advertiser advertiser;
+
+ private MemoryMappedFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
+ final RandomAccessFileManager manager, final String filename, final boolean ignoreExceptions,
+ final boolean immediateFlush, final Advertiser advertiser) {
+ super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
+ if (advertiser != null) {
+ final Map<String, String> configuration = new HashMap<String, String>(
+ layout.getContentFormat());
+ configuration.putAll(manager.getContentFormat());
+ configuration.put("contentType", layout.getContentType());
+ configuration.put("name", name);
+ advertisement = advertiser.advertise(configuration);
+ }
+ this.fileName = filename;
+ this.advertiser = advertiser;
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ if (advertiser != null) {
+ advertiser.unadvertise(advertisement);
+ }
+ }
+
+ /**
+ * Write the log entry rolling over the file when required.
+ *
+ * @param event The LogEvent.
+ */
+ @Override
+ public void append(final LogEvent event) {
+
+ // Leverage the nice batching behaviour of async Loggers/Appenders:
+ // we can signal the file manager that it needs to flush the buffer
+ // to disk at the end of a batch.
+ // From a user's point of view, this means that all log events are
+ // _always_ available in the log file, without incurring the overhead
+ // of immediateFlush=true.
+ getManager().setEndOfBatch(event.isEndOfBatch());
+ super.append(event);
+ }
+
+ /**
+ * Returns the file name this appender is associated with.
+ *
+ * @return The File name.
+ */
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ /**
+ * Returns the size of the file manager's buffer.
+ * @return the buffer size
+ */
+ public int getBufferSize() {
+ return getManager().getBufferSize();
+ }
+
+ // difference from standard File Appender:
+ // locking is not supported and buffering cannot be switched off
+ /**
+ * Create a File Appender.
+ *
+ * @param fileName The name and path of the file.
+ * @param append "True" if the file should be appended to, "false" if it
+ * should be overwritten. The default is "true".
+ * @param name The name of the Appender.
+ * @param immediateFlush "true" if the contents should be flushed on every
+ * write, "false" otherwise. The default is "true".
+ * @param bufferSizeStr The buffer size, defaults to {@value RandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
+ * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
+ * they are propagated to the caller.
+ * @param layout The layout to use to format the event. If no layout is
+ * provided the default PatternLayout will be used.
+ * @param filter The filter, if any, to use.
+ * @param advertise "true" if the appender configuration should be
+ * advertised, "false" otherwise.
+ * @param advertiseURI The advertised URI which can be used to retrieve the
+ * file contents.
+ * @param config The Configuration.
+ * @return The FileAppender.
+ */
+ @PluginFactory
+ public static MemoryMappedFileAppender createAppender(
+ @PluginAttribute("fileName") final String fileName,
+ @PluginAttribute("append") final String append,
+ @PluginAttribute("name") final String name,
+ @PluginAttribute("immediateFlush") final String immediateFlush,
+ @PluginAttribute("bufferSize") final String bufferSizeStr,
+ @PluginAttribute("ignoreExceptions") final String ignore,
+ @PluginElement("Layout") Layout<? extends Serializable> layout,
+ @PluginElement("Filter") final Filter filter,
+ @PluginAttribute("advertise") final String advertise,
+ @PluginAttribute("advertiseURI") final String advertiseURI,
+ @PluginConfiguration final Configuration config) {
+
+ final boolean isAppend = Booleans.parseBoolean(append, true);
+ final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final boolean isAdvertise = Boolean.parseBoolean(advertise);
+ final int bufferSize = Integers.parseInt(bufferSizeStr, RandomAccessFileManager.DEFAULT_BUFFER_SIZE);
+
+ if (name == null) {
+ LOGGER.error("No name provided for FileAppender");
+ return null;
+ }
+
+ if (fileName == null) {
+ LOGGER.error("No filename provided for FileAppender with name "
+ + name);
+ return null;
+ }
+ if (layout == null) {
+ layout = PatternLayout.createDefaultLayout();
+ }
+ final RandomAccessFileManager manager = RandomAccessFileManager.getFileManager(
+ fileName, isAppend, isFlush, bufferSize, advertiseURI, layout
+ );
+ if (manager == null) {
+ return null;
+ }
+
+ return new MemoryMappedFileAppender(
+ name, layout, filter, manager, fileName, ignoreExceptions, isFlush,
+ isAdvertise ? config.getAdvertiser() : null
+ );
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fc3d480d/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
new file mode 100644
index 0000000..2a66d03
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileManager.java
@@ -0,0 +1,283 @@
+/*
+ * 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.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 org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.util.Closer;
+
+/**
+ * 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://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>
+ * @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>
+ *
+ */
+public class MemoryMappedFileManager extends OutputStreamManager {
+ private static final MemoryMappedFileManagerFactory FACTORY = new MemoryMappedFileManagerFactory();
+
+ private final boolean isForce;
+ private final int regionLength;
+ private final String advertiseURI;
+ private final RandomAccessFile randomAccessFile;
+ private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
+ private MappedByteBuffer mappedBuffer;
+
+ 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) throws IOException {
+ super(os, fileName, layout);
+ this.isForce = force;
+ this.randomAccessFile = file;
+ this.regionLength = regionLength;
+ this.advertiseURI = advertiseURI;
+ this.isEndOfBatch.set(Boolean.FALSE);
+ this.mappedBuffer = mmap(randomAccessFile.getChannel(), position, regionLength);
+ }
+
+ /**
+ * 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 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.
+ * @return A MemoryMappedFileManager for the File.
+ */
+ public static MemoryMappedFileManager getFileManager(final String fileName, final boolean append,
+ final boolean isFlush, final int bufferSize, final String advertiseURI,
+ final Layout<? extends Serializable> layout) {
+ return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isFlush, bufferSize,
+ advertiseURI, layout), FACTORY);
+ }
+
+ public Boolean isEndOfBatch() {
+ return isEndOfBatch.get();
+ }
+
+ public void setEndOfBatch(final boolean isEndOfBatch) {
+ this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
+ }
+
+ @Override
+ protected synchronized void write(final byte[] bytes, int offset, int length) {
+ super.write(bytes, offset, length); // writes to dummy output stream
+
+ if (length > mappedBuffer.remaining()) {
+ remap();
+ }
+ mappedBuffer.put(bytes, offset, length);
+
+ if (isForce) {
+ flush();
+ }
+ }
+
+ private void remap() {
+ final long position = mappedBuffer.position();
+ try {
+ unsafeUnmap(mappedBuffer);
+ final long newLength = randomAccessFile.length() + regionLength;
+ randomAccessFile.setLength(newLength);
+ mappedBuffer = mmap(randomAccessFile.getChannel(), position, regionLength);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to remap " + getName() + ". " + ex);
+ }
+ }
+
+ @Override
+ public synchronized void flush() {
+ mappedBuffer.force();
+ }
+
+ @Override
+ public synchronized void close() {
+ try {
+ randomAccessFile.close();
+ } catch (final IOException ex) {
+ LOGGER.error("Unable to close MemoryMappedFile " + getName() + ". " + ex);
+ }
+ }
+
+ public static MappedByteBuffer mmap(FileChannel fileChannel, long start, int size) throws IOException {
+ for (int i = 1;; i++) {
+ try {
+ MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size);
+ map.order(ByteOrder.nativeOrder());
+ return map;
+ } catch (IOException e) {
+ if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) {
+ throw e;
+ }
+ if (i < 10) {
+ Thread.yield();
+ } else {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ throw e;
+ }
+ }
+ }
+ }
+ }
+
+ private static void unsafeUnmap(final MappedByteBuffer mbb) throws PrivilegedActionException {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ 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;
+ }
+ });
+ }
+
+ /**
+ * 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;
+ }
+
+ /** {@code OutputStream} subclass that does not write anything. */
+ static class DummyOutputStream extends OutputStream {
+ @Override
+ public void write(final int b) throws IOException {
+ }
+
+ @Override
+ public void write(final byte[] b, final int off, final int len) throws IOException {
+ }
+ }
+
+ /**
+ * FileManager's content format is specified by:
+ * <p/>
+ * Key: "fileURI" Value: provided "advertiseURI" param.
+ *
+ * @return Map of content format keys supporting FileManager
+ */
+ @Override
+ public Map<String, String> getContentFormat() {
+ final Map<String, String> result = new HashMap<String, String>(super.getContentFormat());
+ result.put("fileURI", advertiseURI);
+ return result;
+ }
+
+ /**
+ * 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
+ */
+ 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 OutputStream os = new DummyOutputStream();
+ 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);
+ } catch (final Exception ex) {
+ LOGGER.error("MemoryMappedFileManager (" + name + ") " + ex);
+ Closer.closeSilently(raf);
+ }
+ return null;
+ }
+ }
+}
[3/4] git commit: various improvements and fixes in appender and
manager
Posted by rp...@apache.org.
various improvements and fixes in appender and manager
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e751c22a
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e751c22a
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e751c22a
Branch: refs/heads/LOG4J2-431
Commit: e751c22af16e7b0c4a96932a9ede8ddf51426eb1
Parents: ebbe63a
Author: rpopma <rp...@apache.org>
Authored: Wed Sep 17 00:15:44 2014 +0900
Committer: rpopma <rp...@apache.org>
Committed: Wed Sep 17 00:15:44 2014 +0900
----------------------------------------------------------------------
.../core/appender/MemoryMappedFileAppender.java | 99 ++++++++++----------
.../core/appender/MemoryMappedFileManager.java | 41 +++++---
2 files changed, 78 insertions(+), 62 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e751c22a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
index f0910b5..785c3da 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppender.java
@@ -35,10 +35,10 @@ import org.apache.logging.log4j.core.util.Booleans;
import org.apache.logging.log4j.core.util.Integers;
/**
- * File Appender.
+ * Memory Mapped File Appender.
*/
@Plugin(name = "MemoryMappedFile", category = "Core", elementType = "appender", printObject = true)
-public final class MemoryMappedFileAppender extends AbstractOutputStreamAppender<RandomAccessFileManager> {
+public final class MemoryMappedFileAppender extends AbstractOutputStreamAppender<MemoryMappedFileManager> {
private static final long serialVersionUID = 1L;
@@ -46,13 +46,12 @@ public final class MemoryMappedFileAppender extends AbstractOutputStreamAppender
private Object advertisement;
private final Advertiser advertiser;
- private MemoryMappedFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
- final RandomAccessFileManager manager, final String filename, final boolean ignoreExceptions,
- final boolean immediateFlush, final Advertiser advertiser) {
+ private MemoryMappedFileAppender(final String name, final Layout<? extends Serializable> layout,
+ final Filter filter, final MemoryMappedFileManager manager, final String filename,
+ final boolean ignoreExceptions, final boolean immediateFlush, final Advertiser advertiser) {
super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
if (advertiser != null) {
- final Map<String, String> configuration = new HashMap<String, String>(
- layout.getContentFormat());
+ final Map<String, String> configuration = new HashMap<String, String>(layout.getContentFormat());
configuration.putAll(manager.getContentFormat());
configuration.put("contentType", layout.getContentType());
configuration.put("name", name);
@@ -96,82 +95,82 @@ public final class MemoryMappedFileAppender extends AbstractOutputStreamAppender
public String getFileName() {
return this.fileName;
}
-
+
/**
- * Returns the size of the file manager's buffer.
- * @return the buffer size
+ * Returns the length of the memory mapped region.
+ *
+ * @return the length of the memory mapped region
*/
- public int getBufferSize() {
- return getManager().getBufferSize();
+ public int getRegionLength() {
+ return getManager().getRegionLength();
}
- // difference from standard File Appender:
- // locking is not supported and buffering cannot be switched off
/**
- * Create a File Appender.
+ * Create a Memory Mapped File Appender.
*
* @param fileName The name and path of the file.
- * @param append "True" if the file should be appended to, "false" if it
- * should be overwritten. The default is "true".
+ * @param append "True" if the file should be appended to, "false" if it should be overwritten. The default is
+ * "true".
* @param name The name of the Appender.
- * @param immediateFlush "true" if the contents should be flushed on every
- * write, "false" otherwise. The default is "true".
- * @param bufferSizeStr The buffer size, defaults to {@value RandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
- * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
- * they are propagated to the caller.
- * @param layout The layout to use to format the event. If no layout is
- * provided the default PatternLayout will be used.
+ * @param immediateFlush "true" if the contents should be flushed on every write, "false" otherwise. The default is
+ * "true".
+ * @param regionLengthStr The buffer size, defaults to {@value RandomAccessFileManager#DEFAULT_REGION=LENGTH}.
+ * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they
+ * are propagated to the caller.
+ * @param layout The layout to use to format the event. If no layout is provided the default PatternLayout will be
+ * used.
* @param filter The filter, if any, to use.
- * @param advertise "true" if the appender configuration should be
- * advertised, "false" otherwise.
- * @param advertiseURI The advertised URI which can be used to retrieve the
- * file contents.
+ * @param advertise "true" if the appender configuration should be advertised, "false" otherwise.
+ * @param advertiseURI The advertised URI which can be used to retrieve the file contents.
* @param config The Configuration.
* @return The FileAppender.
*/
@PluginFactory
public static MemoryMappedFileAppender createAppender(
- @PluginAttribute("fileName") final String fileName,
- @PluginAttribute("append") final String append,
- @PluginAttribute("name") final String name,
- @PluginAttribute("immediateFlush") final String immediateFlush,
- @PluginAttribute("bufferSize") final String bufferSizeStr,
- @PluginAttribute("ignoreExceptions") final String ignore,
- @PluginElement("Layout") Layout<? extends Serializable> layout,
- @PluginElement("Filter") final Filter filter,
- @PluginAttribute("advertise") final String advertise,
- @PluginAttribute("advertiseURI") final String advertiseURI,
+ // @formatter:off
+ @PluginAttribute("fileName") final String fileName, //
+ @PluginAttribute("append") final String append, //
+ @PluginAttribute("name") final String name, //
+ @PluginAttribute("immediateFlush") final String immediateFlush, //
+ @PluginAttribute("regionLength") final String regionLengthStr, //
+ @PluginAttribute("ignoreExceptions") final String ignore, //
+ @PluginElement("Layout") Layout<? extends Serializable> layout, //
+ @PluginElement("Filter") final Filter filter, //
+ @PluginAttribute("advertise") final String advertise, //
+ @PluginAttribute("advertiseURI") final String advertiseURI, //
@PluginConfiguration final Configuration config) {
+ // @formatter:on
final boolean isAppend = Booleans.parseBoolean(append, true);
- final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
+ final boolean isForce = Booleans.parseBoolean(immediateFlush, true);
final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
final boolean isAdvertise = Boolean.parseBoolean(advertise);
- final int bufferSize = Integers.parseInt(bufferSizeStr, RandomAccessFileManager.DEFAULT_BUFFER_SIZE);
+ final int regionLength = Integers.parseInt(regionLengthStr, MemoryMappedFileManager.DEFAULT_REGION_LENGTH);
+ final int actualRegionLength = Integers.ceilingNextPowerOfTwo(regionLength);
+ if (regionLength != actualRegionLength) {
+ LOGGER.info("MemoryMappedAppender[{}] Rounded up region length from {} to next power of two: {}", name,
+ regionLength, actualRegionLength);
+ }
if (name == null) {
- LOGGER.error("No name provided for FileAppender");
+ LOGGER.error("No name provided for MemoryMappedFileAppender");
return null;
}
if (fileName == null) {
- LOGGER.error("No filename provided for FileAppender with name "
- + name);
+ LOGGER.error("No filename provided for MemoryMappedFileAppender with name " + name);
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
- final RandomAccessFileManager manager = RandomAccessFileManager.getFileManager(
- fileName, isAppend, isFlush, bufferSize, advertiseURI, layout
- );
+ final MemoryMappedFileManager manager = MemoryMappedFileManager.getFileManager(fileName, isAppend, isForce,
+ actualRegionLength, advertiseURI, layout);
if (manager == null) {
return null;
}
- return new MemoryMappedFileAppender(
- name, layout, filter, manager, fileName, ignoreExceptions, isFlush,
- isAdvertise ? config.getAdvertiser() : null
- );
+ return new MemoryMappedFileAppender(name, layout, filter, manager, fileName, ignoreExceptions, isForce,
+ isAdvertise ? config.getAdvertiser() : null);
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e751c22a/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 2a66d03..29f8c54 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
@@ -32,6 +32,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.util.Assert;
import org.apache.logging.log4j.core.util.Closer;
/**
@@ -39,14 +40,15 @@ import org.apache.logging.log4j.core.util.Closer;
* 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>
- * @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>
*
*/
public class MemoryMappedFileManager extends OutputStreamManager {
+ static final int DEFAULT_REGION_LENGTH = 32 * 1024 * 1024;
private static final MemoryMappedFileManagerFactory FACTORY = new MemoryMappedFileManagerFactory();
private final boolean isForce;
@@ -55,17 +57,19 @@ public class MemoryMappedFileManager extends OutputStreamManager {
private final RandomAccessFile randomAccessFile;
private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
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) throws IOException {
super(os, fileName, layout);
this.isForce = force;
- this.randomAccessFile = file;
+ this.randomAccessFile = Assert.requireNonNull(file, "RandomAccessFile");
this.regionLength = regionLength;
this.advertiseURI = advertiseURI;
this.isEndOfBatch.set(Boolean.FALSE);
this.mappedBuffer = mmap(randomAccessFile.getChannel(), position, regionLength);
+ this.mappingOffset = position;
}
/**
@@ -73,16 +77,16 @@ public class MemoryMappedFileManager extends OutputStreamManager {
*
* @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 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 isFlush, final int bufferSize, final String advertiseURI,
+ final boolean isForce, final int regionLength, final String advertiseURI,
final Layout<? extends Serializable> layout) {
- return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isFlush, bufferSize,
+ return (MemoryMappedFileManager) getManager(fileName, new FactoryData(append, isForce, regionLength,
advertiseURI, layout), FACTORY);
}
@@ -98,7 +102,11 @@ public class MemoryMappedFileManager extends OutputStreamManager {
protected synchronized void write(final byte[] bytes, int offset, int length) {
super.write(bytes, offset, length); // writes to dummy output stream
- if (length > mappedBuffer.remaining()) {
+ while (length > mappedBuffer.remaining()) {
+ final int chunk = mappedBuffer.remaining();
+ mappedBuffer.put(bytes, offset, chunk);
+ offset += chunk;
+ length -= chunk;
remap();
}
mappedBuffer.put(bytes, offset, length);
@@ -108,13 +116,15 @@ public class MemoryMappedFileManager extends OutputStreamManager {
}
}
- private void remap() {
- final long position = mappedBuffer.position();
+ private synchronized void remap() {
+ final long offset = this.mappingOffset + mappedBuffer.position();
+ final int length = mappedBuffer.remaining() + regionLength;
try {
unsafeUnmap(mappedBuffer);
- final long newLength = randomAccessFile.length() + regionLength;
- randomAccessFile.setLength(newLength);
- mappedBuffer = mmap(randomAccessFile.getChannel(), position, regionLength);
+ final long fileLength = randomAccessFile.length() + regionLength;
+ randomAccessFile.setLength(fileLength);
+ mappedBuffer = mmap(randomAccessFile.getChannel(), offset, length);
+ mappingOffset = offset;
} catch (final Exception ex) {
LOGGER.error("Unable to remap " + getName() + ". " + ex);
}
@@ -127,7 +137,14 @@ public class MemoryMappedFileManager extends OutputStreamManager {
@Override
public synchronized void close() {
+ final long length = mappingOffset + mappedBuffer.position();
+ try {
+ unsafeUnmap(mappedBuffer);
+ } catch (Exception ex) {
+ LOGGER.error("Unable to unmap MappedBuffer " + getName() + ". " + ex);
+ }
try {
+ randomAccessFile.setLength(length);
randomAccessFile.close();
} catch (final IOException ex) {
LOGGER.error("Unable to close MemoryMappedFile " + getName() + ". " + ex);
[2/4] git commit: Add method Integers.ceilingNextPowerOfTwo and use
it instead of the Disruptor's Util class.
Posted by rp...@apache.org.
Add method Integers.ceilingNextPowerOfTwo and use it instead of the
Disruptor's Util class.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/ebbe63ad
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/ebbe63ad
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/ebbe63ad
Branch: refs/heads/LOG4J2-431
Commit: ebbe63ad8d0c44ed1b126296195c65d171c1314b
Parents: fc3d480
Author: rpopma <rp...@apache.org>
Authored: Tue Sep 16 22:54:07 2014 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Sep 16 22:54:07 2014 +0900
----------------------------------------------------------------------
.../logging/log4j/core/async/AsyncLogger.java | 4 +-
.../core/async/AsyncLoggerConfigHelper.java | 4 +-
.../logging/log4j/core/util/Integers.java | 11 ++++++
.../logging/log4j/core/util/IntegersTest.java | 41 ++++++++++++++++++++
4 files changed, 56 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ebbe63ad/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
index 2cabc4f..8d8d656 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
@@ -30,6 +30,7 @@ import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
import org.apache.logging.log4j.core.util.Clock;
import org.apache.logging.log4j.core.util.ClockFactory;
+import org.apache.logging.log4j.core.util.Integers;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
@@ -44,7 +45,6 @@ import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
-import com.lmax.disruptor.util.Util;
/**
* AsyncLogger is a logger designed for high throughput and low latency logging.
@@ -146,7 +146,7 @@ public class AsyncLogger extends Logger {
} catch (final Exception ex) {
LOGGER.warn("Invalid RingBufferSize {}, using default size {}.", userPreferredRBSize, ringBufferSize);
}
- return Util.ceilingNextPowerOfTwo(ringBufferSize);
+ return Integers.ceilingNextPowerOfTwo(ringBufferSize);
}
/**
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ebbe63ad/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
index ead8036..b3b8a93 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ThreadFactory;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
+import org.apache.logging.log4j.core.util.Integers;
import org.apache.logging.log4j.status.StatusLogger;
import com.lmax.disruptor.BlockingWaitStrategy;
@@ -38,7 +39,6 @@ import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
-import com.lmax.disruptor.util.Util;
/**
* Helper class decoupling the {@code AsyncLoggerConfig} class from the LMAX
@@ -160,7 +160,7 @@ class AsyncLoggerConfigHelper {
LOGGER.warn("Invalid RingBufferSize {}, using default size {}.",
userPreferredRBSize, ringBufferSize);
}
- return Util.ceilingNextPowerOfTwo(ringBufferSize);
+ return Integers.ceilingNextPowerOfTwo(ringBufferSize);
}
private static ExceptionHandler getExceptionHandler() {
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ebbe63ad/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Integers.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Integers.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Integers.java
index 296011f..7b09e5a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Integers.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Integers.java
@@ -53,4 +53,15 @@ public final class Integers {
public static int parseInt(final String s) {
return parseInt(s, 0);
}
+
+ /**
+ * Calculate the next power of 2, greater than or equal to x.<p>
+ * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
+ *
+ * @param x Value to round up
+ * @return The next power of 2 from x inclusive
+ */
+ public static int ceilingNextPowerOfTwo(final int x) {
+ return 1 << (32 - Integer.numberOfLeadingZeros(x - 1));
+ }
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ebbe63ad/log4j-core/src/test/java/org/apache/logging/log4j/core/util/IntegersTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/IntegersTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/IntegersTest.java
new file mode 100644
index 0000000..a0c8d1c
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/IntegersTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the Integers class.
+ */
+public class IntegersTest {
+
+ @Test
+ public void testCeilingNextPowerOfTwoReturnsNextPowerOfTwo() {
+ int powerOfTwo = Integers.ceilingNextPowerOfTwo(1000);
+ assertEquals(1024, powerOfTwo);
+ }
+
+ @Test
+ public void testCeilingNextPowerOfTwoReturnsExactPowerOfTwo() {
+ int powerOfTwo = Integers.ceilingNextPowerOfTwo(1024);
+ assertEquals(1024, powerOfTwo);
+ }
+
+}
[4/4] git commit: Initial version of JUnit tests for
MemoryMappedAppender.
Posted by rp...@apache.org.
Initial version of JUnit tests for MemoryMappedAppender.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/9063e0c5
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/9063e0c5
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/9063e0c5
Branch: refs/heads/LOG4J2-431
Commit: 9063e0c573091018cb8b62b63bc1698e26a43ce6
Parents: e751c22
Author: rpopma <rp...@apache.org>
Authored: Wed Sep 17 00:16:21 2014 +0900
Committer: rpopma <rp...@apache.org>
Committed: Wed Sep 17 00:16:21 2014 +0900
----------------------------------------------------------------------
.../MemoryMappedFileAppenderLocationTest.java | 95 ++++++++++++++++++++
.../MemoryMappedFileAppenderRemapTest.java | 95 ++++++++++++++++++++
.../MemoryMappedFileAppenderSimpleTest.java | 86 ++++++++++++++++++
.../MemoryMappedFileAppenderLocationTest.xml | 18 ++++
.../MemoryMappedFileAppenderRemapTest.xml | 18 ++++
.../resources/MemoryMappedFileAppenderTest.xml | 18 ++++
6 files changed, 330 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9063e0c5/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderLocationTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderLocationTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderLocationTest.java
new file mode 100644
index 0000000..d9f5d57
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderLocationTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.Integers;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests that logged strings and their location appear in the file,
+ * that the file size is the next power of two of the specified mapped region length
+ * and that the file is shrunk to its actual usage when done.
+ */
+public class MemoryMappedFileAppenderLocationTest {
+
+ final String LOGFILE = "target/MemoryMappedFileAppenderLocationTest.log";
+
+ @Before
+ public void before() {
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "MemoryMappedFileAppenderLocationTest.xml");
+ }
+
+ @Test
+ public void testMemMapLocation() throws Exception {
+ final File f = new File(LOGFILE);
+ if (f.exists()) {
+ assertTrue(f.delete());
+ }
+ assertTrue(!f.exists());
+
+ final int expectedFileLength = Integers.ceilingNextPowerOfTwo(32000);
+ assertEquals(32768, expectedFileLength);
+
+ final Logger log = LogManager.getLogger();
+ try {
+ log.warn("Test log1");
+ assertTrue(f.exists());
+ assertEquals("initial length", expectedFileLength, f.length());
+
+ log.warn("Test log2");
+ assertEquals("not grown", expectedFileLength, f.length());
+ } finally {
+ ((LoggerContext) LogManager.getContext(false)).stop();
+ }
+ assertEquals("Shrunk to actual used size", 474, f.length());
+
+ String line1, line2, line3;
+ final BufferedReader reader = new BufferedReader(new FileReader(LOGFILE));
+ try {
+ line1 = reader.readLine();
+ line2 = reader.readLine();
+ line3 = reader.readLine();
+ } finally {
+ reader.close();
+ }
+ assertNotNull(line1);
+ assertThat(line1, containsString("Test log1"));
+ String location1 = "org.apache.logging.log4j.core.appender.MemoryMappedFileAppenderLocationTest.testMemMapBasics(MemoryMappedFileAppenderLocationTest.java:63)";
+ assertThat(line1, containsString(location1));
+
+ assertNotNull(line2);
+ assertThat(line2, containsString("Test log2"));
+ String location2 = "org.apache.logging.log4j.core.appender.MemoryMappedFileAppenderLocationTest.testMemMapBasics(MemoryMappedFileAppenderLocationTest.java:67)";
+ assertThat(line2, containsString(location2));
+
+ assertNull("only two lines were logged", line3);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9063e0c5/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderRemapTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderRemapTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderRemapTest.java
new file mode 100644
index 0000000..bcb9cdc
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderRemapTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.Arrays;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests that logged strings appear in the file, that the initial file size is the specified specified region length,
+ * that the file is extended by region length when necessary, and that the file is shrunk to its actual usage when done.
+ */
+public class MemoryMappedFileAppenderRemapTest {
+
+ final String LOGFILE = "target/MemoryMappedFileAppenderRemapTest.log";
+
+ @Before
+ public void before() {
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, "MemoryMappedFileAppenderRemapTest.xml");
+ }
+
+ @Test
+ public void testMemMapExtendsIfNeeded() throws Exception {
+ final File f = new File(LOGFILE);
+ if (f.exists()) {
+ assertTrue(f.delete());
+ }
+ assertTrue(!f.exists());
+
+ final Logger log = LogManager.getLogger();
+ final char[] text = new char[200];
+ Arrays.fill(text, 'A');
+ try {
+ log.warn("Test log1");
+ assertTrue(f.exists());
+ assertEquals("initial length", 256, f.length());
+
+ log.warn(new String(text));
+ assertEquals("grown", 256 * 2, f.length());
+
+ log.warn(new String(text));
+ assertEquals("grown again", 256 * 3, f.length());
+ } finally {
+ ((LoggerContext) LogManager.getContext(false)).stop();
+ }
+ assertEquals("Shrunk to actual used size", 664, f.length());
+
+ String line1, line2, line3, line4;
+ final BufferedReader reader = new BufferedReader(new FileReader(LOGFILE));
+ try {
+ line1 = reader.readLine();
+ line2 = reader.readLine();
+ line3 = reader.readLine();
+ line4 = reader.readLine();
+ } finally {
+ reader.close();
+ }
+ assertNotNull(line1);
+ assertThat(line1, containsString("Test log1"));
+
+ assertNotNull(line2);
+ assertThat(line2, containsString(new String(text)));
+
+ assertNotNull(line3);
+ assertThat(line3, containsString(new String(text)));
+
+ assertNull("only three lines were logged", line4);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9063e0c5/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderSimpleTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderSimpleTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderSimpleTest.java
new file mode 100644
index 0000000..bedeb7f
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderSimpleTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests that logged strings appear in the file,
+ * that the default file size is used if not specified
+ * and that the file is shrunk to its actual usage when done.
+ */
+public class MemoryMappedFileAppenderSimpleTest {
+
+ final String LOGFILE = "target/MemoryMappedFileAppenderTest.log";
+
+ @Before
+ public void before() {
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, "MemoryMappedFileAppenderTest.xml");
+ }
+
+ @Test
+ public void testMemMapBasics() throws Exception {
+ final File f = new File(LOGFILE);
+ if (f.exists()) {
+ assertTrue(f.delete());
+ }
+ assertTrue(!f.exists());
+
+ final Logger log = LogManager.getLogger();
+ try {
+ log.warn("Test log1");
+ assertTrue(f.exists());
+ assertEquals("initial length", MemoryMappedFileManager.DEFAULT_REGION_LENGTH, f.length());
+
+ log.warn("Test log2");
+ assertEquals("not grown", MemoryMappedFileManager.DEFAULT_REGION_LENGTH, f.length());
+ } finally {
+ ((LoggerContext) LogManager.getContext(false)).stop();
+ }
+ assertEquals("Shrunk to actual used size", 190, f.length());
+
+ String line1, line2, line3;
+ final BufferedReader reader = new BufferedReader(new FileReader(LOGFILE));
+ try {
+ line1 = reader.readLine();
+ line2 = reader.readLine();
+ line3 = reader.readLine();
+ } finally {
+ reader.close();
+ }
+ assertNotNull(line1);
+ assertThat(line1, containsString("Test log1"));
+
+ assertNotNull(line2);
+ assertThat(line2, containsString("Test log2"));
+
+ assertNull("only two lines were logged", line3);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9063e0c5/log4j-core/src/test/resources/MemoryMappedFileAppenderLocationTest.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/MemoryMappedFileAppenderLocationTest.xml b/log4j-core/src/test/resources/MemoryMappedFileAppenderLocationTest.xml
new file mode 100644
index 0000000..14b6b25
--- /dev/null
+++ b/log4j-core/src/test/resources/MemoryMappedFileAppenderLocationTest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="off">
+ <Appenders>
+ <MemoryMappedFile name="MemoryMappedFile"
+ fileName="target/MemoryMappedFileAppenderLocationTest.log"
+ regionLength="32000" immediateFlush="true" append="false">
+ <PatternLayout>
+ <Pattern>%d %p %c{1.} [%t] %X{aKey} %m %location %ex%n</Pattern>
+ </PatternLayout>
+ </MemoryMappedFile>
+ </Appenders>
+
+ <Loggers>
+ <Root level="info" includeLocation="true">
+ <AppenderRef ref="MemoryMappedFile"/>
+ </Root>
+ </Loggers>
+</Configuration>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9063e0c5/log4j-core/src/test/resources/MemoryMappedFileAppenderRemapTest.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/MemoryMappedFileAppenderRemapTest.xml b/log4j-core/src/test/resources/MemoryMappedFileAppenderRemapTest.xml
new file mode 100644
index 0000000..02799a5
--- /dev/null
+++ b/log4j-core/src/test/resources/MemoryMappedFileAppenderRemapTest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="off">
+ <Appenders>
+ <MemoryMappedFile name="MemoryMappedFile"
+ fileName="target/MemoryMappedFileAppenderRemapTest.log"
+ regionLength="256" immediateFlush="true" append="false">
+ <PatternLayout>
+ <Pattern>%d %p %c{1.} [%t] %X{aKey} %m%ex%n</Pattern>
+ </PatternLayout>
+ </MemoryMappedFile>
+ </Appenders>
+
+ <Loggers>
+ <Root level="info">
+ <AppenderRef ref="MemoryMappedFile"/>
+ </Root>
+ </Loggers>
+</Configuration>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9063e0c5/log4j-core/src/test/resources/MemoryMappedFileAppenderTest.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/MemoryMappedFileAppenderTest.xml b/log4j-core/src/test/resources/MemoryMappedFileAppenderTest.xml
new file mode 100644
index 0000000..430c8b1
--- /dev/null
+++ b/log4j-core/src/test/resources/MemoryMappedFileAppenderTest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="OFF">
+ <Appenders>
+ <MemoryMappedFile name="MemoryMappedFile"
+ fileName="target/MemoryMappedFileAppenderTest.log"
+ immediateFlush="false" append="false">
+ <PatternLayout>
+ <Pattern>%d %p %c{1.} [%t] %X{aKey} %m%ex%n</Pattern>
+ </PatternLayout>
+ </MemoryMappedFile>
+ </Appenders>
+
+ <Loggers>
+ <Root level="info" includeLocation="false">
+ <AppenderRef ref="MemoryMappedFile"/>
+ </Root>
+ </Loggers>
+</Configuration>
\ No newline at end of file