Index: pom.xml
===================================================================
--- pom.xml (revision 1462791)
+++ pom.xml (working copy)
@@ -115,8 +115,8 @@
+ * For each {@code Property} key in the map, the value is {@code true} if
+ * the property value has a variable that needs to be substituted.
+ *
+ * @return an unmodifiable map with the configuration properties, or
+ * {@code null}
+ * @see Configuration#getSubst()
+ * @see StrSubstitutor
+ */
+ // LOG4J2-157
+ public Map
+ * To use AsyncLogger, specify the System property
+ * -DLog4jContextSelector=log4j.async.AsyncLoggerContextSelector before you
+ * obtain a Logger, and all Loggers returned by LogManager.getLogger will be
+ * AsyncLoggers.
+ *
+ * Note that for performance reasons, this logger does not support source
+ * location. Any %class, %location or %line conversion patterns in your
+ * log4j.xml configuration will produce either a "?" character or no output at
+ * all.
+ *
+ * For best performance, use AsyncLogger with the FastFileAppender or
+ * FastRollingFileAppender, with immediateFlush=false. These appenders have
+ * built-in support for the batching mechanism used by the Disruptor library,
+ * and they will flush to disk at the end of each batch. This means that even
+ * with immediateFlush=false, there will never be any items left in the buffer;
+ * all log events will all be written to disk in a very efficient manner.
+ *
+ * @author Remko Popma
+ */
+public class AsyncLogger extends Logger { // depends on LOG4J2-151
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+ private static volatile Disruptor
+ * It does not fill in a stack trace for performance reasons.
+ */
+@SuppressWarnings("serial")
+public class AlertException extends Exception
+{
+ /** Pre-allocated exception to avoid garbage generation */
+ public static final AlertException INSTANCE = new AlertException();
+
+ /**
+ * Private constructor so only a single instance exists.
+ */
+ private AlertException()
+ {
+ }
+
+ /**
+ * Overridden so the stack trace is not filled in for this exception for performance reasons.
+ *
+ * @return this instance.
+ */
+ @Override
+ public Throwable fillInStackTrace()
+ {
+ return this;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BatchEventProcessor.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BatchEventProcessor.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BatchEventProcessor.java (working copy)
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+/**
+ * Convenience class for handling the batching semantics of consuming entries from a {@link RingBuffer}
+ * and delegating the available events to an {@link EventHandler}.
+ *
+ * If the {@link EventHandler} also implements {@link LifecycleAware} it will be notified just after the thread
+ * is started and just before the thread is shutdown.
+ *
+ * @param Exception thrown when the it is not possible to insert a value into
+ * the ring buffer without it wrapping the consuming sequenes. Used
+ * specifically when claiming with the {@link RingBuffer#tryNext()} call.
+ *
+ * For efficiency this exception will not have a stack trace.
+ * @author mikeb01
+ *
+ */
+@SuppressWarnings("serial")
+public class InsufficientCapacityException extends Exception
+{
+ public static final InsufficientCapacityException INSTANCE = new InsufficientCapacityException();
+
+ private InsufficientCapacityException()
+ {
+ // Singleton
+ }
+
+ @Override
+ public synchronized Throwable fillInStackTrace()
+ {
+ return this;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/LifecycleAware.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/LifecycleAware.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/LifecycleAware.java (working copy)
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+/**
+ * Implement this interface in your {@link EventHandler} to be notified when a thread for the
+ * {@link BatchEventProcessor} starts and shuts down. Get the event for a given sequence in the RingBuffer. This method will wait until the
+ * value is published before returning. This method should only be used by {@link EventProcessor}s
+ * that are reading values out of the ring buffer. Publishing code should use the
+ * {@link RingBuffer#getPreallocated(long)} call to get a handle onto the preallocated event.
+ *
+ * The call implements the appropriate load fence to ensure that the data within the event
+ * is visible after this call completes.
+ *
+ * @param sequence for the event
+ * @return the event that visibily published by the producer
+ */
+ @SuppressWarnings("unchecked")
+ public E getPublished(long sequence)
+ {
+ publisher.ensureAvailable(sequence);
+ return (E)entries[(int)sequence & indexMask];
+ }
+
+ /**
+ * Increment and return the next sequence for the ring buffer. Calls of this
+ * method should ensure that they always publish the sequence afterward. E.g.
+ * Increment and return the next sequence for the ring buffer. Calls of this
+ * method should ensure that they always publish the sequence afterward. E.g.
+ * This method will not block if there is not space available in the ring
+ * buffer, instead it will throw an {@link InsufficientCapacityException}.
+ *
+ *
+ * @see RingBuffer#publish(long)
+ * @see RingBuffer#getPreallocated(long)
+ * @return The next sequence to publish to.
+ * @throws InsufficientCapacityException
+ */
+ public long tryNext() throws InsufficientCapacityException
+ {
+ return sequencer.tryNext(gatingSequences);
+ }
+
+ /**
+ * Resets the cursor to a specific value. This can be applied at any time, but it is worth not
+ * that it is a racy thing to do and should only be used in controlled circumstances. E.g. during
+ * initialisation.
+ *
+ * @param sequence The sequence to reset too.
+ * @throws IllegalStateException If any gating sequences have already been specified.
+ */
+ public void resetTo(long sequence)
+ {
+ sequencer.claim(sequence);
+ publisher.publish(sequence);
+ }
+
+ /**
+ * Sets the cursor to a specific sequence and returns the preallocated entry that is stored there. This
+ * is another deliberatly racy call, that should only be done in controlled circumstances, e.g. initialisation.
+ *
+ * @param sequence The sequence to claim.
+ * @return The preallocated event.
+ */
+ public E claimAndGetPreallocated(long sequence)
+ {
+ sequencer.claim(sequence);
+ return getPreallocated(sequence);
+ }
+
+ /**
+ * Determines if a particular entry has been published.
+ *
+ * @param sequence The sequence to identify the entry.
+ * @return If the value has been published or not.
+ */
+ public boolean isPublished(long sequence)
+ {
+ return publisher.isAvailable(sequence);
+ }
+
+ /**
+ * Add the specified gating sequences to this instance of the Disruptor. They will
+ * safely and atomically added to the list of gating sequences.
+ *
+ * @param gatingSequences The sequences to add.
+ */
+ public void addGatingSequences(Sequence... gatingSequences)
+ {
+ SequenceGroups.addSequences(this, SEQUENCE_UPDATER, this, gatingSequences);
+ }
+
+ /**
+ * Get the minimum sequence value from all of the gating sequences
+ * added to this ringBuffer.
+ *
+ * @return The minimum gating sequence or the cursor sequence if
+ * no sequences have been added.
+ */
+ public long getMinimumGatingSequence()
+ {
+ return Util.getMinimumSequence(gatingSequences, cursor.get());
+ }
+
+ /**
+ * Remove the specified sequence from this ringBuffer.
+ *
+ * @param sequence to be removed.
+ * @return true if this sequence was found, false otherwise.
+ */
+ public boolean removeGatingSequence(Sequence sequence)
+ {
+ return SequenceGroups.removeSequence(this, SEQUENCE_UPDATER, sequence);
+ }
+
+ /**
+ * Create a new SequenceBarrier to be used by an EventProcessor to track which messages
+ * are available to be read from the ring buffer given a list of sequences to track.
+ *
+ * @see SequenceBarrier
+ * @param sequencesToTrack
+ * @return A sequence barrier that will track the specified sequences.
+ */
+ public SequenceBarrier newBarrier(Sequence... sequencesToTrack)
+ {
+ return new ProcessingSequenceBarrier(waitStrategy, cursor, sequencesToTrack);
+ }
+
+ /**
+ * Get the current cursor value for the ring buffer. The cursor value is
+ * the last value that was published, or the highest available sequence
+ * that can be consumed.
+ */
+ public final long getCursor()
+ {
+ return cursor.get();
+ }
+
+ /**
+ * The size of the buffer.
+ */
+ public int getBufferSize()
+ {
+ return bufferSize;
+ }
+
+ /**
+ * Given specified requiredCapacity determines if that amount of space
+ * is available. Note, you can not assume that if this method returns true
+ * that a call to {@link RingBuffer#next()} will not block. Especially true if this
+ * ring buffer is set up to handle multiple producers.
+ *
+ * @param requiredCapacity The capacity to check for.
+ * @return true If the specified requiredCapacity is available
+ * false if now.
+ */
+ public boolean hasAvailableCapacity(int requiredCapacity)
+ {
+ return sequencer.hasAvailableCapacity(gatingSequences, requiredCapacity);
+ }
+
+
+ /**
+ * Publishes an event to the ring buffer. It handles
+ * claiming the next sequence, getting the current (uninitialised)
+ * event from the ring buffer and publishing the claimed sequence
+ * after translation.
+ *
+ * @param translator The user specified translation for the event
+ */
+ public void publishEvent(EventTranslator Concurrent sequence class used for tracking the progress of
+ * the ring buffer and event processors. Support a number
+ * of concurrent operations including CAS and order writes.
+ *
+ * Also attempts to be more efficient with regards to false
+ * sharing by adding padding around the volatile field.
+ */
+public class Sequence
+{
+ static final long INITIAL_VALUE = -1L;
+ private static final Unsafe unsafe;
+ private static final long valueOffset;
+
+ static
+ {
+ unsafe = Util.getUnsafe();
+ final int base = unsafe.arrayBaseOffset(long[].class);
+ final int scale = unsafe.arrayIndexScale(long[].class);
+ valueOffset = base + (scale * 7);
+ }
+
+ private final long[] paddedValue = new long[15];
+
+ /**
+ * Create a sequence initialised to -1.
+ */
+ public Sequence()
+ {
+ this(INITIAL_VALUE);
+ }
+
+ /**
+ * Create a sequence with a specified initial value.
+ *
+ * @param initialValue The initial value for this sequence.
+ */
+ public Sequence(final long initialValue)
+ {
+ unsafe.putOrderedLong(paddedValue, valueOffset, initialValue);
+ }
+
+ /**
+ * Perform a volatile read of this sequence's value.
+ *
+ * @return The current value of the sequence.
+ */
+ public long get()
+ {
+ return unsafe.getLongVolatile(paddedValue, valueOffset);
+ }
+
+ /**
+ * Perform an ordered write of this sequence. The intent is
+ * a Store/Store barrier between this write and any previous
+ * store.
+ *
+ * @param value The new value for the sequence.
+ */
+ public void set(final long value)
+ {
+ unsafe.putOrderedLong(paddedValue, valueOffset, value);
+ }
+
+ /**
+ * Performs a volatile write of this sequence. The intent is
+ * a Store/Store barrier between this write and any previous
+ * write and a Store/Load barrier between this write and any
+ * subsequent volatile read.
+ *
+ * @param value The new value for the sequence.
+ */
+ public void setVolatile(final long value)
+ {
+ unsafe.putLongVolatile(paddedValue, valueOffset, value);
+ }
+
+ /**
+ * Perform a compare and set operation on the sequence.
+ *
+ * @param expectedValue The expected current value.
+ * @param newValue The value to update to.
+ * @return true if the operation succeeds, false otherwise.
+ */
+ public boolean compareAndSet(final long expectedValue, final long newValue)
+ {
+ return unsafe.compareAndSwapLong(paddedValue, valueOffset, expectedValue, newValue);
+ }
+
+ /**
+ * Atomically increment the sequence by one.
+ *
+ * @return The value after the increment
+ */
+ public long incrementAndGet()
+ {
+ return addAndGet(1L);
+ }
+
+ /**
+ * Atomically add the supplied value.
+ *
+ * @param increment The value to add to the sequence.
+ * @return The value after the increment.
+ */
+ public long addAndGet(final long increment)
+ {
+ long currentValue;
+ long newValue;
+
+ do
+ {
+ currentValue = get();
+ newValue = currentValue + increment;
+ }
+ while (!compareAndSet(currentValue, newValue));
+
+ return newValue;
+ }
+
+ public String toString()
+ {
+ return Long.toString(get());
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceBarrier.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceBarrier.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceBarrier.java (working copy)
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+
+/**
+ * Coordination barrier for tracking the cursor for publishers and sequence of
+ * dependent {@link EventProcessor}s for processing a data structure
+ */
+public interface SequenceBarrier
+{
+ /**
+ * Wait for the given sequence to be available for consumption.
+ *
+ * @param sequence to wait for
+ * @return the sequence up to which is available
+ * @throws AlertException if a status change has occurred for the Disruptor
+ * @throws InterruptedException if the thread needs awaking on a condition variable.
+ */
+ long waitFor(long sequence) throws AlertException, InterruptedException;
+
+ /**
+ * Get the current cursor value that can be read.
+ *
+ * @return value of the cursor for entries that have been published.
+ */
+ long getCursor();
+
+ /**
+ * The current alert status for the barrier.
+ *
+ * @return true if in alert otherwise false.
+ */
+ boolean isAlerted();
+
+ /**
+ * Alert the {@link EventProcessor}s of a status change and stay in this status until cleared.
+ */
+ void alert();
+
+ /**
+ * Clear the current alert status.
+ */
+ void clearAlert();
+
+ /**
+ * Check if an alert has been raised and throw an {@link AlertException} if it has.
+ *
+ * @throws AlertException if alert has been raised.
+ */
+ void checkAlert() throws AlertException;
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceGroup.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceGroup.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceGroup.java (working copy)
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+
+/**
+ * A {@link Sequence} group that can dynamically have {@link Sequence}s added and removed while being
+ * thread safe.
+ *
+ * The {@link SequenceGroup#get()} and {@link SequenceGroup#set(long)} methods are lock free and can be
+ * concurrently be called with the {@link SequenceGroup#add(Sequence)} and {@link SequenceGroup#remove(Sequence)}.
+ */
+public final class SequenceGroup extends Sequence
+{
+ private static final AtomicReferenceFieldUpdater
+ * Typically this would be used when the handler is performing some sort of batching operation such as writing to an IO
+ * device; after the operation has completed, the implementation should call {@link Sequence#set} to update the
+ * sequence and allow other processes that are dependent on this handler to progress.
+ *
+ * @param
+ The Disruptor is a concurrent programming framework for exchanging and coordinating work as a continuous series of events.
+ It can be used as an alternative to wiring processing stages together via queues. The Disruptor design has the
+ characteristics of generating significantly less garbage than queues and separates the concurrency concerns so
+ non-locking algorithms can be employed resulting in greater scalability and performance.
+
+ It works on the principle of having a number of stages that are each single threaded with local state and memory.
+ No global memory exists and all communication is achieved by passing messages/state via managed ring buffers.
+
+ Almost any graph or pipeline structure can be composed via one or more Disruptor patterns.
+
+ UniCast a series of items between 1 publisher and 1 EventProcessor.
+
+ Sequence a series of messages from multiple publishers
+
+ Pipeline a series of messages
+
+ Multicast a series of messages to multiple EventProcessors
+
+ Replicate a message then fold back the results
+ A simple example of setting up the disruptor with two event handlers that must process events in order: This method can be used as the start of a chain. For example if the handler This method will not shutdown the executor, nor will it await the final termination of the
+ * processor threads. This method is generally used as part of a chain. For example if the handler This method is generally used as part of a chain. For example if the handler This method is generally used as part of a chain. For example if the handler This method is generally used as part of a chain. For example if the handler
+ * 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));
+ }
+
+ /**
+ * Get the minimum sequence from an array of {@link org.apache.logging.log4j.async.com.lmax.disruptor.Sequence}s.
+ *
+ * @param sequences to compare.
+ * @return the minimum sequence found or Long.MAX_VALUE if the array is empty.
+ */
+ public static long getMinimumSequence(final Sequence[] sequences)
+ {
+ return getMinimumSequence(sequences, Long.MAX_VALUE);
+ }
+
+ /**
+ * Get the minimum sequence from an array of {@link org.apache.logging.log4j.async.com.lmax.disruptor.Sequence}s.
+ *
+ * @param sequences to compare.
+ * @param minimum an initial default minimum. If the array is empty this value will be
+ * returned.
+ * @return the minimum sequence found or Long.MAX_VALUE if the array is empty.
+ */
+ public static long getMinimumSequence(final Sequence[] sequences, long minimum)
+ {
+ for (int i = 0, n = sequences.length; i < n; i++)
+ {
+ long value = sequences[i].get();
+ minimum = Math.min(minimum, value);
+ }
+
+ return minimum;
+ }
+
+ /**
+ * Get an array of {@link Sequence}s for the passed {@link EventProcessor}s
+ *
+ * @param processors for which to get the sequences
+ * @return the array of {@link Sequence}s
+ */
+ public static Sequence[] getSequencesFor(final EventProcessor... processors)
+ {
+ Sequence[] sequences = new Sequence[processors.length];
+ for (int i = 0; i < sequences.length; i++)
+ {
+ sequences[i] = processors[i].getSequence();
+ }
+
+ return sequences;
+ }
+
+ private static final Unsafe THE_UNSAFE;
+ static
+ {
+ try
+ {
+ final PrivilegedExceptionAction
+ Log4j-async uses LMAX Disruptor
+ technology to significantly improve the performance of
+ asynchronous logging, especially in multi-threaded scenarios.
+
+ Log4j-async requires at least Java 6 and is dependent on the Log4j 2 API and implementation.
+
+ Include this jar, the Log4j 2 API and implementation jar together. Configure
+ the logging implementation as required.
+
+ When
+ AsyncLoggerContextSelector is used to make
+ all loggers asynchronous, make sure to use normal
+ <root> and
+ <logger> elements
+ in the configuration. The
+ AsyncLoggerContextSelector
+ will ensure that all loggers are asynchronous, using a mechanism
+ that is different from what happens when you configure
+ <asyncRoot> or
+ <asyncLogger>.
+ The latter elements are intended for mixing async with sync
+ loggers. If you use both mechanisms together you will end
+ up with two
+ background threads, where your application passes the log
+ message to
+ thread A, which passes the message to thread B,
+ which then finally
+ logs the message to disk. This works, but
+ there will be an unnecessary
+ step in the middle.
+
+ There are a few system properties
+ you can use to control
+ aspects of the asynchronous logging subsystem.
+ Some of these can be used to
+ tune logging performance.
+
+ Synchronous and asynchronous loggers can be combined in
+ configuration. This gives you more flexibility at the cost
+ of a
+ slight loss in performance (compared to making
+ all loggers asynchronous). Use the
+ <asyncRoot>
+ or
+ <asyncLogger>
+ configuration elements to specify the loggers that need to
+ be
+ asynchronous. The same configuration file can also
+ contain
+ <root>
+ and
+ <logger>
+ elements for the synchronous loggers.
+
+ There is no need to set the system property
+ "Log4jContextSelector" to any value.
+
+ By default, location is not passed to the I/O thread by
+ asynchronous loggers. If one of your layouts or custom
+ filters needs location information, you need to set
+ "includeLocation=true"
+ in the configuration of all relevant loggers,
+ including the root logger.
+
+ A configuration that mixes asynchronous loggers might look like:
+
+ There are a few system properties
+ you can use to control
+ aspects of the asynchronous logging subsystem.
+ Some of these can be used to
+ tune logging performance.
+
+ If one of the layouts is
+ configured with a location-related attribute like %line,
+ %location, %class or %method, Log4j will take a snapshot of the
+ stack, and walk the stack trace to find the location information.
+ This is an expensive operation: 1.3 - 5 times slower for
+ synchronous loggers. Synchronous loggers wait as
+ long as possible before they take this stack snapshot. If no
+ location is required, the snapshot will never be taken. However,
+ asynchronous loggers need to make this decision before passing the
+ log message to another thread; the location information will be
+ lost after that point.
+
+ The performance impact of taking a stack trace snapshot is even
+ higher for asynchronous loggers: logging with location is
+ 4 - 20 times slower than without location.
+ For this reason, asynchronous loggers and asynchronous
+ appenders do not include location information by default.
+
+ You can override the default behaviour in your logger
+ or asynchronous appender configuration
+ by specifying
+ includeLocation="true".
+
+
+ The FastFileAppender is similar to the standard
+ FileAppender
+ except it is always buffered (this cannot be switched off)
+ and internally it uses a
+ ByteBuffer + RandomAccessFile
+ instead of a
+ BufferedOutputStream.
+ We saw a 10-30% performance improvement compared to
+ FileAppender with "bufferedIO=true" in our
+ measurements.
+ Similar to the FileAppender,
+ FastFileAppender uses a FastFileManager to actually perform the
+ file I/O. While FastFileAppender
+ from different Configurations
+ cannot be shared, the FastFileManagers can be if the Manager is
+ accessible. For example, two webapps in a
+ servlet container can have
+ their own configuration and safely
+ write to the same file if Log4j
+ is in a ClassLoader that is common to
+ both of them.
+
+ Here is a sample FastFile configuration:
+
+ AsynchAppender createAppender(
- @PluginElement("appender-ref") final AppenderRef[] appenderRefs,
- @PluginAttr("error-ref") final String errorRef,
- @PluginAttr("blocking") final String blocking,
- @PluginAttr("bufferSize") final String size,
- @PluginAttr("name") final String name,
- @PluginElement("filter") final Filter filter,
- @PluginConfiguration final Configuration config,
- @PluginAttr("suppressExceptions") final String suppress) {
+ @PluginElement("appender-ref") final AppenderRef[] appenderRefs,
+ @PluginAttr("error-ref") final String errorRef,
+ @PluginAttr("blocking") final String blocking,
+ @PluginAttr("bufferSize") final String size,
+ @PluginAttr("name") final String name,
+ @PluginAttr("includeLocation") final String includeLocation,
+ @PluginElement("filter") final Filter filter,
+ @PluginConfiguration final Configuration config,
+ @PluginAttr("suppressExceptions") final String suppress) {
if (name == null) {
LOGGER.error("No name provided for AsynchAppender");
return null;
@@ -170,11 +175,13 @@
final boolean isBlocking = blocking == null ? true : Boolean.valueOf(blocking);
final int queueSize = size == null ? DEFAULT_QUEUE_SIZE : Integer.parseInt(size);
+ final boolean isIncludeLocation = includeLocation == null ? false :
+ Boolean.parseBoolean(includeLocation);
final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
- return new AsynchAppender(name, filter, appenderRefs, errorRef, queueSize, isBlocking, handleExceptions,
- config);
+ return new AsynchAppender(name, filter, appenderRefs, errorRef,
+ queueSize, isBlocking, handleExceptions, config, isIncludeLocation);
}
/**
@@ -206,8 +213,9 @@
continue;
}
final Log4jLogEvent event = Log4jLogEvent.deserialize(s);
+ event.setEndOfBatch(queue.isEmpty());
boolean success = false;
- for (final AppenderControl control : appenders) {
+ for (final AppenderControl> control : appenders) {
try {
control.callAppender(event);
success = true;
@@ -229,7 +237,7 @@
Serializable s = queue.take();
if (s instanceof Log4jLogEvent) {
final Log4jLogEvent event = Log4jLogEvent.deserialize(s);
- for (final AppenderControl control : appenders) {
+ for (final AppenderControl> control : appenders) {
control.callAppender(event);
}
}
Index: core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
===================================================================
--- core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java (revision 1462791)
+++ core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java (working copy)
@@ -25,9 +25,11 @@
import org.apache.logging.log4j.core.LifeCycle;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.core.helpers.Constants;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.LogEventFactory;
+import org.apache.logging.log4j.core.lookup.StrSubstitutor;
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.PluginFactory;
@@ -38,6 +40,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -61,13 +64,13 @@
private LogEventFactory logEventFactory;
private Level level;
private boolean additive = true;
+ private boolean includeLocation = true;
private LoggerConfig parent;
private final AtomicInteger counter = new AtomicInteger();
private boolean shutdown = false;
private final Map
+
+
-
+ * long sequence = ringBuffer.next();
+ * try {
+ * Event e = ringBuffer.getPreallocated(sequence);
+ * // Do some work with the event.
+ * } finally {
+ * ringBuffer.publish(sequence);
+ * }
+ *
+ * @see RingBuffer#publish(long)
+ * @see RingBuffer#getPreallocated(long)
+ * @return The next sequence to publish to.
+ */
+ public long next()
+ {
+ return sequencer.next(gatingSequences);
+ }
+
+ /**
+ *
+ * long sequence = ringBuffer.next();
+ * try {
+ * Event e = ringBuffer.getPreallocated(sequence);
+ * // Do some work with the event.
+ * } finally {
+ * ringBuffer.publish(sequence);
+ * }
+ *
+ * requiredCapacity slots
+ * available.
+ * @param gatingSequences to gate on
+ *
+ * @return the claimed sequence value
+ * @throws InsufficientCapacityException
+ */
+ long tryNext(Sequence[] gatingSequences) throws InsufficientCapacityException;
+
+ /**
+ * Get the remaining capacity for this sequencer.
+ * @param gatingSequences to gate on
+ *
+ * @return The number of slots remaining.
+ */
+ long remainingCapacity(Sequence[] gatingSequences);
+
+ /**
+ * Claim a specific sequence. Only used if initialising the ring buffer to
+ * a specific value.
+ *
+ * @param sequence The sequence to initialise too.
+ */
+ void claim(long sequence);
+}
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleProducerPublisher.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleProducerPublisher.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleProducerPublisher.java (working copy)
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+/**
+ * A Publisher optimised for publishing events from a single thread.
+ *
+ * Generally not safe for use from multiple threads as it does not implement any barriers.
+ */
+class SingleProducerPublisher implements Publisher
+{
+ private final WaitStrategy waitStrategy;
+ private final Sequence cursor = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
+
+ public SingleProducerPublisher(WaitStrategy waitStrategy)
+ {
+ this.waitStrategy = waitStrategy;
+ }
+
+ @Override
+ public void publish(long sequence)
+ {
+ cursor.set(sequence);
+ waitStrategy.signalAllWhenBlocking();
+ }
+
+ @Override
+ public void ensureAvailable(long sequence)
+ {
+ }
+
+ @Override
+ public boolean isAvailable(long sequence)
+ {
+ return sequence <= cursor.get();
+ }
+
+ Sequence getCursorSequence()
+ {
+ return cursor;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleProducerSequencer.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleProducerSequencer.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleProducerSequencer.java (working copy)
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+import static org.apache.logging.log4j.async.com.lmax.disruptor.util.Util.*;
+
+import java.util.concurrent.locks.LockSupport;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+
+
+/**
+ * Coordinator for claiming sequences for access to a data structure while tracking dependent {@link Sequence}s.
+ *
+ * Generally not safe for use from multiple threads as it does not implement any barriers.
+ */
+class SingleProducerSequencer implements Sequencer
+{
+ @SuppressWarnings("unused")
+ private final WaitStrategy waitStrategy;
+ private final int bufferSize;
+
+ @SuppressWarnings("unused")
+ private static class Padding
+ {
+ /** Set to -1 as sequence starting point */
+ public long nextValue = Sequence.INITIAL_VALUE, cachedValue = Sequence.INITIAL_VALUE, p2, p3, p4, p5, p6, p7;
+ }
+
+ private final Padding pad = new Padding();
+
+ /**
+ * Construct a Sequencer with the selected wait strategy and buffer size.
+ *
+ * @param bufferSize the size of the buffer that this will sequence over.
+ * @param waitStrategy for those waiting on sequences.
+ */
+ public SingleProducerSequencer(int bufferSize, final WaitStrategy waitStrategy)
+ {
+ this.bufferSize = bufferSize;
+ this.waitStrategy = waitStrategy;
+ }
+
+ @Override
+ public int getBufferSize()
+ {
+ return bufferSize;
+ }
+
+ long getNextValue()
+ {
+ return pad.nextValue;
+ }
+
+ @Override
+ public boolean hasAvailableCapacity(Sequence[] gatingSequences, final int requiredCapacity)
+ {
+ long nextValue = pad.nextValue;
+
+ long wrapPoint = (nextValue + requiredCapacity) - bufferSize;
+ long cachedGatingSequence = pad.cachedValue;
+
+ if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
+ {
+ long minSequence = getMinimumSequence(gatingSequences, nextValue);
+ pad.cachedValue = minSequence;
+
+ if (wrapPoint > minSequence)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public long next(Sequence[] gatingSequences)
+ {
+ long nextValue = pad.nextValue;
+
+ long nextSequence = nextValue + 1;
+ long wrapPoint = nextSequence - bufferSize;
+ long cachedGatingSequence = pad.cachedValue;
+
+ if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
+ {
+ long minSequence;
+ while (wrapPoint > (minSequence = getMinimumSequence(gatingSequences, nextValue)))
+ {
+ LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
+ }
+
+ pad.cachedValue = minSequence;
+ }
+
+ pad.nextValue = nextSequence;
+
+ return nextSequence;
+ }
+
+ @Override
+ public long tryNext(Sequence[] gatingSequences) throws InsufficientCapacityException
+ {
+ if (!hasAvailableCapacity(gatingSequences, 1))
+ {
+ throw InsufficientCapacityException.INSTANCE;
+ }
+
+ long nextSequence = ++pad.nextValue;
+
+ return nextSequence;
+ }
+
+ @Override
+ public long remainingCapacity(Sequence[] gatingSequences)
+ {
+ long nextValue = pad.nextValue;
+
+ long consumed = Util.getMinimumSequence(gatingSequences, nextValue);
+ long produced = nextValue;
+ return getBufferSize() - (produced - consumed);
+ }
+
+ @Override
+ public void claim(long sequence)
+ {
+ pad.nextValue = sequence;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SleepingWaitStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SleepingWaitStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SleepingWaitStrategy.java (working copy)
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+import java.util.concurrent.locks.LockSupport;
+
+/**
+ * Sleeping strategy that initially spins, then uses a Thread.yield(), and eventually for the minimum number of nanos
+ * the OS and JVM will allow while the {@link org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor}s are waiting on a barrier.
+ *
+ * This strategy is a good compromise between performance and CPU resource. Latency spikes can occur after quiet periods.
+ */
+public final class SleepingWaitStrategy implements WaitStrategy
+{
+ private static final int RETRIES = 200;
+
+ @Override
+ public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)
+ throws AlertException, InterruptedException
+ {
+ long availableSequence;
+ int counter = RETRIES;
+
+ while ((availableSequence = dependentSequence.get()) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+ }
+
+ return availableSequence;
+ }
+
+ @Override
+ public void signalAllWhenBlocking()
+ {
+ }
+
+ private int applyWaitMethod(final SequenceBarrier barrier, int counter)
+ throws AlertException
+ {
+ barrier.checkAlert();
+
+ if (counter > 100)
+ {
+ --counter;
+ }
+ else if (counter > 0)
+ {
+ --counter;
+ Thread.yield();
+ }
+ else
+ {
+ LockSupport.parkNanos(1L);
+ }
+
+ return counter;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutBlockingWaitStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutBlockingWaitStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutBlockingWaitStrategy.java (working copy)
@@ -0,0 +1,78 @@
+package org.apache.logging.log4j.async.com.lmax.disruptor;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.AlertException;
+import org.apache.logging.log4j.async.com.lmax.disruptor.Sequence;
+import org.apache.logging.log4j.async.com.lmax.disruptor.SequenceBarrier;
+import org.apache.logging.log4j.async.com.lmax.disruptor.WaitStrategy;
+
+
+public class TimeoutBlockingWaitStrategy implements WaitStrategy
+{
+ private final Lock lock = new ReentrantLock();
+ private final Condition processorNotifyCondition = lock.newCondition();
+ private final long timeoutInNanos;
+
+ public TimeoutBlockingWaitStrategy(final long timeout, final TimeUnit units)
+ {
+ timeoutInNanos = units.toNanos(timeout);
+ }
+
+ @Override
+ public long waitFor(final long sequence,
+ final Sequence cursorSequence,
+ final Sequence dependentSequence,
+ final SequenceBarrier barrier)
+ throws AlertException, InterruptedException
+ {
+ long nanos = timeoutInNanos;
+
+ long availableSequence;
+ if ((availableSequence = cursorSequence.get()) < sequence)
+ {
+ lock.lock();
+ try
+ {
+ while ((availableSequence = cursorSequence.get()) < sequence)
+ {
+ barrier.checkAlert();
+ nanos = processorNotifyCondition.awaitNanos(nanos);
+ if (nanos <= 0)
+ {
+ return availableSequence;
+ }
+ }
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ while ((availableSequence = dependentSequence.get()) < sequence)
+ {
+ barrier.checkAlert();
+ }
+
+ return availableSequence;
+ }
+
+ @Override
+ public void signalAllWhenBlocking()
+ {
+ lock.lock();
+ try
+ {
+ processorNotifyCondition.signalAll();
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutHandler.java (working copy)
@@ -0,0 +1,6 @@
+package org.apache.logging.log4j.async.com.lmax.disruptor;
+
+public interface TimeoutHandler
+{
+ void onTimeout(long sequence) throws Exception;
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WaitStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WaitStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WaitStrategy.java (working copy)
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+
+/**
+ * Strategy employed for making {@link EventProcessor}s wait on a cursor {@link Sequence}.
+ */
+public interface WaitStrategy
+{
+ /**
+ * Wait for the given sequence to be available. It is possible for this method to return a value
+ * less than the sequence number supplied depending on the implementation of the WaitStrategy. A common
+ * use for this is to signal a timeout. Any EventProcessor that is using a WaitStragegy to get notifications
+ * about message becoming available should remember to handle this case. The {@link BatchEventProcessor} explicitly
+ * handles this case and will signal a timeout if required.
+ *
+ * @param sequence to be waited on.
+ * @param cursor the main sequence from ringbuffer. Wait/notify strategies will
+ * need this as it's the only sequence that is also notified upon update.
+ * @param dependentSequence on which to wait.
+ * @param barrier the processor is waiting on.
+ * @return the sequence that is available which may be greater than the requested sequence.
+ * @throws AlertException if the status of the Disruptor has changed.
+ * @throws InterruptedException if the thread is interrupted.
+ */
+ long waitFor(long sequence, Sequence cursor, Sequence dependentSequence, SequenceBarrier barrier)
+ throws AlertException, InterruptedException;
+
+ /**
+ * Implementations should signal the waiting {@link EventProcessor}s that the cursor has advanced.
+ */
+ void signalAllWhenBlocking();
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkHandler.java (working copy)
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor;
+
+/**
+ * Callback interface to be implemented for processing units of work as they become available in the {@link RingBuffer}.
+ *
+ * @param
+ track to prevent wrap
+ +------------------+
+ | |
+ | v
++----+ +-----+ +----+ +====+ +====+ +-----+
+| P1 |--->| EP1 | | P1 |--->| RB |<---| SB | | EP1 |
++----+ +-----+ +----+ +====+ +====+ +-----+
+ claim get ^ |
+ | |
+ +--------+
+ waitFor
+
+
+ track to prevent wrap
+ +--------------------+
+ | |
+ | v
++----+ +----+ +====+ +====+ +-----+
+| P1 |-------+ | P1 |--->| RB |<---| SB | | EP1 |
++----+ | +----+ +====+ +====+ +-----+
+ v ^ get ^ |
++----+ +-----+ +----+ | | |
+| P2 |--->| EP1 | | P2 |------+ +---------+
++----+ +-----+ +----+ | waitFor
+ ^ |
++----+ | +----+ |
+| P3 |-------+ | P3 |------+
++----+ +----+
+
+
+ +----+ +-----+ +-----+ +-----+
+ | P1 |--->| EP1 |--->| EP2 |--->| EP3 |
+ +----+ +-----+ +-----+ +-----+
+
+
+
+ track to prevent wrap
+ +----------------------------------------------------------------+
+ | |
+ | v
++----+ +====+ +=====+ +-----+ +=====+ +-----+ +=====+ +-----+
+| P1 |--->| RB | | SB1 |<---| EP1 |<---| SB2 |<---| EP2 |<---| SB3 |<---| EP3 |
++----+ +====+ +=====+ +-----+ +=====+ +-----+ +=====+ +-----+
+ claim ^ get | waitFor | waitFor | waitFor
+ | | | |
+ +---------+---------------------+---------------------+
+
+
+ +-----+ track to prevent wrap
+ +----->| EP1 | +--------------------+----------+----------+
+ | +-----+ | | | |
+ | | v v v
++----+ +-----+ +----+ +====+ +====+ +-----+ +-----+ +-----+
+| P1 |--->| EP2 | | P1 |--->| RB |<---| SB | | EP1 | | EP2 | | EP3 |
++----+ +-----+ +----+ +====+ +====+ +-----+ +-----+ +-----+
+ | claim get ^ | | |
+ | +-----+ | | | |
+ +----->| EP3 | +---------+----------+----------+
+ +-----+ waitFor
+
+
+ +-----+ track to prevent wrap
+ +----->| EP1 |-----+ +-------------------------------+
+ | +-----+ | | |
+ | v | v
++----+ +-----+ +----+ +====+ +=====+ +-----+
+| P1 | | EP3 | | P1 |--->| RB |<--------------| SB2 |<---| EP3 |
++----+ +-----+ +----+ +====+ +=====+ +-----+
+ | ^ claim ^ get | waitFor
+ | +-----+ | | |
+ +----->| EP2 |-----+ +=====+ +-----+ |
+ +-----+ | SB1 |<---| EP1 |<-----+
+ +=====+ +-----+ |
+ ^ |
+ | +-----+ |
+ +-------| EP2 |<-----+
+ waitFor +-----+
+
+ Code Example
+
+ // Event holder for data to be exchanged
+ public final class ValueEvent
+ {
+ private long value;
+
+ public long getValue()
+ {
+ return value;
+ }
+
+ public void setValue(final long value)
+ {
+ this.value = value;
+ }
+
+ public final static EventFactory<ValueEvent> EVENT_FACTORY = new EventFactory<ValueEvent>()
+ {
+ public ValueEvent newInstance()
+ {
+ return new ValueEvent();
+ }
+ };
+ }
+
+ // Callback handler which can be implemented by EventProcessors
+ final EventHandler<ValueEvent> eventHandler = new EventHandler<ValueEvent>()
+ {
+ public void onEvent(final ValueEvent event, final long sequence, final boolean endOfBatch)
+ throws Exception
+ {
+ // process a new event as it becomes available.
+ }
+ };
+
+ RingBuffer<ValueEvent> ringBuffer =
+ new RingBuffer<ValueEvent>(ValueEvent.EVENT_FACTORY,
+ new SingleThreadedClaimStrategy(BUFFER_SIZE),
+ new SleepingWaitStrategy());
+
+ SequenceBarrier<ValueEvent> sequenceBarrier = ringBuffer.newBarrier();
+ BatchEventProcessor<ValueEvent> batchProcessor = new BatchEventProcessor<ValueEvent>(sequenceBarrier, eventHandler);
+ ringBuffer.setGatingSequences(batchProcessor.getSequence());
+
+ // Each processor runs on a separate thread
+ EXECUTOR.submit(batchProcessor);
+
+ // Publishers claim events in sequence
+ long sequence = ringBuffer.next();
+ ValueEvent event = ringBuffer.get(sequence);
+
+ event.setValue(1234);
+
+ // publish the event so it is available to EventProcessors
+ ringBuffer.publish(sequence);
+
+
+
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/collections/Histogram.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/collections/Histogram.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/collections/Histogram.java (working copy)
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor.collections;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Arrays;
+
+/**
+ * Histogram for tracking the frequency of observations of values below interval upper bounds.
+ *
+ * This class is useful for recording timings across a large number of observations
+ * when high performance is required.
+ *
+ * The interval bounds are used to define the ranges of the histogram buckets. If provided bounds
+ * are [10,20,30,40,50] then there will be five buckets, accessible by index 0-4. Any value
+ * 0-10 will fall into the first interval bar, values 11-20 will fall into the
+ * second bar, and so on.
+ */
+public final class Histogram
+{
+ // tracks the upper intervals of each of the buckets/bars
+ private final long[] upperBounds;
+ // tracks the count of the corresponding bucket
+ private final long[] counts;
+ // minimum value so far observed
+ private long minValue = Long.MAX_VALUE;
+ // maximum value so far observed
+ private long maxValue = 0L;
+
+ /**
+ * Create a new Histogram with a provided list of interval bounds.
+ *
+ * @param upperBounds of the intervals. Bounds must be provided in order least to greatest, and
+ * lowest bound must be greater than or equal to 1.
+ * @throws IllegalArgumentException if any of the upper bounds are less than or equal to zero
+ * @throws IllegalArgumentException if the bounds are not in order, least to greatest
+ */
+ public Histogram(final long[] upperBounds)
+ {
+ validateBounds(upperBounds);
+
+ this.upperBounds = Arrays.copyOf(upperBounds, upperBounds.length);
+ this.counts = new long[upperBounds.length];
+ }
+
+ /**
+ * Validates the input bounds; used by constructor only.
+ */
+ private void validateBounds(final long[] upperBounds)
+ {
+ long lastBound = -1L;
+ if (upperBounds.length <= 0) {
+ throw new IllegalArgumentException("Must provide at least one interval");
+ }
+ for (final long bound : upperBounds)
+ {
+ if (bound <= 0L)
+ {
+ throw new IllegalArgumentException("Bounds must be positive values");
+ }
+
+ if (bound <= lastBound)
+ {
+ throw new IllegalArgumentException("bound " + bound + " is not greater than " + lastBound);
+ }
+
+ lastBound = bound;
+ }
+ }
+
+ /**
+ * Size of the list of interval bars (ie: count of interval bars).
+ *
+ * @return size of the interval bar list.
+ */
+ public int getSize()
+ {
+ return upperBounds.length;
+ }
+
+ /**
+ * Get the upper bound of an interval for an index.
+ *
+ * @param index of the upper bound.
+ * @return the interval upper bound for the index.
+ */
+ public long getUpperBoundAt(final int index)
+ {
+ return upperBounds[index];
+ }
+
+ /**
+ * Get the count of observations at a given index.
+ *
+ * @param index of the observations counter.
+ * @return the count of observations at a given index.
+ */
+ public long getCountAt(final int index)
+ {
+ return counts[index];
+ }
+
+ /**
+ * Add an observation to the histogram and increment the counter for the interval it matches.
+ *
+ * @param value for the observation to be added.
+ * @return return true if in the range of intervals and successfully added observation; otherwise false.
+ */
+ public boolean addObservation(final long value)
+ {
+ int low = 0;
+ int high = upperBounds.length - 1;
+
+ // do a classic binary search to find the high value
+ while (low < high)
+ {
+ int mid = low + ((high - low) >> 1);
+ if (upperBounds[mid] < value)
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ high = mid;
+ }
+ }
+
+ // if the binary search found an eligible bucket, increment
+ if (value <= upperBounds[high])
+ {
+ counts[high]++;
+ trackRange(value);
+
+ return true;
+ }
+
+ // otherwise value was not found
+ return false;
+ }
+
+ /**
+ * Track minimum and maximum observations
+ *
+ * @see getMin
+ * @see getMax
+ */
+ private void trackRange(final long value)
+ {
+ if (value < minValue)
+ {
+ minValue = value;
+ }
+
+ if (value > maxValue)
+ {
+ maxValue = value;
+ }
+ }
+
+ /**
+ * Add observations from another Histogram into this one.
+ *
+ * Histograms must have the same intervals.
+ *
+ * @param histogram from which to add the observation counts.
+ * @throws IllegalArgumentException if interval count or values do not match exactly
+ */
+ public void addObservations(final Histogram histogram)
+ {
+ // validate the intervals
+ if (upperBounds.length != histogram.upperBounds.length)
+ {
+ throw new IllegalArgumentException("Histograms must have matching intervals");
+ }
+
+ for (int i = 0, size = upperBounds.length; i < size; i++)
+ {
+ if (upperBounds[i] != histogram.upperBounds[i])
+ {
+ throw new IllegalArgumentException("Histograms must have matching intervals");
+ }
+ }
+
+ // increment all of the internal counts
+ for (int i = 0, size = counts.length; i < size; i++)
+ {
+ counts[i] += histogram.counts[i];
+ }
+
+ // refresh the minimum and maximum observation ranges
+ trackRange(histogram.minValue);
+ trackRange(histogram.maxValue);
+ }
+
+ /**
+ * Clear the list of interval counters
+ */
+ public void clear()
+ {
+ maxValue = 0L;
+ minValue = Long.MAX_VALUE;
+
+ for (int i = 0, size = counts.length; i < size; i++)
+ {
+ counts[i] = 0L;
+ }
+ }
+
+ /**
+ * Count total number of recorded observations.
+ *
+ * @return the total number of recorded observations.
+ */
+ public long getCount()
+ {
+ long count = 0L;
+
+ for (int i = 0, size = counts.length; i < size; i++)
+ {
+ count += counts[i];
+ }
+
+ return count;
+ }
+
+ /**
+ * Get the minimum observed value.
+ *
+ * @return the minimum value observed.
+ */
+ public long getMin()
+ {
+ return minValue;
+ }
+
+ /**
+ * Get the maximum observed value.
+ *
+ * @return the maximum of the observed values;
+ */
+ public long getMax()
+ {
+ return maxValue;
+ }
+
+ /**
+ * Calculate the mean of all recorded observations.
+ *
+ * The mean is calculated by summing the mid points of each interval multiplied by the count
+ * for that interval, then dividing by the total count of observations. The max and min are
+ * considered for adjusting the top and bottom bin when calculating the mid point, this
+ * minimises skew if the observed values are very far away from the possible histogram values.
+ *
+ * @return the mean of all recorded observations.
+ */
+ public BigDecimal getMean()
+ {
+ // early exit to avoid divide by zero later
+ if (0L == getCount())
+ {
+ return BigDecimal.ZERO;
+ }
+
+ // precalculate the initial lower bound; needed in the loop
+ long lowerBound = counts[0] > 0L ? minValue : 0L;
+ // use BigDecimal to avoid precision errors
+ BigDecimal total = BigDecimal.ZERO;
+
+ // midpoint is calculated as the average between the lower and upper bound
+ // (after taking into account the min & max values seen)
+ // then, simply multiply midpoint by the count of values at the interval (intervalTotal)
+ // and add to running total (total)
+ for (int i = 0, size = upperBounds.length; i < size; i++)
+ {
+ if (0L != counts[i])
+ {
+ long upperBound = Math.min(upperBounds[i], maxValue);
+ long midPoint = lowerBound + ((upperBound - lowerBound) / 2L);
+
+ BigDecimal intervalTotal = new BigDecimal(midPoint).multiply(new BigDecimal(counts[i]));
+ total = total.add(intervalTotal);
+ }
+
+ // and recalculate the lower bound for the next time around the loop
+ lowerBound = Math.max(upperBounds[i] + 1L, minValue);
+ }
+
+ return total.divide(new BigDecimal(getCount()), 2, RoundingMode.HALF_UP);
+ }
+
+ /**
+ * Calculate the upper bound within which 99% of observations fall.
+ *
+ * @return the upper bound for 99% of observations.
+ */
+ public long getTwoNinesUpperBound()
+ {
+ return getUpperBoundForFactor(0.99d);
+ }
+
+ /**
+ * Calculate the upper bound within which 99.99% of observations fall.
+ *
+ * @return the upper bound for 99.99% of observations.
+ */
+ public long getFourNinesUpperBound()
+ {
+ return getUpperBoundForFactor(0.9999d);
+ }
+
+ /**
+ * Get the interval upper bound for a given factor of the observation population.
+ *
+ * Note this does not get the actual percentile measurement, it only gets the bucket
+ *
+ * @param factor representing the size of the population.
+ * @return the interval upper bound.
+ * @throws IllegalArgumentException if factor < 0.0 or factor > 1.0
+ */
+ public long getUpperBoundForFactor(final double factor)
+ {
+ if (0.0d >= factor || factor >= 1.0d)
+ {
+ throw new IllegalArgumentException("factor must be >= 0.0 and <= 1.0");
+ }
+
+ final long totalCount = getCount();
+ final long tailTotal = totalCount - Math.round(totalCount * factor);
+ long tailCount = 0L;
+
+ // reverse search the intervals ('tailCount' from end)
+ for (int i = counts.length - 1; i >= 0; i--)
+ {
+ if (0L != counts[i])
+ {
+ tailCount += counts[i];
+ if (tailCount >= tailTotal)
+ {
+ return upperBounds[i];
+ }
+ }
+ }
+
+ return 0L;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Histogram{");
+
+ sb.append("min=").append(getMin()).append(", ");
+ sb.append("max=").append(getMax()).append(", ");
+ sb.append("mean=").append(getMean()).append(", ");
+ sb.append("99%=").append(getTwoNinesUpperBound()).append(", ");
+ sb.append("99.99%=").append(getFourNinesUpperBound()).append(", ");
+
+ sb.append('[');
+ for (int i = 0, size = counts.length; i < size; i++)
+ {
+ sb.append(upperBounds[i]).append('=').append(counts[i]).append(", ");
+ }
+
+ if (counts.length > 0)
+ {
+ sb.setLength(sb.length() - 2);
+ }
+ sb.append(']');
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ConsumerInfo.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ConsumerInfo.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ConsumerInfo.java (working copy)
@@ -0,0 +1,22 @@
+package org.apache.logging.log4j.async.com.lmax.disruptor.dsl;
+
+
+import java.util.concurrent.Executor;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.Sequence;
+import org.apache.logging.log4j.async.com.lmax.disruptor.SequenceBarrier;
+
+interface ConsumerInfo
+{
+ Sequence[] getSequences();
+
+ SequenceBarrier getBarrier();
+
+ boolean isEndOfChain();
+
+ void start(Executor executor);
+
+ void halt();
+
+ void markAsUsedInBarrier();
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ConsumerRepository.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ConsumerRepository.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ConsumerRepository.java (working copy)
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2011 LMAX Ltd.
+ *
+ * Licensed 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.com.lmax.disruptor.dsl;
+
+
+import java.util.*;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.*;
+
+/**
+ * Provides a repository mechanism to associate {@link EventHandler}s with {@link EventProcessor}s
+ *
+ * @param T the type of the {@link EventHandler}
+ */
+class ConsumerRepository
+ *
+ * @param DisruptorA must
+ * process events before handler B:
+ *
+ * @param handlers the event handlers that will process events.
+ * @return a {@link EventHandlerGroup} that can be used to chain dependencies.
+ */
+ @SuppressWarnings("varargs")
+ public EventHandlerGroupdw.handleEventsWith(A).then(B);disruptorWizard.handleExceptionsIn(eventHandler).with(exceptionHandler);
+ *
+ * @param eventHandler the event handler to set a different exception handler for.
+ * @return an ExceptionHandlerSetting dsl object - intended to be used by chaining the with method call.
+ */
+ public ExceptionHandlerSetting> handleExceptionsFor(final EventHandlerA must process events before handler B:
+ *
+ *
+ *
+ * @param handlers the event handlers, previously set up with {@link #handleEventsWith(org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler[])},
+ * that will form the barrier for subsequent handlers or processors.
+ * @return an {@link EventHandlerGroup} that can be used to setup a dependency barrier over the specified event handlers.
+ */
+ @SuppressWarnings("varargs")
+ public EventHandlerGroupdw.after(A).handleEventsWith(B);A must
+ * process events before handler B:
+ *
+ * @param handlers the batch handlers that will process events.
+ * @return a {@link EventHandlerGroup} that can be used to set up a event processor barrier over the created event processors.
+ */
+ public EventHandlerGroupdw.handleEventsWith(A).then(B);A must
+ * process events before the worker pool with handlers B, C:
+ *
+ * @param handlers the work handlers that will process events. Each work handler instance will provide an extra thread in the worker pool.
+ * @return a {@link EventHandlerGroup} that can be used to set up a event processor barrier over the created event processors.
+ */
+ public EventHandlerGroupdw.handleEventsWith(A).thenHandleEventsWithWorkerPool(B, C);A must
+ * process events before handler B:
+ *
+ * @param handlers the batch handlers that will process events.
+ * @return a {@link EventHandlerGroup} that can be used to set up a event processor barrier over the created event processors.
+ */
+ public EventHandlerGroupdw.after(A).handleEventsWith(B);A must
+ * process events before the worker pool with handlers B, C:
+ *
+ * @param handlers the work handlers that will process events. Each work handler instance will provide an extra thread in the worker pool.
+ * @return a {@link EventHandlerGroup} that can be used to set up a event processor barrier over the created event processors.
+ */
+ public EventHandlerGroupdw.after(A).handleEventsWithWorkerPool(B, C);
+ *
+ * @param disruptorWizard.handleExceptionsIn(eventHandler).with(exceptionHandler);
+
+
+
+
+
+ System Property
+ Default Value
+ Description
+
+
+ AsyncLogger.ExceptionHandler
+
+ null
+
+
+ Fully qualified name of a class that implements the
+ org.apache.logging.log4j.async.com.lmax.disruptor.ExceptionHandler
+ interface. The class needs to have a public
+ zero-argument
+ constructor. If specified, this class
+ will be notified when an
+ exception occurs while
+ logging the messages.
+
+
+
+ AsyncLogger.RingBufferSize
+ 256 * 1024
+
+
+ Size (number of slots) in the RingBuffer used by the
+ asynchronous logging subsystem.
+ Make this value large enough to
+ deal with bursts
+ of activity. The minimum size is 128.
+ The
+ RingBuffer will
+ be pre-allocated at first use and will never grow
+ or shrink during the life of the system.
+
+
+
+ AsyncLogger.WaitStrategy
+
+ Sleep
+
+
+ Valid values: Block, Sleep, Yield.
+
+
+ Block
+ is a strategy that uses a lock and
+ condition
+ variable for the I/O thread waiting for log events.
+ Block can be used when
+ throughput and low-latency
+ are not as important as CPU resource.
+ Recommended for resource constrained/virtualised environments.
+
+ Sleep
+ is a strategy that initially spins, then
+ uses a Thread.yield(), and
+ eventually parks for the
+ minimum number of nanos the OS and JVM will allow
+ while the I/O thread is waiting for log events.
+ Sleep is a good compromise between performance
+ and CPU resource.
+ This strategy has very low impact on the
+ application thread, in exchange for some additional
+ latency for actually getting the message logged.
+
+ Yield
+ is a strategy that uses a Thread.yield() for
+ waiting for log events after an initially spinning.
+ Yield is a good compromise between performance
+ and CPU resource, but may use more CPU than Sleep
+ in order to get the message logged to disk sooner.
+
+
+ AsyncLogger.Clock
+
+ SystemClock
+
+
+ Implementation of the
+ org.apache.logging.log4j.async.Clock
+ interface that is used for timestamping the log
+ events when all loggers are asynchronous.
+
+
+ By default, System.currentTimeMillis
+ is called on every log event.
+
+ CachedClock
+ is an optimization where
+ time stamps are generated from a clock that
+ updates its internal time in a background thread once
+ every millisecond, or every 1024 log events,
+ whichever comes first. This
+ reduces logging latency a little, at the cost of
+ some precision in the logged time stamps.
+ Unless you are logging many
+ events, you may see "jumps"
+ of 10-16 milliseconds between log time stamps.
+
+ You can also specify a fully qualified class name
+ of a custom class that implements the
+ Clock
+ interface.
+
+
+
+
+
+
+
+
+ System Property
+ Default Value
+ Description
+
+
+ AsyncLoggerConfig.ExceptionHandler
+
+ null
+
+
+ Fully qualified name of a class that implements the
+ org.apache.logging.log4j.async.com.lmax.disruptor.ExceptionHandler
+ interface. The class needs to have a public
+ zero-argument
+ constructor. If specified, this class
+ will be notified when an
+ exception occurs while
+ logging the messages.
+
+
+
+ AsyncLoggerConfig.RingBufferSize
+ 256 * 1024
+
+
+ Size (number of slots) in the RingBuffer used by the
+ asynchronous logging subsystem.
+ Make this value large enough to
+ deal with bursts
+ of activity. The minimum size is 128.
+ The RingBuffer will
+ be pre-allocated at first use and will never grow
+ or shrink during the life of the system.
+
+
+
+ AsyncLoggerConfig.WaitStrategy
+
+ Sleep
+
+
+ Valid values: Block, Sleep, Yield.
+
+
+ Block
+ is a strategy that uses a lock and
+ condition
+ variable for the I/O thread waiting for log events.
+ Block can be used when
+ throughput and low-latency
+ are not as important as CPU resource.
+ Recommended for resource constrained/virtualised environments.
+
+ Sleep
+ is a strategy that initially spins, then
+ uses a Thread.yield(), and
+ eventually parks for the
+ minimum number of nanos the OS and JVM will allow
+ while the I/O thread is waiting for log events.
+ Sleep is a good compromise between performance
+ and CPU resource.
+ This strategy has very low impact on the
+ application thread, in exchange for some additional
+ latency for actually getting the message logged.
+
+ Yield
+ is a strategy that uses a Thread.yield() for
+ waiting for log events after an initially spinning.
+ Yield is a good compromise between performance
+ and CPU resource, but may use more CPU than Sleep
+ in order to get the message logged to disk sooner.
+
+
+
+
+ Parameter Name
+ Type
+ Description
+
+
+ append
+ boolean
+ When true - the default, records will be appended to the end
+ of the file. When set to false,
+ the file will be cleared before
+ new records are written.
+
+
+
+ fileName
+ String
+ The name of the file to write to. If the file, or any of its
+ parent directories, do not exist,
+ they will be created.
+
+
+
+ filters
+ Filter
+ A Filter to determine if the event should be handled by this
+ Appender. More than one Filter
+ may be used by using a CompositeFilter.
+
+
+
+ immediateFlush
+ boolean
+ When set to true, each write will be followed by a flush.
+ This will guarantee the data is written
+ to disk but could impact performance.
+ This option is only necessary when using this
+ appender with synchronous loggers. Asynchronous loggers will
+ automatically flush at the end of a batch, which also guarantees
+ the data is written to disk but is more efficient.
+
+
+
+ layout
+ Layout
+ The Layout to use to format the LogEvent
+
+
+ name
+ String
+ The name of the Appender.
+
+
+ suppressExceptions
+ boolean
+ The default is true, causing exceptions to be internally
+ logged and then ignored. When set to
+ false exceptions will be
+ percolated to the caller.
+
+
+
+
+ The FastRollingFileAppender is similar to the standard + RollingFileAppender + except it is always buffered (this cannot be switched off) + and + internally it uses a + ByteBuffer + RandomAccessFile + instead of a + BufferedOutputStream. + + The FastRollingFileAppender writes + to the File named in the + fileName parameter + and rolls the file over according the + TriggeringPolicy + and the RolloverPolicy. + + Similar to the RollingFileAppender, + FastRollingFileAppender uses a FastRollingFileManager + to actually perform the + file I/O and perform the rollover. While FastRollingFileAppender + from different Configurations cannot be + shared, the FastRollingFileManagers can be + if the Manager is accessible. + For example, two webapps in a servlet + container can have their own configuration and safely write to the + same file if Log4j is in a ClassLoader that is common to both of them. +
++ A FastRollingFileAppender requires a + TriggeringPolicy + and a + RolloverStrategy. + The triggering policy determines if a rollover should + be performed + while the RolloverStrategy defines how the rollover + should be done. + If no RolloverStrategy + is configured, FastRollingFileAppender will + use the + DefaultRolloverStrategy. +
++ File locking is not supported by the FastRollingFileAppender. +
+| Parameter Name | +Type | +Description | +
|---|---|---|
| append | +boolean | +When true - the default, records will be appended to the end + of the file. When set to false, + the file will be cleared before + new records are written. + | +
| filter | +Filter | +A Filter to determine if the event should be handled by this + Appender. More than one Filter + may be used by using a + CompositeFilter. + | +
| fileName | +String | +The name of the file to write to. If the file, or any of its + parent directories, do not exist, + they will be created. + | +
| filePattern | +String | ++ The pattern of the file name of the archived log file. The format + of the pattern should is + dependent on the RolloverPolicy that is + used. The DefaultRolloverPolicy + will accept both + a date/time + pattern compatible with + + SimpleDateFormat + + and/or a %i which represents an integer counter. The pattern + also supports interpolation at + runtime so any of the Lookups (such + as the + DateLookup + can + be included in the pattern. + | +
| immediateFlush | +boolean | +When set to true, each write will be followed by a flush. This + will guarantee the data is written + to disk but could impact + performance. + This option is only necessary when using this + appender with + synchronous loggers. Asynchronous loggers will + automatically + flush at the end of a batch, which also guarantees + the data + is written to disk but is more efficient. + | +
| layout | +Layout | +The Layout to use to format the LogEvent | +
| name | +String | +The name of the Appender. | +
| policy | +TriggeringPolicy | +The policy to use to determine if a rollover should occur. + | +
| strategy | +RolloverStrategy | +The strategy to use to determine the name and location of the + archive file. + | +
| suppressExceptions | +boolean | +The default is true, causing exceptions to be internally + logged and then ignored. When set to + false exceptions will be + percolated to the caller. + | +
+ See + RollingFileAppender Triggering Policies. +
+ ++ See + RollingFileAppender Rollover Strategies. +
+ ++ Below is a sample configuration that uses a FastRollingFileAppender + with both the time and size based + triggering policies, will create + up to 7 archives on the same day (1-7) that + are stored in a + directory + based on the current year and month, and will compress + each + archive using gzip: + +
++ ++ ]]>+ ++ ++ +%d %p %c{1.} [%t] %m%n ++ ++ + + ++ ++
+ This second example shows a rollover strategy that will keep up to + 20 files before removing them. +
++ ++ ]]>+ ++ ++ +%d %p %c{1.} [%t] %m%n ++ ++ + + + ++ ++
+ Below is a sample configuration that uses a FastRollingFileAppender + with both the time and size based + triggering policies, will create + up to 7 archives on the same day (1-7) that + are stored in a + directory + based on the current year and month, and will compress + each + archive using gzip and will roll every 6 hours when the hour is + divisible + by 6: + +
++ ++ ]]>+ ++ ++ +%d %p %c{1.} [%t] %m%n ++ ++ + + ++ ++
+ This section shows the results of our performance tests. + The + methodology used was the same for all tests: +
+On Windows 7 (64bit) with JDK1.7.0_11, 2-core Intel i5-3317u CPU + @1.70Ghz with hyperthreading switched on (4 virtual cores):
+| Logger | +1 thread | +2 threads | +4 threads | +8 threads | +16 threads | +32 threads | +
|---|---|---|---|---|---|---|
| Log4j2: Loggers all asynchronous | +1,655,952 | +928,951 | +1,045,265 | +1,509,109 | +1,708,989 | +773,565 | +
| Log4j2: Loggers mixed sync/async | +534,592 | +1,204,774 | +1,632,204 | +1,368,041 | +462,093 | +908,529 | +
| Log4j2: Async Appender | +1,091,563 | +1,006,287 | +511,571 | +302,230 | +160,094 | +60,152 | +
| Log4j1: Async Appender | +1,336,667 | +911,657 | +636,899 | +406,405 | +202,777 | +162,964 | +
| Logback: Async Appender | +2,231,044 | +783,722 | +582,935 | +289,905 | +172,463 | +133,435 | +
| Log4j2: Synchronous | +281,250 | +225,731 | +129,015 | +66,590 | +34,401 | +17,347 | +
| Log4j1: Synchronous | +147,824 | +72,383 | +32,865 | +18,025 | +8,937 | +4,440 | +
| Logback: Synchronous | +149,811 | +66,301 | +32,341 | +16,962 | +8,431 | +3,610 | +
On Solaris 10 (64bit) with JDK1.7.0_06, 4-core Xeon X5570 dual CPU + @2.93Ghz with hyperthreading switched on (16 virtual cores):
+| Logger | +1 thread | +2 threads | +4 threads | +8 threads | +16 threads | +32 threads | +64 threads | +
|---|---|---|---|---|---|---|---|
| Log4j2: Loggers all asynchronous | +2,652,412 | +909,119 | +776,993 | +516,365 | +239,246 | +253,791 | +288,997 | +
| Log4j2: Loggers mixed sync/async | +2,454,358 | +839,394 | +854,578 | +597,913 | +261,003 | +216,863 | +218,937 | +
| Log4j2: Async Appender | +1,713,429 | +603,019 | +331,506 | +149,408 | +86,107 | +45,529 | +23,980 | +
| Log4j1: Async Appender | +2,239,664 | +494,470 | +221,402 | +109,314 | +60,580 | +31,706 | +14,072 | +
| Logback: Async Appender | +2,206,907 | +624,082 | +307,500 | +160,096 | +85,701 | +43,422 | +21,303 | +
| Log4j2: Synchronous | +273,536 | +136,523 | +67,609 | +34,404 | +15,373 | +7,903 | +4,253 | +
| Log4j1: Synchronous | +326,894 | +105,591 | +57,036 | +30,511 | +13,900 | +7,094 | +3,509 | +
| Logback: Synchronous | +178,063 | +65,000 | +34,372 | +16,903 | +8,334 | +3,985 | +1,967 | +
+ In the above two environments, with the + default settings (SystemClock and SleepingWaitStrategy), + asynchronous logging + processes 6 - 68 times the number of log events in the same time + as synchronous logging. These numbers may also give you an + idea of the performance trade-off when choosing between + All Async and the more flexible + Mixed Async logging. +
+Note that the numbers above are throughput per thread.
+ The graph below shows the total throughput of all threads together.
+ 
On Solaris 10 (64bit) with JDK1.7.0_06, 4-core Xeon X5570 dual CPU + @2.93Ghz with hyperthreading switched off (8 virtual cores):
+| Logger | +1 thread | +2 threads | +4 threads | +8 threads | +
|---|---|---|---|---|
| Loggers all asynchronous | +75,862 | +88,775 | +80,240 | +68,077 | +
| Loggers mixed sync/async | +61,993 | +66,164 | +55,735 | +52,843 | +
| Async Appender | +47,033 | +52,426 | +50,882 | +36,905 | +
| Synchronous | +31,054 | +33,175 | +29,791 | +23,628 | +
As expected, logging location information + has a large performance impact. Asynchronous loggers are 4 - 20 + times slower, while synchronous loggers are 1.3 - 5 times slower. + However, if you do need location information, + asynchronous logging will still be faster than synchronous logging.
+ + +The latency comparison below is done by logging at + less than saturation, measuring how long a call to Logger.log + takes to return. After each call to Logger.log, the test waits + for 10 microseconds * threadCount before continuing. + Each thread logs 5 million messages. +
+
+
+
+ The scale of the latency comparison graphs is logarithmic, not linear. + That makes it a bit difficult to see, but the average latency of + asynchronous loggers is half to one third of the latency of + ArrayBlockingQueue-based asynchronous appenders in scenarios + up to 8 threads. With more threads, the average latency of + asynchronous appenders is orders of magnitude larger than + asynchronous loggers. +
++ Applications interested in low latency often care not only about + average latency, but also about worst-case latency. + The graph below shows that asynchronous loggers also do + better when comparing the maximum latency of 99.99% of + observations with other logging methods. + The worst case latency of asynchronous loggers remains more or + less the same (around 10-20 microseconds) in multi-threaded scenarios where + ArrayBlockingQueue-based asynchronous appenders have more outliers + with latencies of over 100 milliseconds per call to Logger.log. +
+
+
+
On Windows 7 (64bit) with JDK1.7.0_11, 2-core Intel i5-3317u CPU + @1.70Ghz with hyperthreading switched on (4 virtual cores):
+| Appender | +1 thread | +2 threads | +4 threads | +8 threads | +
|---|---|---|---|---|
| FastFileAppender | +250,438 | +169,939 | +109,074 | +58,845 | +
| FileAppender | +186,695 | +118,587 | +57,012 | +28,846 | +
| RollingFastFileAppender | +104,254 | +39,345 | +16,971 | +9,465 | +
| RollingFileAppender | +182,518 | +114,690 | +55,147 | +28,153 | +
On Solaris 10 (64bit) with JDK1.7.0_06, 4-core Xeon X5570 CPU + @2.93GHz with hyperthreading switched on (8 virtual cores):
+| Appender | +1 thread | +2 threads | +4 threads | +8 threads | +
|---|---|---|---|---|
| FastFileAppender | +240,760 | +128,713 | +66,555 | +30,544 | +
| FileAppender | +172,517 | +106,587 | +55,885 | +25,675 | +
| RollingFastFileAppender | +78,715 | +31,426 | +17,881 | +8,727 | +
| RollingFileAppender | +186,422 | +97,737 | +55,766 | +25,097 | +
+ Asynchronous Loggers are implemented using the + LMAX Disruptor + inter-thread messaging library. From the LMAX web site: +
+++ +... using queues to pass + data between stages of the system was + introducing latency, so we + focused on optimising this area. + The Disruptor is the result of our research and + testing. We found that cache misses at the + CPU-level, and locks requiring kernel + arbitration are both extremely costly, so we created a + framework which has "mechanical sympathy" for + the hardware it's running on, and that's lock-free. +
+
+ LMAX Disruptor internal performance comparisons with + java.util.concurrent.ArrayBlockingQueue + can be found + + here. +
+