You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2013/04/01 05:51:23 UTC

svn commit: r1463078 [2/4] - in /logging/log4j/log4j2/trunk: ./ core/src/main/java/org/apache/logging/log4j/core/ core/src/main/java/org/apache/logging/log4j/core/appender/ core/src/main/java/org/apache/logging/log4j/core/config/ core/src/main/java/org...

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,53 @@
+/*
+ * 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.async;
+
+import com.lmax.disruptor.util.Util;
+
+
+/**
+ * This Clock implementation is similar to CachedClock. It is slightly faster at
+ * the cost of some accuracy.
+ */
+public class CoarseCachedClock implements Clock {
+    private static CoarseCachedClock instance = new CoarseCachedClock();
+    private volatile long millis = System.currentTimeMillis();
+    private final Thread updater = new Thread("Clock Updater Thread") {
+        public void run() {
+            while (true) {
+                long time = System.currentTimeMillis();
+                millis = time;
+                Util.getUnsafe().park(true, time + 1); // abs (millis)
+                // Util.getUnsafe().park(false, 1000 * 1000);// relative(nanos)
+            }
+        }
+    };
+
+    public static CoarseCachedClock instance() {
+        return instance;
+    }
+
+    private CoarseCachedClock() {
+        updater.setDaemon(true);
+        updater.start();
+    }
+
+    // @Override
+    public long currentTimeMillis() {
+        return millis;
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEvent.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEvent.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEvent.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEvent.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,207 @@
+/*
+ * 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.async;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext.ContextStack;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.lookup.StrSubstitutor;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+
+import com.lmax.disruptor.EventFactory;
+
+/**
+ * When the Disruptor is started, the RingBuffer is populated with event
+ * objects. These objects are then re-used during the life of the RingBuffer.
+ */
+public class RingBufferLogEvent implements LogEvent {
+    private static final long serialVersionUID = 8462119088943934758L;
+
+    public static final Factory FACTORY = new Factory();
+
+    /**
+     * Creates the events that will be put in the RingBuffer.
+     */
+    private static class Factory implements EventFactory<RingBufferLogEvent> {
+        // @Override
+        public RingBufferLogEvent newInstance() {
+            return new RingBufferLogEvent();
+        }
+    }
+
+    private AsyncLogger asyncLogger;
+    private String loggerName;
+    private Marker marker;
+    private String fqcn;
+    private Level level;
+    private Message message;
+    private Throwable thrown;
+    private Map<String, String> contextMap;
+    private ContextStack contextStack;
+    private String threadName;
+    private StackTraceElement location;
+    private long currentTimeMillis;
+    private boolean endOfBatch;
+    private boolean includeLocation;
+
+    public void setValues(AsyncLogger asyncLogger, String loggerName,
+            Marker marker, String fqcn, Level level, Message data, Throwable t,
+            Map<String, String> map, ContextStack contextStack,
+            String threadName, StackTraceElement location,
+            long currentTimeMillis) {
+        this.asyncLogger = asyncLogger;
+        this.loggerName = loggerName;
+        this.marker = marker;
+        this.fqcn = fqcn;
+        this.level = level;
+        this.message = data;
+        this.thrown = t;
+        this.contextMap = map;
+        this.contextStack = contextStack;
+        this.threadName = threadName;
+        this.location = location;
+        this.currentTimeMillis = currentTimeMillis;
+    }
+
+    /**
+     * Event processor that reads the event from the ringbuffer can call this
+     * method.
+     * 
+     * @param endOfBatch flag to indicate if this is the last event in a batch
+     *            from the RingBuffer
+     */
+    public void execute(boolean endOfBatch) {
+        this.endOfBatch = endOfBatch;
+        asyncLogger.actualAsyncLog(this);
+    }
+
+    /**
+     * Returns {@code true} if this event is the end of a batch, {@code false}
+     * otherwise.
+     * 
+     * @return {@code true} if this event is the end of a batch, {@code false}
+     *         otherwise
+     */
+    public boolean isEndOfBatch() {
+        return endOfBatch;
+    }
+
+    public void setEndOfBatch(boolean endOfBatch) {
+        this.endOfBatch = endOfBatch;
+    }
+
+    public boolean isIncludeLocation() {
+        return includeLocation;
+    }
+
+    public void setIncludeLocation(boolean includeLocation) {
+        this.includeLocation = includeLocation;
+    }
+
+    // @Override
+    public String getLoggerName() {
+        return loggerName;
+    }
+
+    // @Override
+    public Marker getMarker() {
+        return marker;
+    }
+
+    // @Override
+    public String getFQCN() {
+        return fqcn;
+    }
+
+    // @Override
+    public Level getLevel() {
+        return level;
+    }
+
+    // @Override
+    public Message getMessage() {
+        if (message == null) {
+            message = new SimpleMessage("");
+        }
+        return message;
+    }
+
+    // @Override
+    public Throwable getThrown() {
+        return thrown;
+    }
+
+    // @Override
+    public Map<String, String> getContextMap() {
+        return contextMap;
+    }
+
+    // @Override
+    public ContextStack getContextStack() {
+        return contextStack;
+    }
+
+    // @Override
+    public String getThreadName() {
+        return threadName;
+    }
+
+    // @Override
+    public StackTraceElement getSource() {
+        return location;
+    }
+
+    // @Override
+    public long getMillis() {
+        return currentTimeMillis;
+    }
+
+    /**
+     * Merges the contents of the specified map into the contextMap, after
+     * replacing any variables in the property values with the
+     * StrSubstitutor-supplied actual values.
+     * 
+     * @param properties configured properties
+     * @param strSubstitutor used to lookup values of variables in properties
+     */
+    public void mergePropertiesIntoContextMap(
+            Map<Property, Boolean> properties, StrSubstitutor strSubstitutor) {
+        if (properties == null) {
+            return; // nothing to do
+        }
+
+        Map<String, String> map = (contextMap == null) ? new HashMap<String, String>()
+                : new HashMap<String, String>(contextMap);
+
+        for (Map.Entry<Property, Boolean> entry : properties.entrySet()) {
+            Property prop = entry.getKey();
+            if (map.containsKey(prop.getName())) {
+                continue; // contextMap overrides config properties
+            }
+            String value = entry.getValue() ? strSubstitutor.replace(prop
+                    .getValue()) : prop.getValue();
+            map.put(prop.getName(), value);
+        }
+        contextMap = map;
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,36 @@
+/*
+ * 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.async;
+
+import com.lmax.disruptor.EventHandler;
+
+/**
+ * This event handler gets passed messages from the RingBuffer as they become
+ * available. Processing of these messages is done in a separate thread,
+ * controlled by the {@code Executor} passed to the {@code Disruptor}
+ * constructor.
+ */
+public class RingBufferLogEventHandler implements
+        EventHandler<RingBufferLogEvent> {
+
+    // @Override
+    public void onEvent(RingBufferLogEvent event, long sequence,
+            boolean endOfBatch) throws Exception {
+        event.execute(endOfBatch);
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,70 @@
+/*
+ * 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.async;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext.ContextStack;
+import org.apache.logging.log4j.message.Message;
+
+import com.lmax.disruptor.EventTranslator;
+
+public class RingBufferLogEventTranslator implements
+        EventTranslator<RingBufferLogEvent> {
+
+    private AsyncLogger asyncLogger;
+    private String loggerName;
+    private Marker marker;
+    private String fqcn;
+    private Level level;
+    private Message message;
+    private Throwable thrown;
+    private Map<String, String> contextMap;
+    private ContextStack contextStack;
+    private String threadName;
+    private StackTraceElement location;
+    private long currentTimeMillis;
+
+    // @Override
+    public void translateTo(RingBufferLogEvent event, long sequence) {
+        event.setValues(asyncLogger, loggerName, marker, fqcn, level, message,
+                thrown, contextMap, contextStack, threadName, location,
+                currentTimeMillis);
+    }
+
+    public void setValues(AsyncLogger asyncLogger, String loggerName,
+            Marker marker, String fqcn, Level level, Message message,
+            Throwable thrown, Map<String, String> contextMap,
+            ContextStack contextStack, String threadName,
+            StackTraceElement location, long currentTimeMillis) {
+        this.asyncLogger = asyncLogger;
+        this.loggerName = loggerName;
+        this.marker = marker;
+        this.fqcn = fqcn;
+        this.level = level;
+        this.message = message;
+        this.thrown = thrown;
+        this.contextMap = contextMap;
+        this.contextStack = contextStack;
+        this.threadName = threadName;
+        this.location = location;
+        this.currentTimeMillis = currentTimeMillis;
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,26 @@
+/*
+ * 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.async;
+
+public class SystemClock implements Clock {
+
+    // @Override
+    public long currentTimeMillis() {
+        return System.currentTimeMillis();
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,165 @@
+/*
+ * 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.async.appender;
+
+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.appender.AbstractOutputStreamAppender;
+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.PluginAttr;
+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;
+
+/**
+ * File Appender.
+ */
+@Plugin(name = "FastFile", type = "Core", elementType = "appender", printObject = true)
+public final class FastFileAppender extends AbstractOutputStreamAppender {
+
+    private final String fileName;
+    private Object advertisement;
+    private final Advertiser advertiser;
+
+    private FastFileAppender(String name, Layout<?> layout, Filter filter,
+            FastFileManager manager, String filename, boolean handleException,
+            boolean immediateFlush, Advertiser advertiser) {
+        super(name, layout, filter, handleException, immediateFlush, manager);
+        if (advertiser != null) {
+            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(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.
+        //
+        // without LOG4J2-164:
+        // if (event.getClass() == RingBufferLogEvent.class) {
+        // boolean isEndOfBatch = ((RingBufferLogEvent) event).isEndOfBatch();
+        // ((FastFileManager) getManager()).setEndOfBatch(isEndOfBatch);
+        // }
+        ((FastFileManager) 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;
+    }
+
+    // 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 suppress "true" if exceptions should be hidden from the
+     *            application, "false" otherwise. The default is "true".
+     * @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 FastFileAppender createAppender(
+            @PluginAttr("fileName") String fileName,
+            @PluginAttr("append") String append,
+            @PluginAttr("name") String name,
+            @PluginAttr("immediateFlush") String immediateFlush,
+            @PluginAttr("suppressExceptions") String suppress,
+            @PluginElement("layout") Layout<?> layout,
+            @PluginElement("filters") final Filter filter,
+            @PluginAttr("advertise") final String advertise,
+            @PluginAttr("advertiseURI") final String advertiseURI,
+            @PluginConfiguration final Configuration config) {
+
+        boolean isAppend = append == null ? true : Boolean.valueOf(append);
+        boolean isFlush = immediateFlush == null ? true : Boolean.valueOf(immediateFlush);
+        boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
+        boolean isAdvertise = advertise == null ? false : Boolean.valueOf(advertise);
+
+        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;
+        }
+
+        FastFileManager manager = FastFileManager.getFileManager(fileName,
+                isAppend, isFlush, advertiseURI);
+        if (manager == null) {
+            return null;
+        }
+        if (layout == null) {
+            layout = PatternLayout.createLayout(null, null, null, null);
+        }
+        return new FastFileAppender(name, layout, filter, manager, fileName,
+                handleExceptions, isFlush, isAdvertise ? config.getAdvertiser() : null);
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,195 @@
+/*
+ * 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.async.appender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.OutputStreamManager;
+
+/**
+ * Manages actual File I/O for File Appenders.
+ */
+public class FastFileManager extends OutputStreamManager {
+
+    private static final FastFileManagerFactory factory = new FastFileManagerFactory();
+
+    private final boolean isImmediateFlush;
+    private final String advertiseURI;
+    private final RandomAccessFile randomAccessFile;
+    private final ByteBuffer buffer;
+    private ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
+
+    protected FastFileManager(RandomAccessFile file, String fileName,
+            OutputStream os, boolean immediateFlush, String advertiseURI) {
+        super(os, fileName);
+        this.isImmediateFlush = immediateFlush;
+        this.randomAccessFile = file;
+        this.advertiseURI = advertiseURI;
+        isEndOfBatch.set(Boolean.FALSE);
+        buffer = ByteBuffer.allocate(256 * 1024); // TODO make configurable?
+    }
+
+    /**
+     * Returns the FastFileManager.
+     * 
+     * @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 advertiseURI the URI to use when advertising the file
+     * @return A FastFileManager for the File.
+     */
+    public static FastFileManager getFileManager(String fileName,
+            boolean append, boolean isFlush, String advertiseURI) {
+        return (FastFileManager) getManager(fileName, new FactoryData(append,
+                isFlush, advertiseURI), factory);
+    }
+
+    public Boolean isEndOfBatch() {
+        return isEndOfBatch.get();
+    }
+
+    public void setEndOfBatch(boolean isEndOfBatch) {
+        this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
+    }
+
+    @Override
+    protected synchronized void write(byte[] bytes, int offset, int length) {
+        super.write(bytes, offset, length); // writes to dummy output stream
+
+        if (length > buffer.remaining()) {
+            flush();
+        }
+        buffer.put(bytes, offset, length);
+        if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
+            flush();
+        }
+    }
+
+    @Override
+    public void flush() {
+        buffer.flip();
+        try {
+            randomAccessFile.write(buffer.array(), 0, buffer.limit());
+        } catch (IOException ex) {
+            String msg = "Error writing to RandomAccessFile " + getName();
+            throw new AppenderRuntimeException(msg, ex);
+        }
+        buffer.clear();
+    }
+
+    /**
+     * Returns the name of the File being managed.
+     * 
+     * @return The name of the File being managed.
+     */
+    public String getFileName() {
+        return getName();
+    }
+
+    private static class DummyOutputStream extends OutputStream {
+        @Override
+        public void write(int b) throws IOException {
+        }
+
+        @Override
+        public void write(byte[] b, int off, 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
+     */
+    public Map<String, String> getContentFormat() {
+        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 immediateFlush;
+        private final String advertiseURI;
+
+        /**
+         * Constructor.
+         * 
+         * @param append
+         *            Append status.
+         */
+        public FactoryData(boolean append, boolean immediateFlush, String advertiseURI) {
+            this.append = append;
+            this.immediateFlush = immediateFlush;
+            this.advertiseURI = advertiseURI;
+        }
+    }
+
+    /**
+     * Factory to create a FastFileManager.
+     */
+    private static class FastFileManagerFactory implements
+            ManagerFactory<FastFileManager, FactoryData> {
+
+        /**
+         * Create a FastFileManager.
+         * 
+         * @param name
+         *            The name of the File.
+         * @param data
+         *            The FactoryData
+         * @return The FastFileManager for the File.
+         */
+        public FastFileManager createManager(String name, FactoryData data) {
+            File file = new File(name);
+            final File parent = file.getParentFile();
+            if (null != parent && !parent.exists()) {
+                parent.mkdirs();
+            }
+            if (!data.append) {
+                file.delete();
+            }
+
+            OutputStream os = new DummyOutputStream();
+            RandomAccessFile raf;
+            try {
+                raf = new RandomAccessFile(name, "rw");
+                return new FastFileManager(raf, name, os, data.immediateFlush,
+                        data.advertiseURI);
+            } catch (Exception ex) {
+                LOGGER.error("FastFileManager (" + name + ") " + ex);
+            }
+            return null;
+        }
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,193 @@
+/*
+ * 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.async.appender;
+
+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.appender.AbstractOutputStreamAppender;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RollingFileManager;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+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.PluginAttr;
+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;
+
+/**
+ * An appender that writes to random access files and can roll over at
+ * intervals.
+ */
+@Plugin(name = "FastRollingFile", type = "Core", elementType = "appender", printObject = true)
+public final class FastRollingFileAppender extends AbstractOutputStreamAppender {
+
+    private final String fileName;
+    private final String filePattern;
+    private Object advertisement;
+    private final Advertiser advertiser;
+
+    private FastRollingFileAppender(String name, Layout<?> layout, Filter filter,
+                                RollingFileManager manager, String fileName,
+                                String filePattern, boolean handleException, 
+                                boolean immediateFlush, Advertiser advertiser) {
+        super(name, layout, filter, handleException, immediateFlush, manager);
+        if (advertiser != null) {
+            Map<String, String> configuration = new HashMap<String, String>(layout.getContentFormat());
+            configuration.put("contentType", layout.getContentType());
+            configuration.put("name", name);
+            advertisement = advertiser.advertise(configuration);
+        }
+        this.fileName = fileName;
+        this.filePattern = filePattern;
+        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) {
+        ((RollingFileManager) getManager()).checkRollover(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.
+        //
+        // without LOG4J2-164:
+        // if (event.getClass() == RingBufferLogEvent.class) {
+        // boolean isEndOfBatch = ((RingBufferLogEvent) event).isEndOfBatch();
+        // ((FastRollingFileManager) getManager()).setEndOfBatch(isEndOfBatch);
+        // }
+        ((FastRollingFileManager) getManager()).setEndOfBatch(event.isEndOfBatch());
+        super.append(event);
+    }
+
+    /**
+     * Returns the File name for the Appender.
+     * @return The file name.
+     */
+    public String getFileName() {
+        return fileName;
+    }
+
+    /**
+     * Returns the file pattern used when rolling over.
+     * @return The file pattern.
+     */
+    public String getFilePattern() {
+        return filePattern;
+    }
+
+    /**
+     * Create a FastRollingFileAppender.
+     * @param fileName The name of the file that is actively written to. (required).
+     * @param filePattern The pattern of the file name to use on rollover. (required).
+     * @param append If true, events are appended to the file. If false, the file
+     * is overwritten when opened. Defaults to "true"
+     * @param name The name of the Appender (required).
+     * @param immediateFlush When true, events are immediately flushed. Defaults to "true".
+     * @param policy The triggering policy. (required).
+     * @param strategy The rollover strategy. Defaults to DefaultRolloverStrategy.
+     * @param layout The layout to use (defaults to the default PatternLayout).
+     * @param filter The Filter or null.
+     * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
+     * The default is "true".
+     * @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 A FastRollingFileAppender.
+     */
+    @PluginFactory
+    public static FastRollingFileAppender createAppender(@PluginAttr("fileName") final String fileName,
+                                              @PluginAttr("filePattern") final String filePattern,
+                                              @PluginAttr("append") final String append,
+                                              @PluginAttr("name") final String name,
+                                              @PluginAttr("immediateFlush") final String immediateFlush,
+                                              @PluginElement("policy") final TriggeringPolicy policy,
+                                              @PluginElement("strategy") RolloverStrategy strategy,
+                                              @PluginElement("layout") Layout<?> layout,
+                                              @PluginElement("filter") final Filter filter,
+                                              @PluginAttr("suppressExceptions") final String suppress,
+                                              @PluginAttr("advertise") final String advertise,
+                                              @PluginAttr("advertiseURI") final String advertiseURI,
+                                              @PluginConfiguration final Configuration config) {
+
+        final boolean isAppend = append == null ? true : Boolean.valueOf(append);
+        final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
+        final boolean isFlush = immediateFlush == null ? true : Boolean.valueOf(immediateFlush);
+        boolean isAdvertise = advertise == null ? false : Boolean.valueOf(advertise);
+
+        if (name == null) {
+            LOGGER.error("No name provided for FileAppender");
+            return null;
+        }
+
+        if (fileName == null) {
+            LOGGER.error("No filename was provided for FileAppender with name "  + name);
+            return null;
+        }
+
+        if (filePattern == null) {
+            LOGGER.error("No filename pattern provided for FileAppender with name "  + name);
+            return null;
+        }
+
+        if (policy == null) {
+            LOGGER.error("A TriggeringPolicy must be provided");
+            return null;
+        }
+
+        if (strategy == null) {
+            strategy = DefaultRolloverStrategy.createStrategy(null, null, "true", config);
+        }
+
+        final FastRollingFileManager manager = FastRollingFileManager
+                .getFastRollingFileManager(fileName, filePattern, isAppend, 
+                        isFlush, policy, strategy, advertiseURI);
+        if (manager == null) {
+            return null;
+        }
+
+        if (layout == null) {
+            layout = PatternLayout.createLayout(null, null, null, null);
+        }
+
+        return new FastRollingFileAppender(name, layout, filter, manager, fileName, filePattern,
+            handleExceptions, isFlush, isAdvertise ? config.getAdvertiser() : null);
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java Mon Apr  1 03:51:21 2013
@@ -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.async.appender;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+
+import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.rolling.RollingFileManager;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+
+public class FastRollingFileManager extends RollingFileManager {
+    private static FastRollingFileManagerFactory factory = new FastRollingFileManagerFactory();
+
+    private final boolean isImmediateFlush;
+    private final RandomAccessFile randomAccessFile;
+    private final ByteBuffer buffer;
+    private ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
+
+    public FastRollingFileManager(RandomAccessFile raf, String fileName,
+            String pattern, OutputStream os, boolean append,
+            boolean immediateFlush, long size, long time, TriggeringPolicy policy,
+            RolloverStrategy strategy, String advertiseURI) {
+        super(fileName, pattern, os, append, size, time, policy, strategy, advertiseURI);
+        this.isImmediateFlush = immediateFlush;
+        this.randomAccessFile = raf;
+        isEndOfBatch.set(Boolean.FALSE);
+        buffer = ByteBuffer.allocate(256 * 1024); // TODO make configurable?
+    }
+
+    public static FastRollingFileManager getFastRollingFileManager(
+            String fileName, String filePattern, boolean isAppend,
+            boolean immediateFlush, TriggeringPolicy policy,
+            RolloverStrategy strategy, String advertiseURI) {
+        return (FastRollingFileManager) getManager(fileName, new FactoryData(
+                filePattern, isAppend, immediateFlush, policy, strategy, 
+                advertiseURI), factory);
+    }
+
+    public Boolean isEndOfBatch() {
+        return isEndOfBatch.get();
+    }
+
+    public void setEndOfBatch(boolean isEndOfBatch) {
+        this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
+    }
+
+    @Override
+    protected synchronized void write(byte[] bytes, int offset, int length) {
+        super.write(bytes, offset, length); // writes to dummy output stream
+
+        if (length > buffer.remaining()) {
+            flush();
+        }
+        buffer.put(bytes, offset, length);
+        if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
+            flush();
+        }
+    }
+
+    @Override
+    public void flush() {
+        buffer.flip();
+        try {
+            randomAccessFile.write(buffer.array(), 0, buffer.limit());
+        } catch (IOException ex) {
+            String msg = "Error writing to RandomAccessFile " + getName();
+            throw new AppenderRuntimeException(msg, ex);
+        }
+        buffer.clear();
+    }
+
+    /**
+     * Factory to create a FastRollingFileManager.
+     */
+    private static class FastRollingFileManagerFactory implements
+            ManagerFactory<FastRollingFileManager, FactoryData> {
+
+        /**
+         * Create the FastRollingFileManager.
+         * 
+         * @param name
+         *            The name of the entity to manage.
+         * @param data
+         *            The data required to create the entity.
+         * @return a RollingFileManager.
+         */
+        public FastRollingFileManager createManager(String name,
+                FactoryData data) {
+            File file = new File(name);
+            final File parent = file.getParentFile();
+            if (null != parent && !parent.exists()) {
+                parent.mkdirs();
+            }
+            if (!data.append) {
+                file.delete();
+            }
+            long size = data.append ? file.length() : 0;
+            long time = file.lastModified();
+
+            RandomAccessFile raf;
+            try {
+                raf = new RandomAccessFile(name, "rw");
+                return new FastRollingFileManager(raf, name, data.pattern,
+                        new DummyOutputStream(), data.append,
+                        data.immediateFlush, size, time, data.policy,
+                        data.strategy, data.advertiseURI);
+            } catch (FileNotFoundException ex) {
+                LOGGER.error("FastRollingFileManager (" + name + ") " + ex);
+            }
+            return null;
+        }
+    }
+
+    private static class DummyOutputStream extends OutputStream {
+        @Override
+        public void write(int b) throws IOException {
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+        }
+    }
+
+    /**
+     * Factory data.
+     */
+    private static class FactoryData {
+        private final String pattern;
+        private final boolean append;
+        private final boolean immediateFlush;
+        private final TriggeringPolicy policy;
+        private final RolloverStrategy strategy;
+        private final String advertiseURI;
+
+        /**
+         * Create the data for the factory.
+         * 
+         * @param pattern
+         *            The pattern.
+         * @param append
+         *            The append flag.
+         * @param immediateFlush
+         */
+        public FactoryData(String pattern, boolean append,
+                boolean immediateFlush, TriggeringPolicy policy,
+                RolloverStrategy strategy, String advertiseURI) {
+            this.pattern = pattern;
+            this.append = append;
+            this.immediateFlush = immediateFlush;
+            this.policy = policy;
+            this.strategy = strategy;
+            this.advertiseURI = advertiseURI;
+        }
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,354 @@
+/*
+ * 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.async.config;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.LoggerContext.Status;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+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.status.StatusLogger;
+
+import com.lmax.disruptor.BlockingWaitStrategy;
+import com.lmax.disruptor.EventFactory;
+import com.lmax.disruptor.EventHandler;
+import com.lmax.disruptor.EventTranslator;
+import com.lmax.disruptor.ExceptionHandler;
+import com.lmax.disruptor.RingBuffer;
+import com.lmax.disruptor.SleepingWaitStrategy;
+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;
+
+/**
+ * Asynchronous Logger object that is created via configuration.
+ */
+@Plugin(name = "asyncLogger", type = "Core", printObject = true)
+public class AsyncLoggerConfig extends LoggerConfig {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static volatile Disruptor<RingBufferLog4jEvent> disruptor;
+    private static ExecutorService executor = Executors
+            .newSingleThreadExecutor();
+
+    private ThreadLocal<LogEvent> currentLogEvent = new ThreadLocal<LogEvent>();
+
+    /**
+     * RingBuffer events contain all information necessary to perform the work
+     * in a separate thread.
+     */
+    private static class RingBufferLog4jEvent {
+        private AsyncLoggerConfig loggerConfig;
+        private LogEvent event;
+    }
+
+    /**
+     * Factory used to populate the RingBuffer with events. These event objects
+     * are then re-used during the life of the RingBuffer.
+     */
+    private static final EventFactory<RingBufferLog4jEvent> FACTORY = new EventFactory<RingBufferLog4jEvent>() {
+        @Override
+        public RingBufferLog4jEvent newInstance() {
+            return new RingBufferLog4jEvent();
+        }
+    };
+
+    /**
+     * Object responsible for passing on data to a specific RingBuffer event.
+     */
+    private final EventTranslator<RingBufferLog4jEvent> translator = new EventTranslator<RingBufferLog4jEvent>() {
+        @Override
+        public void translateTo(RingBufferLog4jEvent event, long sequence) {
+            event.event = currentLogEvent.get();
+            event.loggerConfig = AsyncLoggerConfig.this;
+        }
+    };
+
+    /**
+     * EventHandler performs the work in a separate thread.
+     */
+    private static class RingBufferLog4jEventHandler implements
+            EventHandler<RingBufferLog4jEvent> {
+        @Override
+        public void onEvent(RingBufferLog4jEvent event, long sequence,
+                boolean endOfBatch) throws Exception {
+            event.loggerConfig.asyncCallAppenders(event.event, endOfBatch);
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public AsyncLoggerConfig() {
+        super();
+    }
+
+    /**
+     * Constructor that sets the name, level and additive values.
+     * 
+     * @param name The Logger name.
+     * @param level The Level.
+     * @param additive true if the Logger is additive, false otherwise.
+     */
+    public AsyncLoggerConfig(final String name, final Level level,
+            final boolean additive) {
+        super(name, level, additive);
+    }
+
+    protected AsyncLoggerConfig(final String name,
+            final List<AppenderRef> appenders, final Filter filter,
+            final Level level, final boolean additive,
+            final Property[] properties, final Configuration config,
+            final boolean includeLocation) {
+        super(name, appenders, filter, level, additive, properties, config,
+                includeLocation);
+    }
+
+    /**
+     * Passes on the event to a separate thread that will call
+     * {@link #asyncCallAppenders(LogEvent)}.
+     */
+    @Override
+    protected void callAppenders(LogEvent event) {
+        // populate lazily initialized fields
+        event.getSource();
+        event.getThreadName();
+
+        // pass on the event to a separate thread
+        currentLogEvent.set(event);
+        disruptor.publishEvent(translator);
+    }
+
+    /** Called by RingBufferLog4jEventHandler. */
+    private void asyncCallAppenders(LogEvent event, boolean endOfBatch) {
+        event.setEndOfBatch(endOfBatch);
+        super.callAppenders(event);
+    }
+
+    @Override
+    public void startFilter() {
+        if (disruptor == null) {
+            int ringBufferSize = calculateRingBufferSize();
+            WaitStrategy waitStrategy = createWaitStrategy();
+            disruptor = new Disruptor<RingBufferLog4jEvent>(FACTORY,
+                    ringBufferSize, executor, ProducerType.MULTI, waitStrategy);
+            EventHandler<RingBufferLog4jEvent>[] handlers = new RingBufferLog4jEventHandler[] { new RingBufferLog4jEventHandler() };
+            disruptor.handleExceptionsWith(getExceptionHandler());
+            disruptor.handleEventsWith(handlers);
+
+            LOGGER.debug(
+                    "Starting AsyncLoggerConfig disruptor with ringbuffer size {}...",
+                    disruptor.getRingBuffer().getBufferSize());
+            disruptor.start();
+        }
+        super.startFilter();
+    }
+
+    private WaitStrategy createWaitStrategy() {
+        String strategy = System.getProperty("AsyncLoggerConfig.WaitStrategy");
+        LOGGER.debug("property AsyncLoggerConfig.WaitStrategy={}", strategy);
+        if ("Sleep".equals(strategy)) {
+            LOGGER.debug("disruptor event handler uses SleepingWaitStrategy");
+            return new SleepingWaitStrategy();
+        } else if ("Yield".equals(strategy)) {
+            LOGGER.debug("disruptor event handler uses YieldingWaitStrategy");
+            return new YieldingWaitStrategy();
+        } else if ("Block".equals(strategy)) {
+            LOGGER.debug("disruptor event handler uses BlockingWaitStrategy");
+            return new BlockingWaitStrategy();
+        }
+        LOGGER.debug("disruptor event handler uses SleepingWaitStrategy");
+        return new SleepingWaitStrategy();
+    }
+
+    private static int calculateRingBufferSize() {
+        String userPreferredRBSize = System.getProperty(
+                "AsyncLoggerConfig.RingBufferSize", "256000");
+        int ringBufferSize = 256000; // default
+        try {
+            int size = Integer.parseInt(userPreferredRBSize);
+            if (size < 128) {
+                size = 128;
+                LOGGER.warn(
+                        "Invalid RingBufferSize {}, using minimum size 128.",
+                        userPreferredRBSize);
+            }
+            ringBufferSize = size;
+        } catch (Exception ex) {
+            LOGGER.warn("Invalid RingBufferSize {}, using default size.",
+                    userPreferredRBSize);
+        }
+        return Util.ceilingNextPowerOfTwo(ringBufferSize);
+    }
+
+    private static ExceptionHandler getExceptionHandler() {
+        String cls = System.getProperty("AsyncLoggerConfig.ExceptionHandler");
+        if (cls == null) {
+            LOGGER.debug("No AsyncLoggerConfig.ExceptionHandler specified");
+            return null;
+        }
+        try {
+            @SuppressWarnings("unchecked")
+            Class<? extends ExceptionHandler> klass = (Class<? extends ExceptionHandler>) Class
+                    .forName(cls);
+            ExceptionHandler result = klass.newInstance();
+            LOGGER.debug("AsyncLoggerConfig.ExceptionHandler=" + result);
+            return result;
+        } catch (Exception ignored) {
+            LOGGER.debug(
+                    "AsyncLoggerConfig.ExceptionHandler not set: error creating "
+                            + cls + ": ", ignored);
+            return null;
+        }
+    }
+
+    @Override
+    public void stopFilter() {
+        // only stop disruptor if shutting down logging subsystem
+        if (LogManager.getContext() instanceof LoggerContext) {
+            if (((LoggerContext) LogManager.getContext()).getStatus() != Status.STOPPING) {
+                return;
+            }
+        }
+        Disruptor<RingBufferLog4jEvent> temp = disruptor;
+
+        // Must guarantee that publishing to the RingBuffer has stopped
+        // before we call disruptor.shutdown()
+        disruptor = null; // client code fails with NPE if log after stop = OK
+        temp.shutdown();
+
+        // wait up to 10 seconds for the ringbuffer to drain
+        RingBuffer<RingBufferLog4jEvent> ringBuffer = temp.getRingBuffer();
+        for (int i = 0; i < 20; i++) {
+            if (ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize())) {
+                break;
+            }
+            try {
+                Thread.sleep(500); // give ringbuffer some time to drain...
+            } catch (InterruptedException e) {
+            }
+        }
+        executor.shutdown(); // finally, kill the processor thread
+        super.stopFilter();
+    }
+
+    /**
+     * Factory method to create a LoggerConfig.
+     * 
+     * @param additivity True if additive, false otherwise.
+     * @param levelName The Level to be associated with the Logger.
+     * @param loggerName The name of the Logger.
+     * @param includeLocation "true" if location should be passed downstream
+     * @param refs An array of Appender names.
+     * @param properties Properties to pass to the Logger.
+     * @param config The Configuration.
+     * @param filter A Filter.
+     * @return A new LoggerConfig.
+     */
+    @PluginFactory
+    public static LoggerConfig createLogger(
+            @PluginAttr("additivity") final String additivity,
+            @PluginAttr("level") final String levelName,
+            @PluginAttr("name") final String loggerName,
+            @PluginAttr("includeLocation") final String includeLocation,
+            @PluginElement("appender-ref") final AppenderRef[] refs,
+            @PluginElement("properties") final Property[] properties,
+            @PluginConfiguration final Configuration config,
+            @PluginElement("filters") final Filter filter) {
+        if (loggerName == null) {
+            LOGGER.error("Loggers cannot be configured without a name");
+            return null;
+        }
+
+        final List<AppenderRef> appenderRefs = Arrays.asList(refs);
+        Level level;
+        try {
+            level = Level.toLevel(levelName, Level.ERROR);
+        } catch (final Exception ex) {
+            LOGGER.error(
+                    "Invalid Log level specified: {}. Defaulting to Error",
+                    levelName);
+            level = Level.ERROR;
+        }
+        final String name = loggerName.equals("root") ? "" : loggerName;
+        final boolean additive = additivity == null ? true : Boolean
+                .parseBoolean(additivity);
+
+        return new AsyncLoggerConfig(name, appenderRefs, filter, level,
+                additive, properties, config, includeLocation(includeLocation));
+    }
+    
+    // Note: for asynchronous loggers, includeLocation default is FALSE
+    private static boolean includeLocation(String includeLocationConfigValue) {
+        if (includeLocationConfigValue == null) {
+            return false;
+        }
+        return Boolean.parseBoolean(includeLocationConfigValue);
+    }
+
+    /**
+     * An asynchronous root Logger.
+     */
+    @Plugin(name = "asyncRoot", type = "Core", printObject = true)
+    public static class RootLogger extends LoggerConfig {
+
+        @PluginFactory
+        public static LoggerConfig createLogger(
+                @PluginAttr("additivity") final String additivity,
+                @PluginAttr("level") final String levelName,
+                @PluginAttr("includeLocation") final String includeLocation,
+                @PluginElement("appender-ref") final AppenderRef[] refs,
+                @PluginElement("properties") final Property[] properties,
+                @PluginConfiguration final Configuration config,
+                @PluginElement("filters") final Filter filter) {
+            final List<AppenderRef> appenderRefs = Arrays.asList(refs);
+            Level level;
+            try {
+                level = Level.toLevel(levelName, Level.ERROR);
+            } catch (final Exception ex) {
+                LOGGER.error(
+                        "Invalid Log level specified: {}. Defaulting to Error",
+                        levelName);
+                level = Level.ERROR;
+            }
+            final boolean additive = additivity == null ? true : Boolean
+                    .parseBoolean(additivity);
+
+            return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
+                    appenderRefs, filter, level, additive, properties, config,
+                    includeLocation(includeLocation));
+        }
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/site/site.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/site/site.xml?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/site/site.xml (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/site/site.xml Mon Apr  1 03:51:21 2013
@@ -0,0 +1,27 @@
+<!--
+ 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.
+
+-->
+<project name="Async">
+  <body>
+    <links>
+      <item name="Apache" href="http://www.apache.org/" />
+      <item name="Logging Services" href="http://logging.apache.org/"/>
+      <item name="Log4j" href="../index.html"/>
+    </links>
+    <menu ref="reports"/>
+  </body>
+</project>

Added: logging/log4j/log4j2/trunk/log4j-async/src/site/xdoc/index.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/site/xdoc/index.xml?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/site/xdoc/index.xml (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/site/xdoc/index.xml Mon Apr  1 03:51:21 2013
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<!--
+    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.
+-->
+<document>
+    <properties>
+        <title>Asynchronous Logging</title>
+        <author email="remkop@yahoo.com">Remko Popma</author>
+    </properties>
+    <body>
+        <section name="Asynchronous Logging">
+            <p>
+              Log4j-async uses <a href="http://lmax-exchange.github.com/disruptor/">LMAX Disruptor</a> 
+              technology to significantly improve the performance of
+              asynchronous logging, especially in multi-threaded scenarios.
+            </p>
+        </section>
+
+        <section name="Requirements">
+           <p>
+             Log4j-async requires at least Java 6 and is dependent on the Log4j 2 
+             API and implementation, as well as the LMAX disruptor library.
+          </p>
+        </section>
+
+      <section name="Usage">
+        <p>
+          Include this jar, the disruptor-3.0.0 jar, the Log4j 2 API and 
+          implementation jar together. Configure
+          the logging implementation as required.
+        </p>
+      </section>
+    </body>
+</document>
\ No newline at end of file

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,37 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.junit.Test;
+
+public class AsyncLoggerContextSelectorTest {
+
+    @Test
+    public void testContextReturnsAsyncLoggerContext() {
+        AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
+        LoggerContext context = selector.getContext(null, null, false);
+        
+        assertTrue(context instanceof AsyncLoggerContext);
+    }
+
+    @Test
+    public void testContext2ReturnsAsyncLoggerContext() {
+        AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
+        LoggerContext context = selector.getContext(null, null, false, null);
+        
+        assertTrue(context instanceof AsyncLoggerContext);
+    }
+
+    @Test
+    public void testLoggerContextsReturnsAsyncLoggerContext() {
+        AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
+        List<LoggerContext> list = selector.getLoggerContexts();
+        
+        assertEquals(1, list.size());
+        assertTrue(list.get(0) instanceof AsyncLoggerContext);
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,21 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.junit.Test;
+
+public class AsyncLoggerContextTest {
+
+    @Test
+    public void testNewInstanceReturnsAsyncLogger() {
+        Logger logger = new AsyncLoggerContext("a").newInstance(
+                new LoggerContext("a"), "a", null);
+        assertTrue(logger instanceof AsyncLogger);
+        
+        ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,54 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+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.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.apache.logging.log4j.core.helpers.Constants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AsyncLoggerLocationTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
+                AsyncLoggerContextSelector.class.getName());
+        System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+                "AsyncLoggerLocationTest.xml");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, "");
+    }
+
+    @Test
+    public void testAsyncLogWritesToLog() throws Exception {
+        File f = new File("AsyncLoggerLocationTest.log");
+        // System.out.println(f.getAbsolutePath());
+        f.delete();
+        Logger log = LogManager.getLogger("com.foo.Bar");
+        String msg = "Async logger msg with location";
+        log.info(msg);
+        ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+        BufferedReader reader = new BufferedReader(new FileReader(f));
+        String line1 = reader.readLine();
+        reader.close();
+        f.delete();
+        assertNotNull("line1", line1);
+        assertTrue("line1 correct", line1.contains(msg));
+
+        String location = "testAsyncLogWritesToLog";
+        assertTrue("has location", line1.contains(location));
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,54 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+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.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.apache.logging.log4j.core.helpers.Constants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AsyncLoggerTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
+                AsyncLoggerContextSelector.class.getName());
+        System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+                "AsyncLoggerTest.xml");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, "");
+    }
+
+    @Test
+    public void testAsyncLogWritesToLog() throws Exception {
+        File f = new File("AsyncLoggerTest.log");
+        // System.out.println(f.getAbsolutePath());
+        f.delete();
+        Logger log = LogManager.getLogger("com.foo.Bar");
+        String msg = "Async logger msg";
+        log.info(msg);
+        ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+        BufferedReader reader = new BufferedReader(new FileReader(f));
+        String line1 = reader.readLine();
+        reader.close();
+        f.delete();
+        assertNotNull("line1", line1);
+        assertTrue("line1 correct", line1.contains(msg));
+
+        String location = "testAsyncLogWritesToLog";
+        assertTrue("no location", !line1.contains(location));
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,30 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class CachedClockTest {
+
+    @Test
+    public void testLessThan17Millis() {
+        long millis1 = CachedClock.instance().currentTimeMillis();
+        long sysMillis = System.currentTimeMillis();
+        
+        long diff = sysMillis - millis1;
+        
+        assertTrue("diff too large: " + diff, diff <= 16);
+    }
+
+    @Test
+    public void testAfterWaitStillLessThan17Millis() throws Exception {
+        Thread.sleep(100);
+        long millis1 = CachedClock.instance().currentTimeMillis();
+        long sysMillis = System.currentTimeMillis();
+        
+        long diff = sysMillis - millis1;
+        
+        assertTrue("diff too large: " + diff, diff <= 16);
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/ClockFactoryTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/ClockFactoryTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/ClockFactoryTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/ClockFactoryTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,64 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class ClockFactoryTest {
+
+    @Test
+    public void testDefaultIsSystemClock() {
+        System.clearProperty(ClockFactory.PROPERTY_NAME);
+        assertEquals(SystemClock.class, ClockFactory.getClock().getClass());
+    }
+
+    @Test
+    public void testSpecifySystemClockShort() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, "SystemClock");
+        assertEquals(SystemClock.class, ClockFactory.getClock().getClass());
+    }
+
+    @Test
+    public void testSpecifySystemClockLong() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, SystemClock.class.getName());
+        assertEquals(SystemClock.class, ClockFactory.getClock().getClass());
+    }
+
+    @Test
+    public void testSpecifyCachedClockShort() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, "CachedClock");
+        assertEquals(CachedClock.class, ClockFactory.getClock().getClass());
+    }
+
+    @Test
+    public void testSpecifyCachedClockLong() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, CachedClock.class.getName());
+        assertEquals(CachedClock.class, ClockFactory.getClock().getClass());
+    }
+
+    @Test
+    public void testSpecifyCoarseCachedClockShort() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, "CoarseCachedClock");
+        assertEquals(CoarseCachedClock.class, ClockFactory.getClock().getClass());
+    }
+
+    @Test
+    public void testSpecifyCoarseCachedClockLong() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, CoarseCachedClock.class.getName());
+        assertEquals(CoarseCachedClock.class, ClockFactory.getClock().getClass());
+    }
+
+    static class MyClock implements Clock {
+        @Override
+        public long currentTimeMillis() {
+            return 42;
+        }
+    }
+    
+    @Test
+    public void testCustomClock() {
+        System.setProperty(ClockFactory.PROPERTY_NAME, MyClock.class.getName());
+        assertEquals(MyClock.class, ClockFactory.getClock().getClass());
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,30 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class SystemClockTest {
+
+    @Test
+    public void testLessThan2Millis() {
+        long millis1 = new SystemClock().currentTimeMillis();
+        long sysMillis = System.currentTimeMillis();
+        
+        long diff = sysMillis - millis1;
+        
+        assertTrue("diff too large: " + diff, diff <= 1);
+    }
+
+    @Test
+    public void testAfterWaitStillLessThan2Millis() throws Exception {
+        Thread.sleep(100);
+        long millis1 = new SystemClock().currentTimeMillis();
+        long sysMillis = System.currentTimeMillis();
+        
+        long diff = sysMillis - millis1;
+        
+        assertTrue("diff too large: " + diff, diff <= 1);
+    }
+
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+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.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastFileAppenderLocationTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+                "FastFileAppenderLocationTest.xml");
+    }
+
+    @Test
+    public void testLocationIncluded() throws Exception {
+        File f = new File("FastFileAppenderLocationTest.log");
+        // System.out.println(f.getAbsolutePath());
+        f.delete();
+        Logger log = LogManager.getLogger("com.foo.Bar");
+        String msg = "Message with location, flushed with immediate flush=false";
+        log.info(msg);
+        ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+        BufferedReader reader = new BufferedReader(new FileReader(f));
+        String line1 = reader.readLine();
+        reader.close();
+        f.delete();
+        assertNotNull("line1", line1);
+        assertTrue("line1 correct", line1.contains(msg));
+
+        String location = "testLocationIncluded";
+        assertTrue("has location", line1.contains(location));
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+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.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastFileAppenderTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+                "FastFileAppenderTest.xml");
+    }
+
+    @Test
+    public void testFlushAtEndOfBatch() throws Exception {
+        File f = new File("FastFileAppenderTest.log");
+        // System.out.println(f.getAbsolutePath());
+        f.delete();
+        Logger log = LogManager.getLogger("com.foo.Bar");
+        String msg = "Message flushed with immediate flush=false";
+        log.info(msg);
+        ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+        BufferedReader reader = new BufferedReader(new FileReader(f));
+        String line1 = reader.readLine();
+        reader.close();
+        f.delete();
+        assertNotNull("line1", line1);
+        assertTrue("line1 correct", line1.contains(msg));
+
+        String location = "testFlushAtEndOfBatch";
+        assertTrue("no location", !line1.contains(location));
+    }
+}

Added: logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java?rev=1463078&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java (added)
+++ logging/log4j/log4j2/trunk/log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java Mon Apr  1 03:51:21 2013
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+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.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastRollingFileAppenderLocationTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+                "FastRollingFileAppenderLocationTest.xml");
+    }
+
+    @Test
+    public void testLocationIncluded() throws Exception {
+        File f = new File("FastRollingFileAppenderLocationTest.log");
+        // System.out.println(f.getAbsolutePath());
+        f.delete();
+        Logger log = LogManager.getLogger("com.foo.Bar");
+        String msg = "Message with location, flushed with immediate flush=false";
+        log.info(msg);
+        ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+        BufferedReader reader = new BufferedReader(new FileReader(f));
+        String line1 = reader.readLine();
+        reader.close();
+        f.delete();
+        assertNotNull("line1", line1);
+        assertTrue("line1 correct", line1.contains(msg));
+
+        String location = "testLocationIncluded";
+        assertTrue("has location", line1.contains(location));
+    }
+}