+ * To use AsyncLogger, specify the System property + * {@code -DLog4jContextSelector=org.apache.logging.log4j.core.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 include source + * location by default. You need to specify {@code includeLocation="true"} in + * the configuration or 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.
+ */
+public class AsyncLogger extends Logger {
+ private static final int HALF_A_SECOND = 500;
+ private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 20;
+ private static final int RINGBUFFER_MIN_SIZE = 128;
+ private static final int RINGBUFFER_DEFAULT_SIZE = 256 * 1024;
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
+ private static volatile Disruptor
+ * AsyncLoggerConfig is a logger designed for high throughput and low latency
+ * logging. It does not perform any I/O in the calling (application) thread, but
+ * instead hands off the work to another thread as soon as possible. The actual
+ * logging is performed in the background thread. It uses the LMAX Disruptor
+ * library for inter-thread communication. (http://lmax-exchange.github.com/disruptor/)
+ *
+ * To use AsyncLoggerConfig, specify {@code
+ * Note that for performance reasons, this logger does not include source
+ * location by default. You need to specify {@code includeLocation="true"} in
+ * the configuration or 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 AsyncLoggerConfig 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.
+ */
+@Plugin(name = "asyncLogger", type = "Core", printObject = true)
+public class AsyncLoggerConfig extends LoggerConfig {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private AsyncLoggerConfigHelper helper;
+
+ /**
+ * 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
+ * {@code AsyncLoggerConfig} is a plugin, and will be loaded even if users do
+ * not configure any {@code
+ * This class serves to make the dependency on the Disruptor optional, so that
+ * these classes are only loaded when the {@code AsyncLoggerConfig} is actually
+ * used.
+ */
+class AsyncLoggerConfigHelper {
+
+ private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 20;
+ private static final int HALF_A_SECOND = 500;
+ private static final int RINGBUFFER_MIN_SIZE = 128;
+ private static final int RINGBUFFER_DEFAULT_SIZE = 256 * 1024;
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ private static volatile Disruptor
+ * If system property {@code AsyncLogger.Clock=CachedClock} is specified,
+ * this method returns an instance of {@link CachedClock}. If system
+ * property {@code AsyncLogger.Clock=CoarseCachedClock} is specified, this
+ * method returns an instance of {@link CoarseCachedClock}.
+ *
+ * If another value is specified, this value is taken as the fully qualified
+ * class name of a class that implements the {@code Clock} interface. An
+ * object of this class is instantiated and returned.
+ *
+ * If no value is specified, or if the specified value could not correctly
+ * be instantiated or did not implement the {@code Clock} interface, then an
+ * instance of {@link SystemClock} is returned.
+ *
+ * @return a {@code Clock} instance
+ */
+ public static Clock getClock() {
+ return createClock();
+ }
+
+ private static Clock createClock() {
+ String userRequest = System.getProperty(PROPERTY_NAME);
+ if (userRequest == null || "SystemClock".equals(userRequest)) {
+ LOGGER.debug("Using default SystemClock for timestamps");
+ return new SystemClock();
+ }
+ if (CachedClock.class.getName().equals(userRequest) //
+ || "CachedClock".equals(userRequest)) {
+ LOGGER.debug("Using specified CachedClock for timestamps");
+ return CachedClock.instance();
+ }
+ if (CoarseCachedClock.class.getName().equals(userRequest) //
+ || "CoarseCachedClock".equals(userRequest)) {
+ LOGGER.debug("Using specified CoarseCachedClock for timestamps");
+ return CoarseCachedClock.instance();
+ }
+ try {
+ Clock result = (Clock) Class.forName(userRequest).newInstance();
+ LOGGER.debug("Using {} for timestamps", userRequest);
+ return result;
+ } catch (Exception e) {
+ String fmt = "Could not create {}: {}, using default SystemClock for timestamps";
+ LOGGER.error(fmt, userRequest, e);
+ return new SystemClock();
+ }
+ }
+}
Index: core/src/main/java/org/apache/logging/log4j/core/async/CoarseCachedClock.java
===================================================================
--- core/src/main/java/org/apache/logging/log4j/core/async/CoarseCachedClock.java (revision 0)
+++ core/src/main/java/org/apache/logging/log4j/core/async/CoarseCachedClock.java (working copy)
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.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 final 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)
+ }
+ }
+ };
+
+ private CoarseCachedClock() {
+ updater.setDaemon(true);
+ updater.start();
+ }
+
+ /**
+ * Returns the singleton instance.
+ *
+ * @return the singleton instance
+ */
+ public static CoarseCachedClock instance() {
+ return instance;
+ }
+
+ /**
+ * Returns the value of a private long field that is updated by a background
+ * thread once every millisecond. Because timers on most platforms do not
+ * have millisecond granularity, the returned value may "jump" every 10 or
+ * 16 milliseconds.
+ * @return the cached time
+ */
+ @Override
+ public long currentTimeMillis() {
+ return millis;
+ }
+}
Index: core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
===================================================================
--- core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java (revision 0)
+++ core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java (working copy)
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.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;
+
+ /**
+ * Creates the events that will be put in the RingBuffer.
+ */
+ private static class Factory implements EventFactory
- The Log4Jj 2 implementation provides the functional components of the logging system. Users are
- free to create their own plugins and include them in the logging configuration.
+ The Log4Jj 2 implementation provides the functional components
+ of the logging system.
+ Users are free to create their own plugins and include them
+ in the logging configuration.
- Log4j 2 requires Java 5 but has will take advantage of enhancements in Java 6 to improve performance.
- Some features may require optional dependencies. These dependencies are
+ Log4j 2 requires Java 6.
+ Some features may require optional
+ dependencies. These dependencies are
specified in the documentation for those features.
+