(properties.length);
@@ -250,11 +256,34 @@
/**
* Sets the additive setting.
- * @param additive true if thee LoggerConfig should be additive, false otherwise.
+ * @param additive true if the LoggerConfig should be additive, false otherwise.
*/
public void setAdditive(final boolean additive) {
this.additive = additive;
}
+
+ public boolean isLocationRequired() {
+ return isLocationRequired;
+ }
+
+ /**
+ * Returns an unmodifiable map with the configuration properties, or
+ * {@code null} if this {@code LoggerConfig} does not have any configuration
+ * properties.
+ *
+ * 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 getProperties() {
+ return properties == null ? null :
+ Collections.unmodifiableMap(properties);
+ }
/**
* Logs an event.
@@ -313,6 +342,8 @@
return;
}
+ event.setLocationRequired(isLocationRequired);
+
callAppenders(event);
if (additive && parent != null) {
@@ -330,7 +361,7 @@
}
}
- private void callAppenders(final LogEvent event) {
+ protected void callAppenders(final LogEvent event) {
for (final AppenderControl control : appenders.values()) {
control.callAppender(event);
}
@@ -372,6 +403,7 @@
public static LoggerConfig createLogger(@PluginAttr("additivity") final String additivity,
@PluginAttr("level") final String levelName,
@PluginAttr("name") final String loggerName,
+ @PluginAttr("needsLocation") final String needsLocation,
@PluginElement("appender-ref") final AppenderRef[] refs,
@PluginElement("properties") final Property[] properties,
@PluginConfiguration final Configuration config,
@@ -391,8 +423,11 @@
}
final String name = loggerName.equals("root") ? "" : loggerName;
final boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity);
+ final boolean isLocationRequired = needsLocation == null ? true
+ : Boolean.parseBoolean(needsLocation);
- return new LoggerConfig(name, appenderRefs, filter, level, additive, properties, config);
+ return new LoggerConfig(name, appenderRefs, filter, level, additive,
+ properties, config, isLocationRequired);
}
/**
@@ -404,6 +439,7 @@
@PluginFactory
public static LoggerConfig createLogger(@PluginAttr("additivity") final String additivity,
@PluginAttr("level") final String levelName,
+ @PluginAttr("needsLocation") final String needsLocation,
@PluginElement("appender-ref") final AppenderRef[] refs,
@PluginElement("properties") final Property[] properties,
@PluginConfiguration final Configuration config,
@@ -417,9 +453,11 @@
level = Level.ERROR;
}
final boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity);
+ final boolean isLocationRequired = needsLocation == null ? true
+ : Boolean.parseBoolean(needsLocation);
- return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additive, properties,
- config);
+ return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs,
+ filter, level, additive, properties, config, isLocationRequired);
}
}
Index: core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
===================================================================
--- core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java (revision 1452660)
+++ core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java (working copy)
@@ -49,6 +49,8 @@
private final ThreadContext.ContextStack ndc;
private String threadName = null;
private StackTraceElement location;
+ private boolean locationRequired;
+ private boolean endOfBatch = false;
/**
* Constructor.
@@ -222,33 +224,56 @@
* @return the StackTraceElement for the caller.
*/
public StackTraceElement getSource() {
+ if (location != null) {
+ return location;
+ }
+ if (fqcnOfLogger == null || !locationRequired) {
+ return null;
+ }
+ location = calcLocation(fqcnOfLogger);
+ return location;
+ }
+
+ public static StackTraceElement calcLocation(String fqcnOfLogger) {
if (fqcnOfLogger == null) {
return null;
}
- if (location == null) {
- final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
- boolean next = false;
- for (final StackTraceElement element : stackTrace) {
- final String className = element.getClassName();
- if (next) {
- if (fqcnOfLogger.equals(className)) {
- continue;
- }
- location = element;
- break;
- }
-
+ final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ boolean next = false;
+ for (final StackTraceElement element : stackTrace) {
+ final String className = element.getClassName();
+ if (next) {
if (fqcnOfLogger.equals(className)) {
- next = true;
- } else if (NOT_AVAIL.equals(className)) {
- break;
+ continue;
}
+ return element;
}
+
+ if (fqcnOfLogger.equals(className)) {
+ next = true;
+ } else if (NOT_AVAIL.equals(className)) {
+ break;
+ }
}
+ return null;
+ }
- return location;
+ public boolean isLocationRequired() {
+ return locationRequired;
}
+ public void setLocationRequired(boolean locationRequired) {
+ this.locationRequired = locationRequired;
+ }
+
+ public boolean isEndOfBatch() {
+ return endOfBatch;
+ }
+
+ public void setEndOfBatch(boolean endOfBatch) {
+ this.endOfBatch = endOfBatch;
+ }
+
/**
* Creates a LogEventProxy that can be serialized.
* @return a LogEventProxy.
@@ -267,8 +292,13 @@
}
if (event instanceof LogEventProxy) {
final LogEventProxy proxy = (LogEventProxy) event;
- return new Log4jLogEvent(proxy.name, proxy.marker, proxy.fqcnOfLogger, proxy.level, proxy.message,
- proxy.throwable, proxy.mdc, proxy.ndc, proxy.threadName, proxy.location, proxy.timestamp);
+ Log4jLogEvent result = new Log4jLogEvent(proxy.name, proxy.marker,
+ proxy.fqcnOfLogger, proxy.level, proxy.message,
+ proxy.throwable, proxy.mdc, proxy.ndc, proxy.threadName,
+ proxy.location, proxy.timestamp);
+ result.setEndOfBatch(proxy.isEndOfBatch);
+ result.setLocationRequired(proxy.isLocationRequired);
+ return result;
}
throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
}
@@ -304,6 +334,8 @@
private final ThreadContext.ContextStack ndc;
private final String threadName;
private final StackTraceElement location;
+ private final boolean isLocationRequired;
+ private final boolean isEndOfBatch;
public LogEventProxy(final Log4jLogEvent event) {
this.fqcnOfLogger = event.fqcnOfLogger;
@@ -317,6 +349,8 @@
this.ndc = event.ndc;
this.location = event.getSource();
this.threadName = event.getThreadName();
+ this.isLocationRequired = event.locationRequired;
+ this.isEndOfBatch = event.endOfBatch;
}
/**
@@ -324,10 +358,12 @@
* @return Log4jLogEvent.
*/
protected Object readResolve() {
- return new Log4jLogEvent(name, marker, fqcnOfLogger, level, message, throwable, mdc, ndc, threadName,
- location, timestamp);
+ Log4jLogEvent result = new Log4jLogEvent(name, marker, fqcnOfLogger,
+ level, message, throwable, mdc, ndc, threadName, location,
+ timestamp);
+ result.setEndOfBatch(isEndOfBatch);
+ result.setLocationRequired(isLocationRequired);
+ return result;
}
-
}
-
}
Index: core/src/test/java/org/apache/logging/log4j/core/lookup/DateLookupTest.java
===================================================================
--- core/src/test/java/org/apache/logging/log4j/core/lookup/DateLookupTest.java (revision 1452646)
+++ core/src/test/java/org/apache/logging/log4j/core/lookup/DateLookupTest.java (working copy)
@@ -94,5 +94,19 @@
public String getFQCN() {
return null;
}
+
+ public boolean isEndOfBatch() {
+ return false;
+ }
+
+ public void setEndOfBatch(boolean endOfBatch) {
+ }
+
+ public boolean isLocationRequired() {
+ return false;
+ }
+
+ public void setLocationRequired(boolean locationRequired) {
+ }
}
}
Index: flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
===================================================================
--- flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java (revision 1452646)
+++ flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java (working copy)
@@ -277,4 +277,24 @@
public ThreadContext.ContextStack getContextStack() {
return event.getContextStack();
}
+
+ @Override
+ public boolean isLocationRequired() {
+ return event.isLocationRequired();
+ }
+
+ @Override
+ public void setLocationRequired(boolean locationRequired) {
+ event.setLocationRequired(locationRequired);
+ }
+
+ @Override
+ public boolean isEndOfBatch() {
+ return event.isEndOfBatch();
+ }
+
+ @Override
+ public void setEndOfBatch(boolean endOfBatch) {
+ event.setEndOfBatch(endOfBatch);
+ }
}
Index: log4j-async/.classpath
===================================================================
--- log4j-async/.classpath (revision 0)
+++ log4j-async/.classpath (working copy)
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: log4j-async/.project
===================================================================
--- log4j-async/.project (revision 0)
+++ log4j-async/.project (working copy)
@@ -0,0 +1,23 @@
+
+
+ log4j-async
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
Index: log4j-async/pom.xml
===================================================================
--- log4j-async/pom.xml (revision 0)
+++ log4j-async/pom.xml (working copy)
@@ -0,0 +1,222 @@
+
+
+
+ 4.0.0
+
+ log4j
+ org.apache.logging.log4j
+ 2.0-beta5-SNAPSHOT
+ ../
+
+
+ org.apache.logging.log4j
+ log4j-async
+ jar
+ Apache Log4j Async
+ Log4j 2.0 Asynchronous Loggers for Low Latency Logging
+
+ UTF-8
+ ${basedir}/..
+ Asynchronous Logging Documentation
+ /log4j-async
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.apache.logging.log4j
+ log4j-core
+ test-jar
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ always
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.2.1
+
+
+ process-classes
+
+ java
+
+
+
+
+ org.apache.logging.log4j.core.config.plugins.PluginManager
+
+ ${project.build.outputDirectory}
+ org.apache.logging.log4j.async
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-changes-plugin
+ ${changes.plugin.version}
+
+
+
+ changes-report
+
+
+
+
+ %URL%/show_bug.cgi?id=%ISSUE%
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 2.7
+
+
+ ${log4jParentDir}/checkstyle.xml
+ ${log4jParentDir}/checkstyle-suppressions.xml
+ false
+ basedir=${basedir}
+ licensedir=${log4jParentDir}/checkstyle-header.txt
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${javadoc.plugin.version}
+
+ Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved. Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, and the
+ Apache Logging project logo are trademarks of The Apache Software Foundation.
+
+ false
+ true
+
+
+ issue
+ a
+ JIRA issue:
+
+
+ doubt
+ a
+ Troublesome:
+
+
+ compare
+ a
+ Compare with:
+
+
+
+
+
+ non-aggregate
+
+ javadoc
+
+
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+ 2.3.2
+
+ Normal
+ Default
+ findbugs-exclude-filter.xml
+
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+ 2.3
+
+
+ non-aggregate
+
+ jxr
+
+
+
+ aggregate
+
+ aggregate
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+ ${pmd.plugin.version}
+
+ 1.5
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ 2.2
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLogger.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLogger.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLogger.java (working copy)
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.ExceptionHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.RingBuffer;
+import org.apache.logging.log4j.async.com.lmax.disruptor.dsl.Disruptor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * AsyncLogger 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 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 disruptor;
+ private static Clock clock = Boolean.getBoolean("useSystemClock") ? new SystemClock()
+ : CachedClock.instance();
+ private static ExecutorService executor = Executors
+ .newSingleThreadExecutor();
+ private ThreadLocal threadlocalInfo = new ThreadLocal();
+
+ static {
+ int ringBufferSize = calculateRingBufferSize();
+
+ disruptor = new Disruptor(
+ RingBufferLogEvent.FACTORY, ringBufferSize, executor);
+ EventHandler[] handlers = new RingBufferLogEventHandler[] { new RingBufferLogEventHandler() };
+ disruptor.handleExceptionsWith(getExceptionHandler());
+ disruptor.handleEventsWith(handlers);
+
+ LOGGER.debug(
+ "Starting AsyncLogger disruptor with ringbuffer size {}...",
+ disruptor.getRingBuffer().getBufferSize());
+ disruptor.start();
+ }
+
+ private static int calculateRingBufferSize() {
+ String userPreferredRBSize = System.getProperty(
+ "AsyncLogger.RingBufferSize", "256000");
+ int ringBufferSize = 256000; // default
+ try {
+ int size = Integer.parseInt(userPreferredRBSize);
+ if (size < 128) {
+ size = 128;
+ LOGGER.warn(
+ "Invalid RingBufferSize {}, using minimum size 128.",
+ userPreferredRBSize);
+ }
+ ringBufferSize = size;
+ } catch (Exception ex) {
+ LOGGER.warn("Invalid RingBufferSize {}, using default size.",
+ userPreferredRBSize);
+ }
+ return Util.ceilingNextPowerOfTwo(ringBufferSize);
+ }
+
+ private static ExceptionHandler getExceptionHandler() {
+ String cls = System.getProperty("AsyncLogger.ExceptionHandler");
+ if (cls == null) {
+ return null;
+ }
+ try {
+ @SuppressWarnings("unchecked")
+ Class extends ExceptionHandler> klass = (Class extends ExceptionHandler>) Class
+ .forName(cls);
+ return klass.newInstance();
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
+
+ private static class Info {
+ RingBufferLogEventTranslator translator;
+ String cachedThreadName;
+ }
+
+ public AsyncLogger(LoggerContext context, String name,
+ MessageFactory messageFactory) {
+ super(context, name, messageFactory);
+ }
+
+ @Override
+ public void log(Marker marker, String fqcn, Level level, Message data,
+ Throwable t) {
+ Info info = threadlocalInfo.get();
+ if (info == null) {
+ info = new Info();
+ info.translator = new RingBufferLogEventTranslator();
+ info.cachedThreadName = Thread.currentThread().getName();
+ threadlocalInfo.set(info);
+ }
+
+ info.translator.setValues(this, getName(), marker, fqcn, level, data,
+ t, //
+
+ // config properties are taken care of in the EventHandler
+ // thread in the #actualAsyncLog method
+
+ // needs shallow copy to be fast (LOG4J2-154)
+ ThreadContext.getImmutableContext(),//
+
+ // needs shallow copy to be fast (LOG4J2-154)
+ ThreadContext.getImmutableStack(), //
+
+ // Thread.currentThread().getName(), //
+ info.cachedThreadName, //
+
+ // location: very expensive without LOG4J2-153
+ config.loggerConfig.isLocationRequired() ? location(fqcn)
+ : null,
+
+ // System.currentTimeMillis());
+ // CoarseCachedClock: 20% faster than system clock, 16ms gaps
+ // CachedClock: 10% faster than system clock, smaller gaps
+ clock.currentTimeMillis());
+
+ disruptor.publishEvent(info.translator);
+ }
+
+ private StackTraceElement location(String fqcnOfLogger) {
+ return Log4jLogEvent.calcLocation(fqcnOfLogger);
+ }
+
+ /**
+ * This method is called by the EventHandler that processes the
+ * RingBufferLogEvent in a separate thread.
+ *
+ * @param event the event to log
+ */
+ public void actualAsyncLog(RingBufferLogEvent event) {
+ Map properties = config.loggerConfig.getProperties();
+ event.mergePropertiesIntoContextMap(properties,
+ config.config.getSubst());
+ config.logEvent(event);
+ }
+
+ public static void stop() {
+ Disruptor temp = disruptor;
+
+ // Must guarantee that publishing to the RingBuffer has stopped
+ // before we call disruptor.shutdown()
+ disruptor = null; // client code fails with NPE if log after stop = OK
+ temp.shutdown();
+
+ // wait up to 10 seconds for the ringbuffer to drain
+ RingBuffer ringBuffer = temp.getRingBuffer();
+ for (int i = 0; i < 20; i++) {
+ if (ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize())) {
+ break;
+ }
+ try {
+ Thread.sleep(500); // give ringbuffer some time to drain...
+ } catch (InterruptedException e) {
+ }
+ }
+ executor.shutdown(); // finally, kill the processor thread
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLoggerContext.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLoggerContext.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLoggerContext.java (working copy)
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import java.net.URI;
+
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.message.MessageFactory;
+
+// depends on LOG4J2-151
+public class AsyncLoggerContext extends LoggerContext {
+
+ public AsyncLoggerContext(String name) {
+ super(name);
+ }
+
+ public AsyncLoggerContext(String name, Object externalContext) {
+ super(name, externalContext);
+ }
+
+ public AsyncLoggerContext(String name, Object externalContext,
+ URI configLocn) {
+ super(name, externalContext, configLocn);
+ }
+
+ public AsyncLoggerContext(String name, Object externalContext,
+ String configLocn) {
+ super(name, externalContext, configLocn);
+ }
+
+ // @Override
+ protected Logger newInstance(LoggerContext ctx, String name,
+ MessageFactory messageFactory) {
+ return new AsyncLogger(ctx, name, messageFactory);
+ }
+
+ @Override
+ public void stop() {
+ AsyncLogger.stop();
+ super.stop();
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLoggerContextSelector.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLoggerContextSelector.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/AsyncLoggerContextSelector.java (working copy)
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.selector.ContextSelector;
+
+public class AsyncLoggerContextSelector implements ContextSelector {
+
+ private static final AsyncLoggerContext context = new AsyncLoggerContext(
+ "Default");
+
+ public LoggerContext getContext(String fqcn, ClassLoader loader,
+ boolean currentContext) {
+ return context;
+ }
+
+ public List getLoggerContexts() {
+ List list = new ArrayList();
+ list.add(context);
+ return Collections.unmodifiableList(list);
+ }
+
+ public LoggerContext getContext(String fqcn, ClassLoader loader,
+ boolean currentContext, URI configLocation) {
+ return context;
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/CachedClock.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/CachedClock.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/CachedClock.java (working copy)
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+public class CachedClock implements Clock {
+ private static CachedClock instance = new CachedClock();
+ private volatile long millis = System.currentTimeMillis();
+ private volatile short count = 0;
+ private final Thread updater = new Thread("Clock Updater Thread") {
+ public void run() {
+ while (true) {
+ long time = System.currentTimeMillis();
+ millis = time;
+ Util.getUnsafe().park(true, time + 1); // abs (millis)
+ // Util.getUnsafe().park(false, 1000 * 1000);// relative(nanos)
+ }
+ }
+ };
+
+ public static CachedClock instance() {
+ return instance;
+ }
+
+ private CachedClock() {
+ updater.setDaemon(true);
+ updater.start();
+ }
+
+ // @Override
+ public long currentTimeMillis() {
+
+ // improve granularity: also update time field every 1024 calls.
+ // (the bit fiddling means we don't need to worry about overflows)
+ if ((++count & 0x3FF) == 0x3FF) {
+ millis = System.currentTimeMillis();
+ }
+ return millis;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/Clock.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/Clock.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/Clock.java (working copy)
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+public interface Clock {
+ long currentTimeMillis();
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/CoarseCachedClock.java (working copy)
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+/**
+ * This Clock implementation is similar to CachedClock. It is slightly faster at
+ * the cost of some accuracy.
+ *
+ * @author Remko Popma
+ */
+public class CoarseCachedClock implements Clock {
+ private static CoarseCachedClock instance = new CoarseCachedClock();
+ private volatile long millis = System.currentTimeMillis();
+ private final Thread updater = new Thread("Clock Updater Thread") {
+ public void run() {
+ while (true) {
+ long time = System.currentTimeMillis();
+ millis = time;
+ Util.getUnsafe().park(true, time + 1); // abs (millis)
+ // Util.getUnsafe().park(false, 1000 * 1000);// relative(nanos)
+ }
+ }
+ };
+
+ public static CoarseCachedClock instance() {
+ return instance;
+ }
+
+ private CoarseCachedClock() {
+ updater.setDaemon(true);
+ updater.start();
+ }
+
+ // @Override
+ public long currentTimeMillis() {
+ return millis;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEvent.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEvent.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/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.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.async.com.lmax.disruptor.EventFactory;
+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;
+
+/**
+ * When the Disruptor is started, the RingBuffer is populated with event
+ * objects. These objects are then re-used during the life of the RingBuffer.
+ *
+ * @author Remko Popma
+ */
+public class RingBufferLogEvent implements LogEvent {
+ private static final long serialVersionUID = 8462119088943934758L;
+
+ public static final Factory FACTORY = new Factory();
+
+ /**
+ * Creates the events that will be put in the RingBuffer.
+ */
+ private static class Factory implements EventFactory {
+ // @Override
+ public RingBufferLogEvent newInstance() {
+ return new RingBufferLogEvent();
+ }
+ }
+
+ private AsyncLogger asyncLogger;
+ private String loggerName;
+ private Marker marker;
+ private String fqcn;
+ private Level level;
+ private Message message;
+ private Throwable thrown;
+ private Map contextMap;
+ private ContextStack contextStack;
+ private String threadName;
+ private StackTraceElement location;
+ private long currentTimeMillis;
+ private boolean endOfBatch;
+ private boolean locationRequired;
+
+ public void setValues(AsyncLogger asyncLogger, String loggerName,
+ Marker marker, String fqcn, Level level, Message data, Throwable t,
+ Map map, ContextStack contextStack,
+ String threadName, StackTraceElement location,
+ long currentTimeMillis) {
+ this.asyncLogger = asyncLogger;
+ this.loggerName = loggerName;
+ this.marker = marker;
+ this.fqcn = fqcn;
+ this.level = level;
+ this.message = data;
+ this.thrown = t;
+ this.contextMap = map;
+ this.contextStack = contextStack;
+ this.threadName = threadName;
+ this.location = location;
+ this.currentTimeMillis = currentTimeMillis;
+ }
+
+ /**
+ * Event processor that reads the event from the ringbuffer can call this
+ * method.
+ *
+ * @param endOfBatch flag to indicate if this is the last event in a batch
+ * from the RingBuffer
+ */
+ public void execute(boolean endOfBatch) {
+ this.endOfBatch = endOfBatch;
+ asyncLogger.actualAsyncLog(this);
+ }
+
+ /**
+ * Returns {@code true} if this event is the end of a batch, {@code false}
+ * otherwise.
+ *
+ * @return {@code true} if this event is the end of a batch, {@code false}
+ * otherwise
+ */
+ public boolean isEndOfBatch() {
+ return endOfBatch;
+ }
+
+ public void setEndOfBatch(boolean endOfBatch) {
+ this.endOfBatch = endOfBatch;
+ }
+
+ public boolean isLocationRequired() {
+ return locationRequired;
+ }
+
+ public void setLocationRequired(boolean locationRequired) {
+ this.locationRequired = locationRequired;
+ }
+
+ // @Override
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ // @Override
+ public Marker getMarker() {
+ return marker;
+ }
+
+ // @Override
+ public String getFQCN() {
+ return fqcn;
+ }
+
+ // @Override
+ public Level getLevel() {
+ return level;
+ }
+
+ // @Override
+ public Message getMessage() {
+ if (message == null) {
+ message = new SimpleMessage("");
+ }
+ return message;
+ }
+
+ // @Override
+ public Throwable getThrown() {
+ return thrown;
+ }
+
+ // @Override
+ public Map getContextMap() {
+ return contextMap;
+ }
+
+ // @Override
+ public ContextStack getContextStack() {
+ return contextStack;
+ }
+
+ // @Override
+ public String getThreadName() {
+ return threadName;
+ }
+
+ // @Override
+ public StackTraceElement getSource() {
+ return location;
+ }
+
+ // @Override
+ public long getMillis() {
+ return currentTimeMillis;
+ }
+
+ /**
+ * Merges the contents of the specified map into the contextMap, after
+ * replacing any variables in the property values with the
+ * StrSubstitutor-supplied actual values.
+ *
+ * @param properties configured properties
+ * @param strSubstitutor used to lookup values of variables in properties
+ */
+ public void mergePropertiesIntoContextMap(
+ Map properties, StrSubstitutor strSubstitutor) {
+ if (properties == null) {
+ return; // nothing to do
+ }
+
+ Map map = (contextMap == null) ? new HashMap()
+ : new HashMap(contextMap);
+
+ for (Map.Entry entry : properties.entrySet()) {
+ Property prop = entry.getKey();
+ if (map.containsKey(prop.getName())) {
+ continue; // contextMap overrides config properties
+ }
+ String value = entry.getValue() ? strSubstitutor.replace(prop
+ .getValue()) : prop.getValue();
+ map.put(prop.getName(), value);
+ }
+ contextMap = map;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventHandler.java (working copy)
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+
+/**
+ * This event handler gets passed messages from the RingBuffer as they become
+ * available. Processing of these messages is done in a separate thread,
+ * controlled by the {@code Executor} passed to the {@code Disruptor}
+ * constructor.
+ *
+ * @author Remko Popma
+ */
+public class RingBufferLogEventHandler implements
+ EventHandler {
+
+ // @Override
+ public void onEvent(RingBufferLogEvent event, long sequence,
+ boolean endOfBatch) throws Exception {
+ event.execute(endOfBatch);
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/RingBufferLogEventTranslator.java (working copy)
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext.ContextStack;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventTranslator;
+import org.apache.logging.log4j.message.Message;
+
+public class RingBufferLogEventTranslator implements
+ EventTranslator {
+
+ private AsyncLogger asyncLogger;
+ private String loggerName;
+ private Marker marker;
+ private String fqcn;
+ private Level level;
+ private Message message;
+ private Throwable thrown;
+ private Map contextMap;
+ private ContextStack contextStack;
+ private String threadName;
+ private StackTraceElement location;
+ private long currentTimeMillis;
+
+ // @Override
+ public void translateTo(RingBufferLogEvent event, long sequence) {
+ event.setValues(asyncLogger, loggerName, marker, fqcn, level, message,
+ thrown, contextMap, contextStack, threadName, location,
+ currentTimeMillis);
+ }
+
+ public void setValues(AsyncLogger asyncLogger, String loggerName,
+ Marker marker, String fqcn, Level level, Message message,
+ Throwable thrown, Map contextMap,
+ ContextStack contextStack, String threadName,
+ StackTraceElement location, long currentTimeMillis) {
+ this.asyncLogger = asyncLogger;
+ this.loggerName = loggerName;
+ this.marker = marker;
+ this.fqcn = fqcn;
+ this.level = level;
+ this.message = message;
+ this.thrown = thrown;
+ this.contextMap = contextMap;
+ this.contextStack = contextStack;
+ this.threadName = threadName;
+ this.location = location;
+ this.currentTimeMillis = currentTimeMillis;
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/SystemClock.java (working copy)
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async;
+
+public class SystemClock implements Clock {
+
+ // @Override
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileAppender.java (working copy)
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async.appender;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.Advertiser;
+
+/**
+ * File Appender.
+ */
+@Plugin(name = "FastFile", type = "Core", elementType = "appender", printObject = true)
+public final class FastFileAppender extends AbstractOutputStreamAppender {
+
+ private final String fileName;
+ private Object advertisement;
+ private final Advertiser advertiser;
+
+ private FastFileAppender(String name, Layout> layout, Filter filter,
+ FastFileManager manager, String filename, boolean handleException,
+ boolean immediateFlush, Advertiser advertiser) {
+ super(name, layout, filter, handleException, immediateFlush, manager);
+ if (advertiser != null) {
+ Map configuration = new HashMap(
+ layout.getContentFormat());
+ configuration.putAll(manager.getContentFormat());
+ configuration.put("contentType", layout.getContentType());
+ configuration.put("name", name);
+ advertisement = advertiser.advertise(configuration);
+ }
+ this.fileName = filename;
+ this.advertiser = advertiser;
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ if (advertiser != null) {
+ advertiser.unadvertise(advertisement);
+ }
+ }
+
+ /**
+ * Write the log entry rolling over the file when required.
+ *
+ * @param event The LogEvent.
+ */
+ @Override
+ public void append(LogEvent event) {
+
+ // Leverage the nice batching behaviour of async Loggers/Appenders:
+ // we can signal the file manager that it needs to flush the buffer
+ // to disk at the end of a batch.
+ // From a user's point of view, this means that all log events are
+ // _always_ available in the log file, without incurring the overhead
+ // of immediateFlush=true.
+ //
+ // without LOG4J2-164:
+ // if (event.getClass() == RingBufferLogEvent.class) {
+ // boolean isEndOfBatch = ((RingBufferLogEvent) event).isEndOfBatch();
+ // ((FastFileManager) getManager()).setEndOfBatch(isEndOfBatch);
+ // }
+ ((FastFileManager) getManager()).setEndOfBatch(event.isEndOfBatch());
+ super.append(event);
+ }
+
+ /**
+ * Returns the file name this appender is associated with.
+ *
+ * @return The File name.
+ */
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ // difference from standard File Appender:
+ // locking is not supported and buffering cannot be switched off
+ /**
+ * Create a File Appender.
+ *
+ * @param fileName The name and path of the file.
+ * @param append "True" if the file should be appended to, "false" if it
+ * should be overwritten. The default is "true".
+ * @param name The name of the Appender.
+ * @param immediateFlush "true" if the contents should be flushed on every
+ * write, "false" otherwise. The default is "true".
+ * @param suppress "true" if exceptions should be hidden from the
+ * application, "false" otherwise. The default is "true".
+ * @param layout The layout to use to format the event. If no layout is
+ * provided the default PatternLayout will be used.
+ * @param filter The filter, if any, to use.
+ * @param advertise "true" if the appender configuration should be
+ * advertised, "false" otherwise.
+ * @param advertiseURI The advertised URI which can be used to retrieve the
+ * file contents.
+ * @param config The Configuration.
+ * @return The FileAppender.
+ */
+ @PluginFactory
+ public static FastFileAppender createAppender(
+ @PluginAttr("fileName") String fileName,
+ @PluginAttr("append") String append,
+ @PluginAttr("name") String name,
+ @PluginAttr("immediateFlush") String immediateFlush,
+ @PluginAttr("suppressExceptions") String suppress,
+ @PluginElement("layout") Layout> layout,
+ @PluginElement("filters") final Filter filter,
+ @PluginAttr("advertise") final String advertise,
+ @PluginAttr("advertiseURI") final String advertiseURI,
+ @PluginConfiguration final Configuration config) {
+
+ boolean isAppend = append == null ? true : Boolean.valueOf(append);
+ boolean isFlush = immediateFlush == null ? true : Boolean.valueOf(immediateFlush);
+ boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
+ boolean isAdvertise = advertise == null ? false : Boolean.valueOf(advertise);
+
+ if (name == null) {
+ LOGGER.error("No name provided for FileAppender");
+ return null;
+ }
+
+ if (fileName == null) {
+ LOGGER.error("No filename provided for FileAppender with name "
+ + name);
+ return null;
+ }
+
+ FastFileManager manager = FastFileManager.getFileManager(fileName,
+ isAppend, isFlush, advertiseURI);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = PatternLayout.createLayout(null, null, null, null);
+ }
+ return new FastFileAppender(name, layout, filter, manager, fileName,
+ handleExceptions, isFlush, isAdvertise ? config.getAdvertiser() : null);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastFileManager.java (working copy)
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async.appender;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.OutputStreamManager;
+
+/**
+ * Manages actual File I/O for File Appenders.
+ */
+public class FastFileManager extends OutputStreamManager {
+
+ private static final FastFileManagerFactory factory = new FastFileManagerFactory();
+
+ private final boolean isImmediateFlush;
+ private final String advertiseURI;
+ private final RandomAccessFile randomAccessFile;
+ private final ByteBuffer buffer;
+ private ThreadLocal isEndOfBatch = new ThreadLocal();
+
+ protected FastFileManager(RandomAccessFile file, String fileName,
+ OutputStream os, boolean immediateFlush, String advertiseURI) {
+ super(os, fileName);
+ this.isImmediateFlush = immediateFlush;
+ this.randomAccessFile = file;
+ this.advertiseURI = advertiseURI;
+ isEndOfBatch.set(Boolean.FALSE);
+ buffer = ByteBuffer.allocate(256 * 1024); // TODO make configurable?
+ }
+
+ /**
+ * Returns the FastFileManager.
+ *
+ * @param fileName
+ * The name of the file to manage.
+ * @param append
+ * true if the file should be appended to, false if it should be
+ * overwritten.
+ * @param isFlush
+ * true if the contents should be flushed to disk on every write
+ * @param advertiseURI the URI to use when advertising the file
+ * @return A FastFileManager for the File.
+ */
+ public static FastFileManager getFileManager(String fileName,
+ boolean append, boolean isFlush, String advertiseURI) {
+ return (FastFileManager) getManager(fileName, new FactoryData(append,
+ isFlush, advertiseURI), factory);
+ }
+
+ public Boolean isEndOfBatch() {
+ return isEndOfBatch.get();
+ }
+
+ public void setEndOfBatch(boolean isEndOfBatch) {
+ this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
+ }
+
+ @Override
+ protected synchronized void write(byte[] bytes, int offset, int length) {
+ super.write(bytes, offset, length); // writes to dummy output stream
+
+ if (length > buffer.remaining()) {
+ flush();
+ }
+ buffer.put(bytes, offset, length);
+ if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
+ flush();
+ }
+ }
+
+ @Override
+ public void flush() {
+ buffer.flip();
+ try {
+ randomAccessFile.write(buffer.array(), 0, buffer.limit());
+ } catch (IOException ex) {
+ String msg = "Error writing to RandomAccessFile " + getName();
+ throw new AppenderRuntimeException(msg, ex);
+ }
+ buffer.clear();
+ }
+
+ /**
+ * Returns the name of the File being managed.
+ *
+ * @return The name of the File being managed.
+ */
+ public String getFileName() {
+ return getName();
+ }
+
+ private static class DummyOutputStream extends OutputStream {
+ @Override
+ public void write(int b) throws IOException {
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ }
+ }
+
+ /**
+ * FileManager's content format is specified by:
+ * Key: "fileURI" Value: provided "advertiseURI" param
+ * @return Map of content format keys supporting FileManager
+ */
+ public Map getContentFormat() {
+ Map result = new HashMap(super.getContentFormat());
+ result.put("fileURI", advertiseURI);
+ return result;
+ }
+
+ /**
+ * Factory Data.
+ */
+ private static class FactoryData {
+ private final boolean append;
+ private final boolean immediateFlush;
+ private final String advertiseURI;
+
+ /**
+ * Constructor.
+ *
+ * @param append
+ * Append status.
+ */
+ public FactoryData(boolean append, boolean immediateFlush, String advertiseURI) {
+ this.append = append;
+ this.immediateFlush = immediateFlush;
+ this.advertiseURI = advertiseURI;
+ }
+ }
+
+ /**
+ * Factory to create a FastFileManager.
+ */
+ private static class FastFileManagerFactory implements
+ ManagerFactory {
+
+ /**
+ * Create a FastFileManager.
+ *
+ * @param name
+ * The name of the File.
+ * @param data
+ * The FactoryData
+ * @return The FastFileManager for the File.
+ */
+ public FastFileManager createManager(String name, FactoryData data) {
+ File file = new File(name);
+ final File parent = file.getParentFile();
+ if (null != parent && !parent.exists()) {
+ parent.mkdirs();
+ }
+ if (!data.append) {
+ file.delete();
+ }
+
+ OutputStream os = new DummyOutputStream();
+ RandomAccessFile raf;
+ try {
+ raf = new RandomAccessFile(name, "rw");
+ return new FastFileManager(raf, name, os, data.immediateFlush,
+ data.advertiseURI);
+ } catch (Exception ex) {
+ LOGGER.error("FastFileManager (" + name + ") " + ex);
+ }
+ return null;
+ }
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileAppender.java (working copy)
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async.appender;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RollingFileManager;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.Advertiser;
+
+/**
+ * An appender that writes to random access files and can roll over at
+ * intervals.
+ */
+@Plugin(name = "FastRollingFile", type = "Core", elementType = "appender", printObject = true)
+public final class FastRollingFileAppender extends AbstractOutputStreamAppender {
+
+ private final String fileName;
+ private final String filePattern;
+ private Object advertisement;
+ private final Advertiser advertiser;
+
+ private FastRollingFileAppender(String name, Layout> layout, Filter filter,
+ RollingFileManager manager, String fileName,
+ String filePattern, boolean handleException,
+ boolean immediateFlush, Advertiser advertiser) {
+ super(name, layout, filter, handleException, immediateFlush, manager);
+ if (advertiser != null) {
+ Map configuration = new HashMap(layout.getContentFormat());
+ configuration.put("contentType", layout.getContentType());
+ configuration.put("name", name);
+ advertisement = advertiser.advertise(configuration);
+ }
+ this.fileName = fileName;
+ this.filePattern = filePattern;
+ this.advertiser = advertiser;
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ if (advertiser != null) {
+ advertiser.unadvertise(advertisement);
+ }
+ }
+
+ /**
+ * Write the log entry rolling over the file when required.
+
+ * @param event The LogEvent.
+ */
+ @Override
+ public void append(final LogEvent event) {
+ ((RollingFileManager) getManager()).checkRollover(event);
+
+ // Leverage the nice batching behaviour of async Loggers/Appenders:
+ // we can signal the file manager that it needs to flush the buffer
+ // to disk at the end of a batch.
+ // From a user's point of view, this means that all log events are
+ // _always_ available in the log file, without incurring the overhead
+ // of immediateFlush=true.
+ //
+ // without LOG4J2-164:
+ // if (event.getClass() == RingBufferLogEvent.class) {
+ // boolean isEndOfBatch = ((RingBufferLogEvent) event).isEndOfBatch();
+ // ((FastRollingFileManager) getManager()).setEndOfBatch(isEndOfBatch);
+ // }
+ ((FastRollingFileManager) getManager()).setEndOfBatch(event.isEndOfBatch());
+ super.append(event);
+ }
+
+ /**
+ * Returns the File name for the Appender.
+ * @return The file name.
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * Returns the file pattern used when rolling over.
+ * @return The file pattern.
+ */
+ public String getFilePattern() {
+ return filePattern;
+ }
+
+ /**
+ * Create a FastRollingFileAppender.
+ * @param fileName The name of the file that is actively written to. (required).
+ * @param filePattern The pattern of the file name to use on rollover. (required).
+ * @param append If true, events are appended to the file. If false, the file
+ * is overwritten when opened. Defaults to "true"
+ * @param name The name of the Appender (required).
+ * @param immediateFlush When true, events are immediately flushed. Defaults to "true".
+ * @param policy The triggering policy. (required).
+ * @param strategy The rollover strategy. Defaults to DefaultRolloverStrategy.
+ * @param layout The layout to use (defaults to the default PatternLayout).
+ * @param filter The Filter or null.
+ * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
+ * The default is "true".
+ * @param advertise "true" if the appender configuration should be advertised, "false" otherwise.
+ * @param advertiseURI The advertised URI which can be used to retrieve the file contents.
+ * @param config The Configuration.
+ * @return A FastRollingFileAppender.
+ */
+ @PluginFactory
+ public static FastRollingFileAppender createAppender(@PluginAttr("fileName") final String fileName,
+ @PluginAttr("filePattern") final String filePattern,
+ @PluginAttr("append") final String append,
+ @PluginAttr("name") final String name,
+ @PluginAttr("immediateFlush") final String immediateFlush,
+ @PluginElement("policy") final TriggeringPolicy policy,
+ @PluginElement("strategy") RolloverStrategy strategy,
+ @PluginElement("layout") Layout> layout,
+ @PluginElement("filter") final Filter filter,
+ @PluginAttr("suppressExceptions") final String suppress,
+ @PluginAttr("advertise") final String advertise,
+ @PluginAttr("advertiseURI") final String advertiseURI,
+ @PluginConfiguration final Configuration config) {
+
+ final boolean isAppend = append == null ? true : Boolean.valueOf(append);
+ final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
+ final boolean isFlush = immediateFlush == null ? true : Boolean.valueOf(immediateFlush);
+ boolean isAdvertise = advertise == null ? false : Boolean.valueOf(advertise);
+
+ if (name == null) {
+ LOGGER.error("No name provided for FileAppender");
+ return null;
+ }
+
+ if (fileName == null) {
+ LOGGER.error("No filename was provided for FileAppender with name " + name);
+ return null;
+ }
+
+ if (filePattern == null) {
+ LOGGER.error("No filename pattern provided for FileAppender with name " + name);
+ return null;
+ }
+
+ if (policy == null) {
+ LOGGER.error("A TriggeringPolicy must be provided");
+ return null;
+ }
+
+ if (strategy == null) {
+ strategy = DefaultRolloverStrategy.createStrategy(null, null, "true", config);
+ }
+
+ final FastRollingFileManager manager = FastRollingFileManager
+ .getFastRollingFileManager(fileName, filePattern, isAppend,
+ isFlush, policy, strategy, advertiseURI);
+ if (manager == null) {
+ return null;
+ }
+
+ if (layout == null) {
+ layout = PatternLayout.createLayout(null, null, null, null);
+ }
+
+ return new FastRollingFileAppender(name, layout, filter, manager, fileName, filePattern,
+ handleExceptions, isFlush, isAdvertise ? config.getAdvertiser() : null);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/appender/FastRollingFileManager.java (working copy)
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async.appender;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+
+import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.rolling.RollingFileManager;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+
+public class FastRollingFileManager extends RollingFileManager {
+ private static FastRollingFileManagerFactory factory = new FastRollingFileManagerFactory();
+
+ private final boolean isImmediateFlush;
+ private final RandomAccessFile randomAccessFile;
+ private final ByteBuffer buffer;
+ private ThreadLocal isEndOfBatch = new ThreadLocal();
+
+ public FastRollingFileManager(RandomAccessFile raf, String fileName,
+ String pattern, OutputStream os, boolean append,
+ boolean immediateFlush, long size, long time, TriggeringPolicy policy,
+ RolloverStrategy strategy, String advertiseURI) {
+ super(fileName, pattern, os, append, size, time, policy, strategy, advertiseURI);
+ this.isImmediateFlush = immediateFlush;
+ this.randomAccessFile = raf;
+ isEndOfBatch.set(Boolean.FALSE);
+ buffer = ByteBuffer.allocate(256 * 1024); // TODO make configurable?
+ }
+
+ public static FastRollingFileManager getFastRollingFileManager(
+ String fileName, String filePattern, boolean isAppend,
+ boolean immediateFlush, TriggeringPolicy policy,
+ RolloverStrategy strategy, String advertiseURI) {
+ return (FastRollingFileManager) getManager(fileName, new FactoryData(
+ filePattern, isAppend, immediateFlush, policy, strategy,
+ advertiseURI), factory);
+ }
+
+ public Boolean isEndOfBatch() {
+ return isEndOfBatch.get();
+ }
+
+ public void setEndOfBatch(boolean isEndOfBatch) {
+ this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
+ }
+
+ @Override
+ protected synchronized void write(byte[] bytes, int offset, int length) {
+ super.write(bytes, offset, length); // writes to dummy output stream
+
+ if (length > buffer.remaining()) {
+ flush();
+ }
+ buffer.put(bytes, offset, length);
+ if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
+ flush();
+ }
+ }
+
+ @Override
+ public void flush() {
+ buffer.flip();
+ try {
+ randomAccessFile.write(buffer.array(), 0, buffer.limit());
+ } catch (IOException ex) {
+ String msg = "Error writing to RandomAccessFile " + getName();
+ throw new AppenderRuntimeException(msg, ex);
+ }
+ buffer.clear();
+ }
+
+ /**
+ * Factory to create a FastRollingFileManager.
+ */
+ private static class FastRollingFileManagerFactory implements
+ ManagerFactory {
+
+ /**
+ * Create the FastRollingFileManager.
+ *
+ * @param name
+ * The name of the entity to manage.
+ * @param data
+ * The data required to create the entity.
+ * @return a RollingFileManager.
+ */
+ public FastRollingFileManager createManager(String name,
+ FactoryData data) {
+ File file = new File(name);
+ final File parent = file.getParentFile();
+ if (null != parent && !parent.exists()) {
+ parent.mkdirs();
+ }
+ if (!data.append) {
+ file.delete();
+ }
+ long size = data.append ? file.length() : 0;
+ long time = file.lastModified();
+
+ RandomAccessFile raf;
+ try {
+ raf = new RandomAccessFile(name, "rw");
+ return new FastRollingFileManager(raf, name, data.pattern,
+ new DummyOutputStream(), data.append,
+ data.immediateFlush, size, time, data.policy,
+ data.strategy, data.advertiseURI);
+ } catch (FileNotFoundException ex) {
+ LOGGER.error("FastRollingFileManager (" + name + ") " + ex);
+ }
+ return null;
+ }
+ }
+
+ private static class DummyOutputStream extends OutputStream {
+ @Override
+ public void write(int b) throws IOException {
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ }
+ }
+
+ /**
+ * Factory data.
+ */
+ private static class FactoryData {
+ private final String pattern;
+ private final boolean append;
+ private final boolean immediateFlush;
+ private final TriggeringPolicy policy;
+ private final RolloverStrategy strategy;
+ private final String advertiseURI;
+
+ /**
+ * Create the data for the factory.
+ *
+ * @param pattern
+ * The pattern.
+ * @param append
+ * The append flag.
+ * @param immediateFlush
+ */
+ public FactoryData(String pattern, boolean append,
+ boolean immediateFlush, TriggeringPolicy policy,
+ RolloverStrategy strategy, String advertiseURI) {
+ this.pattern = pattern;
+ this.append = append;
+ this.immediateFlush = immediateFlush;
+ this.policy = policy;
+ this.strategy = strategy;
+ this.advertiseURI = advertiseURI;
+ }
+ }
+
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AbstractMultithreadedClaimStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AbstractMultithreadedClaimStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AbstractMultithreadedClaimStrategy.java (working copy)
@@ -0,0 +1,123 @@
+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.MutableLong;
+
+public abstract class AbstractMultithreadedClaimStrategy implements ClaimStrategy
+{
+ private final int bufferSize;
+ private final Sequence claimSequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
+ private final ThreadLocal minGatingSequenceThreadLocal = new ThreadLocal()
+ {
+ @Override
+ protected MutableLong initialValue()
+ {
+ return new MutableLong(Sequencer.INITIAL_CURSOR_VALUE);
+ }
+ };
+
+ public AbstractMultithreadedClaimStrategy(int bufferSize)
+ {
+ this.bufferSize = bufferSize;
+ }
+
+// @Override
+ public int getBufferSize()
+ {
+ return bufferSize;
+ }
+
+// @Override
+ public long getSequence()
+ {
+ return claimSequence.get();
+ }
+
+// @Override
+ public boolean hasAvailableCapacity(final int availableCapacity, final Sequence[] dependentSequences)
+ {
+ return hasAvailableCapacity(claimSequence.get(), availableCapacity, dependentSequences);
+ }
+
+// @Override
+ public long incrementAndGet(final Sequence[] dependentSequences)
+ {
+ final long nextSequence = claimSequence.incrementAndGet();
+ waitForFreeSlotAt(nextSequence, dependentSequences, minGatingSequenceThreadLocal.get());
+
+ return nextSequence;
+ }
+
+// @Override
+ public long checkAndIncrement(int availableCapacity, int delta, Sequence[] gatingSequences) throws InsufficientCapacityException
+ {
+ for (;;)
+ {
+ long sequence = claimSequence.get();
+ if (hasAvailableCapacity(sequence, availableCapacity, gatingSequences))
+ {
+ long nextSequence = sequence + delta;
+ if (claimSequence.compareAndSet(sequence, nextSequence))
+ {
+ return nextSequence;
+ }
+ }
+ else
+ {
+ throw InsufficientCapacityException.INSTANCE;
+ }
+ }
+ }
+
+// @Override
+ public long incrementAndGet(final int delta, final Sequence[] dependentSequences)
+ {
+ final long nextSequence = claimSequence.addAndGet(delta);
+ waitForFreeSlotAt(nextSequence, dependentSequences, minGatingSequenceThreadLocal.get());
+
+ return nextSequence;
+ }
+
+// @Override
+ public void setSequence(final long sequence, final Sequence[] dependentSequences)
+ {
+ claimSequence.set(sequence);
+ waitForFreeSlotAt(sequence, dependentSequences, minGatingSequenceThreadLocal.get());
+ }
+
+ private void waitForFreeSlotAt(final long sequence, final Sequence[] dependentSequences, final MutableLong minGatingSequence)
+ {
+ final long wrapPoint = sequence - bufferSize;
+ if (wrapPoint > minGatingSequence.get())
+ {
+ long minSequence;
+ while (wrapPoint > (minSequence = getMinimumSequence(dependentSequences)))
+ {
+ LockSupport.parkNanos(1L);
+ }
+
+ minGatingSequence.set(minSequence);
+ }
+ }
+
+ private boolean hasAvailableCapacity(long sequence, final int availableCapacity, final Sequence[] dependentSequences)
+ {
+ final long wrapPoint = (sequence + availableCapacity) - bufferSize;
+ final MutableLong minGatingSequence = minGatingSequenceThreadLocal.get();
+ if (wrapPoint > minGatingSequence.get())
+ {
+ long minSequence = getMinimumSequence(dependentSequences);
+ minGatingSequence.set(minSequence);
+
+ if (wrapPoint > minSequence)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AggregateEventHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AggregateEventHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AggregateEventHandler.java (working copy)
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+
+/**
+ * An aggregate collection of {@link EventHandler}s that get called in sequence for each event.
+ *
+ * @param event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public final class AggregateEventHandler
+ implements EventHandler, LifecycleAware
+{
+ private final EventHandler[] eventHandlers;
+
+ /**
+ * Construct an aggregate collection of {@link EventHandler}s to be called in sequence.
+ *
+ * @param eventHandlers to be called in sequence.
+ */
+ public AggregateEventHandler(final EventHandler... eventHandlers)
+ {
+ this.eventHandlers = eventHandlers;
+ }
+
+// @Override
+ public void onEvent(final T event, final long sequence, final boolean endOfBatch)
+ throws Exception
+ {
+ for (final EventHandler eventHandler : eventHandlers)
+ {
+ eventHandler.onEvent(event, sequence, endOfBatch);
+ }
+ }
+
+// @Override
+ public void onStart()
+ {
+ for (final EventHandler eventHandler : eventHandlers)
+ {
+ if (eventHandler instanceof LifecycleAware)
+ {
+ ((LifecycleAware)eventHandler).onStart();
+ }
+ }
+ }
+
+// @Override
+ public void onShutdown()
+ {
+ for (final EventHandler eventHandler : eventHandlers)
+ {
+ if (eventHandler instanceof LifecycleAware)
+ {
+ ((LifecycleAware)eventHandler).onShutdown();
+ }
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AlertException.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AlertException.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/AlertException.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;
+
+
+/**
+ * Used to alert {@link EventProcessor}s waiting at a {@link SequenceBarrier} of status changes.
+ *
+ * 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/BatchDescriptor.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BatchDescriptor.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BatchDescriptor.java (working copy)
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+
+/**
+ * Used to record the batch of sequences claimed via a {@link Sequencer}.
+ */
+public final class BatchDescriptor
+{
+ private final int size;
+ private long end = Sequencer.INITIAL_CURSOR_VALUE;
+
+ /**
+ * Create a holder for tracking a batch of claimed sequences in a {@link Sequencer}
+ * @param size of the batch to claim.
+ */
+ BatchDescriptor(final int size)
+ {
+ this.size = size;
+ }
+
+ /**
+ * Get the end sequence of a batch.
+ *
+ * @return the end sequence in a batch
+ */
+ public long getEnd()
+ {
+ return end;
+ }
+
+ /**
+ * Set the end of the batch sequence. To be used by the {@link Sequencer}.
+ *
+ * @param end sequence in the batch.
+ */
+ void setEnd(final long end)
+ {
+ this.end = end;
+ }
+
+ /**
+ * Get the size of the batch.
+ *
+ * @return the size of the batch.
+ */
+ public int getSize()
+ {
+ return size;
+ }
+
+ /**
+ * Get the starting sequence for a batch.
+ *
+ * @return the starting sequence of a batch.
+ */
+ public long getStart()
+ {
+ return end - (size - 1L);
+ }
+}
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,168 @@
+/*
+ * 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 a {@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 event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public final class BatchEventProcessor
+ implements EventProcessor
+{
+ private final AtomicBoolean running = new AtomicBoolean(false);
+ private ExceptionHandler exceptionHandler = new FatalExceptionHandler();
+ private final RingBuffer ringBuffer;
+ private final SequenceBarrier sequenceBarrier;
+ private final EventHandler eventHandler;
+ private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
+
+ /**
+ * Construct a {@link EventProcessor} that will automatically track the progress by updating its sequence when
+ * the {@link EventHandler#onEvent(Object, long, boolean)} method returns.
+ *
+ * @param ringBuffer to which events are published.
+ * @param sequenceBarrier on which it is waiting.
+ * @param eventHandler is the delegate to which events are dispatched.
+ */
+ public BatchEventProcessor(final RingBuffer ringBuffer,
+ final SequenceBarrier sequenceBarrier,
+ final EventHandler eventHandler)
+ {
+ this.ringBuffer = ringBuffer;
+ this.sequenceBarrier = sequenceBarrier;
+ this.eventHandler = eventHandler;
+
+ if (eventHandler instanceof SequenceReportingEventHandler)
+ {
+ ((SequenceReportingEventHandler>)eventHandler).setSequenceCallback(sequence);
+ }
+ }
+
+// @Override
+ public Sequence getSequence()
+ {
+ return sequence;
+ }
+
+// @Override
+ public void halt()
+ {
+ running.set(false);
+ sequenceBarrier.alert();
+ }
+
+ /**
+ * Set a new {@link ExceptionHandler} for handling exceptions propagated out of the {@link BatchEventProcessor}
+ *
+ * @param exceptionHandler to replace the existing exceptionHandler.
+ */
+ public void setExceptionHandler(final ExceptionHandler exceptionHandler)
+ {
+ if (null == exceptionHandler)
+ {
+ throw new NullPointerException();
+ }
+
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ /**
+ * It is ok to have another thread rerun this method after a halt().
+ */
+// @Override
+ public void run()
+ {
+ if (!running.compareAndSet(false, true))
+ {
+ throw new IllegalStateException("Thread is already running");
+ }
+ sequenceBarrier.clearAlert();
+
+ notifyStart();
+
+ T event = null;
+ long nextSequence = sequence.get() + 1L;
+ while (true)
+ {
+ try
+ {
+ final long availableSequence = sequenceBarrier.waitFor(nextSequence);
+ while (nextSequence <= availableSequence)
+ {
+ event = ringBuffer.get(nextSequence);
+ eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
+ nextSequence++;
+ }
+
+ sequence.set(nextSequence - 1L);
+ }
+ catch (final AlertException ex)
+ {
+ if (!running.get())
+ {
+ break;
+ }
+ }
+ catch (final Throwable ex)
+ {
+ exceptionHandler.handleEventException(ex, nextSequence, event);
+ sequence.set(nextSequence);
+ nextSequence++;
+ }
+ }
+
+ notifyShutdown();
+
+ running.set(false);
+ }
+
+ private void notifyStart()
+ {
+ if (eventHandler instanceof LifecycleAware)
+ {
+ try
+ {
+ ((LifecycleAware)eventHandler).onStart();
+ }
+ catch (final Throwable ex)
+ {
+ exceptionHandler.handleOnStartException(ex);
+ }
+ }
+ }
+
+ private void notifyShutdown()
+ {
+ if (eventHandler instanceof LifecycleAware)
+ {
+ try
+ {
+ ((LifecycleAware)eventHandler).onShutdown();
+ }
+ catch (final Throwable ex)
+ {
+ exceptionHandler.handleOnShutdownException(ex);
+ }
+ }
+ }
+}
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BlockingWaitStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BlockingWaitStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BlockingWaitStrategy.java (working copy)
@@ -0,0 +1,119 @@
+/*
+ * 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.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Blocking strategy that uses a lock and condition variable for {@link EventProcessor}s waiting on a barrier.
+ *
+ * This strategy can be used when throughput and low-latency are not as important as CPU resource.
+ */
+public final class BlockingWaitStrategy implements WaitStrategy
+{
+ private final Lock lock = new ReentrantLock();
+ private final Condition processorNotifyCondition = lock.newCondition();
+
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier)
+ throws AlertException, InterruptedException
+ {
+ long availableSequence;
+ if ((availableSequence = cursor.get()) < sequence)
+ {
+ lock.lock();
+ try
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ barrier.checkAlert();
+ processorNotifyCondition.await();
+ }
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ if (0 != dependents.length)
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ barrier.checkAlert();
+ }
+ }
+
+ return availableSequence;
+ }
+
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier,
+ final long timeout, final TimeUnit sourceUnit)
+ throws AlertException, InterruptedException
+ {
+ long availableSequence;
+ if ((availableSequence = cursor.get()) < sequence)
+ {
+ lock.lock();
+ try
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ barrier.checkAlert();
+
+ if (!processorNotifyCondition.await(timeout, sourceUnit))
+ {
+ break;
+ }
+ }
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ if (0 != dependents.length)
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < 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/BusySpinWaitStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BusySpinWaitStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/BusySpinWaitStrategy.java (working copy)
@@ -0,0 +1,97 @@
+/*
+ * 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.TimeUnit;
+
+/**
+ * Busy Spin strategy that uses a busy spin loop for {@link org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor}s waiting on a barrier.
+ *
+ * This strategy will use CPU resource to avoid syscalls which can introduce latency jitter. It is best
+ * used when threads can be bound to specific CPU cores.
+ */
+public final class BusySpinWaitStrategy implements WaitStrategy
+{
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier)
+ throws AlertException, InterruptedException
+ {
+ long availableSequence;
+
+ if (0 == dependents.length)
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ barrier.checkAlert();
+ }
+ }
+ else
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ barrier.checkAlert();
+ }
+ }
+
+ return availableSequence;
+ }
+
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier,
+ final long timeout, final TimeUnit sourceUnit)
+ throws AlertException, InterruptedException
+ {
+ final long timeoutMs = sourceUnit.toMillis(timeout);
+ final long startTime = System.currentTimeMillis();
+ long availableSequence;
+
+ if (0 == dependents.length)
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ barrier.checkAlert();
+
+ final long elapsedTime = System.currentTimeMillis() - startTime;
+ if (elapsedTime > timeoutMs)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ barrier.checkAlert();
+
+ final long elapsedTime = System.currentTimeMillis() - startTime;
+ if (elapsedTime > timeoutMs)
+ {
+ break;
+ }
+ }
+ }
+
+ return availableSequence;
+ }
+
+// @Override
+ public void signalAllWhenBlocking()
+ {
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ClaimStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ClaimStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ClaimStrategy.java (working copy)
@@ -0,0 +1,96 @@
+/*
+ * 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 contract for claiming the sequence of events in the {@link Sequencer} by event publishers.
+ */
+public interface ClaimStrategy
+{
+ /**
+ * Get the size of the data structure used to buffer events.
+ *
+ * @return size of the underlying buffer.
+ */
+ int getBufferSize();
+
+ /**
+ * Get the current claimed sequence.
+ *
+ * @return the current claimed sequence.
+ */
+ long getSequence();
+
+ /**
+ * Is there available capacity in the buffer for the requested sequence.
+ *
+ * @param availableCapacity remaining in the buffer.
+ * @param dependentSequences to be checked for range.
+ * @return true if the buffer has capacity for the requested sequence.
+ */
+ boolean hasAvailableCapacity(final int availableCapacity, final Sequence[] dependentSequences);
+
+ /**
+ * Claim the next sequence in the {@link Sequencer}.
+ * The caller should be held up until the claimed sequence is available by tracking the dependentSequences.
+ *
+ * @param dependentSequences to be checked for range.
+ * @return the index to be used for the publishing.
+ */
+ long incrementAndGet(final Sequence[] dependentSequences);
+
+ /**
+ * Increment sequence by a delta and get the result.
+ * The caller should be held up until the claimed sequence batch is available by tracking the dependentSequences.
+ *
+ * @param delta to increment by.
+ * @param dependentSequences to be checked for range.
+ * @return the result after incrementing.
+ */
+ long incrementAndGet(final int delta, final Sequence[] dependentSequences);
+
+ /**
+ * Set the current sequence value for claiming an event in the {@link Sequencer}
+ * The caller should be held up until the claimed sequence is available by tracking the dependentSequences.
+ *
+ * @param dependentSequences to be checked for range.
+ * @param sequence to be set as the current value.
+ */
+ void setSequence(final long sequence, final Sequence[] dependentSequences);
+
+ /**
+ * Serialise publishers in sequence and set cursor to latest available sequence.
+ *
+ * @param sequence sequence to be applied
+ * @param cursor to serialise against.
+ * @param batchSize of the sequence.
+ */
+ void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize);
+
+ /**
+ * Atomically checks the available capacity of the ring buffer and claims the next sequence. Will
+ * throw InsufficientCapacityException if the capacity not available.
+ *
+ * @param availableCapacity the capacity that should be available before claiming the next slot
+ * @param delta the number of slots to claim
+ * @param gatingSequences the set of sequences to check to ensure capacity is available
+ * @return the slot after incrementing
+ * @throws InsufficientCapacityException thrown if capacity is not available
+ */
+ long checkAndIncrement(int availableCapacity, int delta, Sequence[] gatingSequences)
+ throws InsufficientCapacityException;
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventFactory.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventFactory.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventFactory.java (working copy)
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+
+/**
+ * Called by the {@link RingBuffer} to pre-populate all the events to fill the RingBuffer.
+ *
+ * @param event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public interface EventFactory
+{
+ T newInstance();
+}
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventHandler.java (working copy)
@@ -0,0 +1,37 @@
+/*
+ * 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 events as they become available in the {@link RingBuffer}
+ *
+ * @see BatchEventProcessor#setExceptionHandler(ExceptionHandler) if you want to handle exceptions propigated out of the handler.
+ *
+ * @param event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public interface EventHandler
+{
+ /**
+ * Called when a publisher has published an event to the {@link RingBuffer}
+ *
+ * @param event published to the {@link RingBuffer}
+ * @param sequence of the event being processed
+ * @param endOfBatch flag to indicate if this is the last event in a batch from the {@link RingBuffer}
+ * @throws Exception if the EventHandler would like the exception handled further up the chain.
+ */
+ void onEvent(T event, long sequence, boolean endOfBatch) throws Exception;
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventProcessor.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventProcessor.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventProcessor.java (working copy)
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * EventProcessors waitFor events to become available for consumption from the {@link RingBuffer}
+ *
+ * An EventProcessor will be associated with a Thread for execution.
+ */
+public interface EventProcessor extends Runnable
+{
+ /**
+ * Get a reference to the {@link Sequence} being used by this {@link EventProcessor}.
+ *
+ * @return reference to the {@link Sequence} for this {@link EventProcessor}
+ */
+ Sequence getSequence();
+
+ /**
+ * Signal that this EventProcessor should stop when it has finished consuming at the next clean break.
+ * It will call {@link SequenceBarrier#alert()} to notify the thread to check status.
+ */
+ void halt();
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventPublisher.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventPublisher.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventPublisher.java (working copy)
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+
+
+/**
+ * Utility class for simplifying publication to the ring buffer.
+ */
+public class EventPublisher
+{
+ private final RingBuffer ringBuffer;
+
+ /**
+ * Construct from the ring buffer to be published to.
+ * @param ringBuffer into which events will be published.
+ */
+ public EventPublisher(final RingBuffer ringBuffer)
+ {
+ this.ringBuffer = ringBuffer;
+ }
+
+ /**
+ * Publishes an event to the ring buffer. It handles
+ * claiming the next sequence, getting the current (uninitialized)
+ * event from the ring buffer and publishing the claimed sequence
+ * after translation.
+ *
+ * @param translator The user specified translation for the event
+ */
+ public void publishEvent(final EventTranslator translator)
+ {
+ final long sequence = ringBuffer.next();
+ translateAndPublish(translator, sequence);
+ }
+
+ /**
+ * Attempts to publish an event to the ring buffer. It handles
+ * claiming the next sequence, getting the current (uninitialized)
+ * event from the ring buffer and publishing the claimed sequence
+ * after translation. Will return false if specified capacity
+ * was not available.
+ *
+ * @param translator The user specified translation for the event
+ * @param capacity The capacity that should be available before publishing
+ * @return true if the value was published, false if there was insufficient
+ * capacity.
+ */
+ public boolean tryPublishEvent(EventTranslator translator, int capacity)
+ {
+ try
+ {
+ final long sequence = ringBuffer.tryNext(capacity);
+ translateAndPublish(translator, sequence);
+ return true;
+ }
+ catch (InsufficientCapacityException e)
+ {
+ return false;
+ }
+ }
+
+ private void translateAndPublish(final EventTranslator translator, final long sequence)
+ {
+ try
+ {
+ translator.translateTo(ringBuffer.get(sequence), sequence);
+ }
+ finally
+ {
+ ringBuffer.publish(sequence);
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventTranslator.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventTranslator.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/EventTranslator.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;
+
+
+/**
+ * Implementations translate another data representations into events claimed from the {@link RingBuffer}
+ *
+ * @param event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public interface EventTranslator
+{
+ /**
+ * Translate a data representation into fields set in given event
+ *
+ * @param event into which the data should be translated.
+ * @param sequence that is assigned to event.
+ */
+ void translateTo(final T event, long sequence);
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ExceptionHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ExceptionHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ExceptionHandler.java (working copy)
@@ -0,0 +1,49 @@
+/*
+ * 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 handler for uncaught exceptions in the event processing cycle of the {@link BatchEventProcessor}
+ */
+public interface ExceptionHandler
+{
+ /**
+ * Strategy for handling uncaught exceptions when processing an event.
+ *
+ * If the strategy wishes to suspend further processing by the {@link BatchEventProcessor}
+ * then is should throw a {@link RuntimeException}.
+ *
+ * @param ex the exception that propagated from the {@link EventHandler}.
+ * @param sequence of the event which cause the exception.
+ * @param event being processed when the exception occurred.
+ */
+ void handleEventException(Throwable ex, long sequence, Object event);
+
+ /**
+ * Callback to notify of an exception during {@link LifecycleAware#onStart()}
+ *
+ * @param ex throw during the starting process.
+ */
+ void handleOnStartException(Throwable ex);
+
+ /**
+ * Callback to notify of an exception during {@link LifecycleAware#onShutdown()}
+ *
+ * @param ex throw during the shutdown process.
+ */
+ void handleOnShutdownException(Throwable ex);
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/FatalExceptionHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/FatalExceptionHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/FatalExceptionHandler.java (working copy)
@@ -0,0 +1,59 @@
+/*
+ * 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.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Convenience implementation of an exception handler that using standard JDK logging to log
+ * the exception as {@link Level}.SEVERE and re-throw it wrapped in a {@link RuntimeException}
+ */
+public final class FatalExceptionHandler implements ExceptionHandler
+{
+ private final static Logger LOGGER = Logger.getLogger(FatalExceptionHandler.class.getName());
+ private final Logger logger;
+
+ public FatalExceptionHandler()
+ {
+ this.logger = LOGGER;
+ }
+
+ public FatalExceptionHandler(final Logger logger)
+ {
+ this.logger = logger;
+ }
+
+// @Override
+ public void handleEventException(final Throwable ex, final long sequence, final Object event)
+ {
+ logger.log(Level.SEVERE, "Exception processing: " + sequence + " " + event, ex);
+
+ throw new RuntimeException(ex);
+ }
+
+// @Override
+ public void handleOnStartException(final Throwable ex)
+ {
+ logger.log(Level.SEVERE, "Exception during onStart()", ex);
+ }
+
+// @Override
+ public void handleOnShutdownException(final Throwable ex)
+ {
+ logger.log(Level.SEVERE, "Exception during onShutdown()", ex);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/IgnoreExceptionHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/IgnoreExceptionHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/IgnoreExceptionHandler.java (working copy)
@@ -0,0 +1,57 @@
+/*
+ * 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.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Convenience implementation of an exception handler that using standard JDK logging to log
+ * the exception as {@link Level}.INFO
+ */
+public final class IgnoreExceptionHandler implements ExceptionHandler
+{
+ private final static Logger LOGGER = Logger.getLogger(IgnoreExceptionHandler.class.getName());
+ private final Logger logger;
+
+ public IgnoreExceptionHandler()
+ {
+ this.logger = LOGGER;
+ }
+
+ public IgnoreExceptionHandler(final Logger logger)
+ {
+ this.logger = logger;
+ }
+
+// @Override
+ public void handleEventException(final Throwable ex, final long sequence, final Object event)
+ {
+ logger.log(Level.INFO, "Exception processing: " + sequence + " " + event, ex);
+ }
+
+// @Override
+ public void handleOnStartException(final Throwable ex)
+ {
+ logger.log(Level.INFO, "Exception during onStart()", ex);
+ }
+
+// @Override
+ public void handleOnShutdownException(final Throwable ex)
+ {
+ logger.log(Level.INFO, "Exception during onShutdown()", ex);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/InsufficientCapacityException.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/InsufficientCapacityException.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/InsufficientCapacityException.java (working copy)
@@ -0,0 +1,19 @@
+package org.apache.logging.log4j.async.com.lmax.disruptor;
+
+
+@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,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;
+
+
+/**
+ * Implement this interface to be notified when a thread for the {@link BatchEventProcessor} starts and shuts down.
+ */
+public interface LifecycleAware
+{
+ /**
+ * Called once on thread start before first event is available.
+ */
+ void onStart();
+
+ /**
+ * Called once just before the thread is shutdown.
+ */
+ void onShutdown();
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/MultiThreadedClaimStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/MultiThreadedClaimStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/MultiThreadedClaimStrategy.java (working copy)
@@ -0,0 +1,105 @@
+/*
+ * 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.AtomicLongArray;
+
+
+/**
+ * Strategy to be used when there are multiple publisher threads claiming sequences.
+ *
+ * This strategy is reasonably forgiving when the multiple publisher threads are highly contended or working in an
+ * environment where there is insufficient CPUs to handle multiple publisher threads. It requires 2 CAS operations
+ * for a single publisher, compared to the {@link MultiThreadedLowContentionClaimStrategy} strategy which needs only a single
+ * CAS and a lazySet per publication.
+ */
+public final class MultiThreadedClaimStrategy extends AbstractMultithreadedClaimStrategy
+ implements ClaimStrategy
+{
+ private static final int RETRIES = 1000;
+
+ private final AtomicLongArray pendingPublication;
+ private final int pendingMask;
+
+ /**
+ * Construct a new multi-threaded publisher {@link ClaimStrategy} for a given buffer size.
+ *
+ * @param bufferSize for the underlying data structure.
+ * @param pendingBufferSize number of item that can be pending for serialisation
+ */
+ public MultiThreadedClaimStrategy(final int bufferSize, final int pendingBufferSize)
+ {
+ super(bufferSize);
+
+ if (Integer.bitCount(pendingBufferSize) != 1)
+ {
+ throw new IllegalArgumentException("pendingBufferSize must be a power of 2, was: " + pendingBufferSize);
+ }
+
+ this.pendingPublication = new AtomicLongArray(pendingBufferSize);
+ this.pendingMask = pendingBufferSize - 1;
+ }
+
+ /**
+ * Construct a new multi-threaded publisher {@link ClaimStrategy} for a given buffer size.
+ *
+ * @param bufferSize for the underlying data structure.
+ */
+ public MultiThreadedClaimStrategy(final int bufferSize)
+ {
+ this(bufferSize, 1024);
+ }
+
+// @Override
+ public void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize)
+ {
+ int counter = RETRIES;
+ while (sequence - cursor.get() > pendingPublication.length())
+ {
+ if (--counter == 0)
+ {
+ Thread.yield();
+ counter = RETRIES;
+ }
+ }
+
+ long expectedSequence = sequence - batchSize;
+ for (long pendingSequence = expectedSequence + 1; pendingSequence < sequence; pendingSequence++)
+ {
+ pendingPublication.lazySet((int) pendingSequence & pendingMask, pendingSequence);
+ }
+ pendingPublication.set((int) sequence & pendingMask, sequence);
+
+ long cursorSequence = cursor.get();
+ if (cursorSequence >= sequence)
+ {
+ return;
+ }
+
+ expectedSequence = Math.max(expectedSequence, cursorSequence);
+ long nextSequence = expectedSequence + 1;
+ while (cursor.compareAndSet(expectedSequence, nextSequence))
+ {
+ expectedSequence = nextSequence;
+ nextSequence++;
+ if (pendingPublication.get((int) nextSequence & pendingMask) != nextSequence)
+ {
+ break;
+ }
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/MultiThreadedLowContentionClaimStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/MultiThreadedLowContentionClaimStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/MultiThreadedLowContentionClaimStrategy.java (working copy)
@@ -0,0 +1,50 @@
+/*
+ * 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 to be used when there are multiple publisher threads claiming sequences.
+ *
+ * This strategy requires sufficient cores to allow multiple publishers to be concurrently claiming sequences and those
+ * thread a contented relatively infrequently.
+ */
+public final class MultiThreadedLowContentionClaimStrategy
+ extends AbstractMultithreadedClaimStrategy
+{
+ /**
+ * Construct a new multi-threaded publisher {@link ClaimStrategy} for a given buffer size.
+ *
+ * @param bufferSize for the underlying data structure.
+ */
+ public MultiThreadedLowContentionClaimStrategy(final int bufferSize)
+ {
+ super(bufferSize);
+ }
+
+// @Override
+ public void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize)
+ {
+ final long expectedSequence = sequence - batchSize;
+ while (expectedSequence != cursor.get())
+ {
+ // busy spin
+ }
+
+ cursor.set(sequence);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/NoOpEventProcessor.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/NoOpEventProcessor.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/NoOpEventProcessor.java (working copy)
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+
+/**
+ * No operation version of a {@link EventProcessor} that simply tracks a {@link Sequencer}.
+ * This is useful in tests or for pre-filling a {@link RingBuffer} from a publisher.
+ */
+public final class NoOpEventProcessor implements EventProcessor
+{
+ private final SequencerFollowingSequence sequence;
+
+ /**
+ * Construct a {@link EventProcessor} that simply tracks a {@link Sequencer}.
+ *
+ * @param sequencer to track.
+ */
+ public NoOpEventProcessor(final Sequencer sequencer)
+ {
+ sequence = new SequencerFollowingSequence(sequencer);
+ }
+
+// @Override
+ public Sequence getSequence()
+ {
+ return sequence;
+ }
+
+// @Override
+ public void halt()
+ {
+ }
+
+// @Override
+ public void run()
+ {
+ }
+
+ private static final class SequencerFollowingSequence extends Sequence
+ {
+ private final Sequencer sequencer;
+
+ private SequencerFollowingSequence(final Sequencer sequencer)
+ {
+ super(Sequencer.INITIAL_CURSOR_VALUE);
+ this.sequencer = sequencer;
+ }
+
+ @Override
+ public long get()
+ {
+ return sequencer.getCursor();
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ProcessingSequenceBarrier.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ProcessingSequenceBarrier.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/ProcessingSequenceBarrier.java (working copy)
@@ -0,0 +1,90 @@
+/*
+ * 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.TimeUnit;
+
+/**
+ * {@link SequenceBarrier} handed out for gating {@link EventProcessor}s on a cursor sequence and optional dependent {@link EventProcessor}(s)
+ */
+final class ProcessingSequenceBarrier implements SequenceBarrier
+{
+ private final WaitStrategy waitStrategy;
+ private final Sequence cursorSequence;
+ private final Sequence[] dependentSequences;
+ private volatile boolean alerted = false;
+
+ public ProcessingSequenceBarrier(final WaitStrategy waitStrategy,
+ final Sequence cursorSequence,
+ final Sequence[] dependentSequences)
+ {
+ this.waitStrategy = waitStrategy;
+ this.cursorSequence = cursorSequence;
+ this.dependentSequences = dependentSequences;
+ }
+
+// @Override
+ public long waitFor(final long sequence)
+ throws AlertException, InterruptedException
+ {
+ checkAlert();
+
+ return waitStrategy.waitFor(sequence, cursorSequence, dependentSequences, this);
+ }
+
+// @Override
+ public long waitFor(final long sequence, final long timeout, final TimeUnit units)
+ throws AlertException, InterruptedException
+ {
+ checkAlert();
+
+ return waitStrategy.waitFor(sequence, cursorSequence, dependentSequences, this, timeout, units);
+ }
+
+// @Override
+ public long getCursor()
+ {
+ return cursorSequence.get();
+ }
+
+// @Override
+ public boolean isAlerted()
+ {
+ return alerted;
+ }
+
+// @Override
+ public void alert()
+ {
+ alerted = true;
+ waitStrategy.signalAllWhenBlocking();
+ }
+
+// @Override
+ public void clearAlert()
+ {
+ alerted = false;
+ }
+
+// @Override
+ public void checkAlert() throws AlertException
+ {
+ if (alerted)
+ {
+ throw AlertException.INSTANCE;
+ }
+ }
+}
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/RingBuffer.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/RingBuffer.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/RingBuffer.java (working copy)
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+
+/**
+ * Ring based store of reusable entries containing the data representing an event being exchanged between event publisher and {@link EventProcessor}s.
+ *
+ * @param implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public final class RingBuffer extends Sequencer
+{
+ private final int indexMask;
+ private final Object[] entries;
+
+ /**
+ * Construct a RingBuffer with the full option set.
+ *
+ * @param eventFactory to newInstance entries for filling the RingBuffer
+ * @param claimStrategy threading strategy for publisher claiming entries in the ring.
+ * @param waitStrategy waiting strategy employed by processorsToTrack waiting on entries becoming available.
+ *
+ * @throws IllegalArgumentException if bufferSize is not a power of 2
+ */
+ public RingBuffer(final EventFactory eventFactory,
+ final ClaimStrategy claimStrategy,
+ final WaitStrategy waitStrategy)
+ {
+ super(claimStrategy, waitStrategy);
+
+ if (Integer.bitCount(claimStrategy.getBufferSize()) != 1)
+ {
+ throw new IllegalArgumentException("bufferSize must be a power of 2");
+ }
+
+ indexMask = claimStrategy.getBufferSize() - 1;
+ entries = new Object[claimStrategy.getBufferSize()];
+
+ fill(eventFactory);
+ }
+
+ /**
+ * Construct a RingBuffer with default strategies of:
+ * {@link MultiThreadedClaimStrategy} and {@link BlockingWaitStrategy}
+ *
+ * @param eventFactory to newInstance entries for filling the RingBuffer
+ * @param bufferSize of the RingBuffer that will be rounded up to the next power of 2
+ */
+ public RingBuffer(final EventFactory eventFactory, final int bufferSize)
+ {
+ this(eventFactory,
+ new MultiThreadedClaimStrategy(bufferSize),
+ new BlockingWaitStrategy());
+ }
+
+ /**
+ * Get the event for a given sequence in the RingBuffer.
+ *
+ * @param sequence for the event
+ * @return event for the sequence
+ */
+ @SuppressWarnings("unchecked")
+ public T get(final long sequence)
+ {
+ return (T)entries[(int)sequence & indexMask];
+ }
+
+ private void fill(final EventFactory eventFactory)
+ {
+ for (int i = 0; i < entries.length; i++)
+ {
+ entries[i] = eventFactory.newInstance();
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/Sequence.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/Sequence.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/Sequence.java (working copy)
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+import sun.misc.Unsafe;
+
+public class Sequence
+{
+ 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];
+
+ public Sequence()
+ {
+ setOrdered(-1);
+ }
+
+ public Sequence(final long initialValue)
+ {
+ setOrdered(initialValue);
+ }
+
+ public long get()
+ {
+ return unsafe.getLongVolatile(paddedValue, valueOffset);
+ }
+
+ public void set(final long value)
+ {
+ unsafe.putOrderedLong(paddedValue, valueOffset, value);
+ }
+
+ private void setOrdered(final long value)
+ {
+ unsafe.putOrderedLong(paddedValue, valueOffset, value);
+ }
+
+ public boolean compareAndSet(final long expectedValue, final long newValue)
+ {
+ return unsafe.compareAndSwapLong(paddedValue, valueOffset, expectedValue, newValue);
+ }
+
+ public String toString()
+ {
+ return Long.toString(get());
+ }
+
+ public long incrementAndGet()
+ {
+ return addAndGet(1L);
+ }
+
+ public long addAndGet(final long increment)
+ {
+ long currentValue;
+ long newValue;
+
+ do
+ {
+ currentValue = get();
+ newValue = currentValue + increment;
+ }
+ while (!compareAndSet(currentValue, newValue));
+
+ return newValue;
+ }
+}
+
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,78 @@
+/*
+ * 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.TimeUnit;
+
+/**
+ * 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;
+
+ /**
+ * Wait for the given sequence to be available for consumption with a time out.
+ *
+ * @param sequence to wait for
+ * @param timeout value
+ * @param units for the timeout value
+ * @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, long timeout, TimeUnit units) throws AlertException, InterruptedException;
+
+ /**
+ * Delegate a call to the {@link Sequencer#getCursor()}
+ *
+ * @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,147 @@
+/*
+ * 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.AtomicReference;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+/**
+ * {@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 final AtomicReference sequencesRef;
+
+ /**
+ * Default Constructor
+ */
+ public SequenceGroup()
+ {
+ super(-1);
+ sequencesRef = new AtomicReference(new Sequence[0]);
+ }
+
+ /**
+ * Get the minimum sequence value for the group.
+ *
+ * @return the minimum sequence value for the group.
+ */
+ @Override
+ public long get()
+ {
+ Sequence[] sequences = sequencesRef.get();
+ return sequences.length != 0 ? Util.getMinimumSequence(sequences) : RingBuffer.INITIAL_CURSOR_VALUE;
+ }
+
+ /**
+ * Set all {@link Sequence}s in the group to a given value.
+ *
+ * @param value to set the group of sequences to.
+ */
+ @Override
+ public void set(final long value)
+ {
+ final Sequence[] sequences = sequencesRef.get();
+ for (int i = 0, size = sequences.length; i < size; i++)
+ {
+ sequences[i].set(value);
+ }
+ }
+
+ /**
+ * Add a {@link Sequence} into this aggregate.
+ *
+ * @param sequence to be added to the aggregate.
+ */
+ public void add(final Sequence sequence)
+ {
+ Sequence[] oldSequences;
+ Sequence[] newSequences;
+ do
+ {
+ oldSequences = sequencesRef.get();
+ final int oldSize = oldSequences.length;
+ newSequences = new Sequence[oldSize + 1];
+ System.arraycopy(oldSequences, 0, newSequences, 0, oldSize);
+ newSequences[oldSize] = sequence;
+ }
+ while (!sequencesRef.compareAndSet(oldSequences, newSequences));
+ }
+
+ /**
+ * Remove the first occurrence of the {@link Sequence} from this aggregate.
+ *
+ * @param sequence to be removed from this aggregate.
+ * @return true if the sequence was removed otherwise false.
+ */
+ public boolean remove(final Sequence sequence)
+ {
+ boolean found;
+ int numToRemove;
+ Sequence[] oldSequences;
+ Sequence[] newSequences;
+
+ do
+ {
+ found = false;
+ oldSequences = sequencesRef.get();
+
+ numToRemove = 0;
+ for (Sequence oldSequence : oldSequences)
+ {
+ if (oldSequence == sequence)
+ {
+ numToRemove++;
+ }
+ }
+
+ if (0 == numToRemove)
+ {
+ break;
+ }
+
+ final int oldSize = oldSequences.length;
+ newSequences = new Sequence[oldSize - numToRemove];
+
+ for (int i = 0, pos = 0; i < oldSize; i++)
+ {
+ final Sequence testSequence = oldSequences[i];
+ if (sequence != testSequence)
+ {
+ newSequences[pos++] = testSequence;
+ }
+ }
+ }
+ while (!sequencesRef.compareAndSet(oldSequences, newSequences));
+
+ return numToRemove != 0;
+ }
+
+ /**
+ * Get the size of the group.
+ *
+ * @return the size of the group.
+ */
+ public int size()
+ {
+ return sequencesRef.get().length;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceReportingEventHandler.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceReportingEventHandler.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SequenceReportingEventHandler.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;
+
+
+/**
+ * Used by the {@link BatchEventProcessor} to set a callback allowing the {@link EventHandler} to notify
+ * when it has finished consuming an event if this happens after the {@link EventHandler#onEvent(Object, long, boolean)} call.
+ *
+ * Typically this would be used when the handler is performing some sort of batching operation such are writing to an IO device.
+ *
+ * @param event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public interface SequenceReportingEventHandler
+ extends EventHandler
+{
+ /**
+ * Call by the {@link BatchEventProcessor} to setup the callback.
+ *
+ * @param sequenceCallback callback on which to notify the {@link BatchEventProcessor} that the sequence has progressed.
+ */
+ void setSequenceCallback(final Sequence sequenceCallback);
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/Sequencer.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/Sequencer.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/Sequencer.java (working copy)
@@ -0,0 +1,236 @@
+/*
+ * 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 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
+ */
+public class Sequencer
+{
+ /** Set to -1 as sequence starting point */
+ public static final long INITIAL_CURSOR_VALUE = -1L;
+
+ private final Sequence cursor = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
+ private Sequence[] gatingSequences;
+
+ private final ClaimStrategy claimStrategy;
+ private final WaitStrategy waitStrategy;
+
+ /**
+ * Construct a Sequencer with the selected strategies.
+ *
+ * @param claimStrategy for those claiming sequences.
+ * @param waitStrategy for those waiting on sequences.
+ */
+ public Sequencer(final ClaimStrategy claimStrategy, final WaitStrategy waitStrategy)
+ {
+ this.claimStrategy = claimStrategy;
+ this.waitStrategy = waitStrategy;
+ }
+
+ /**
+ * Set the sequences that will gate publishers to prevent the buffer wrapping.
+ *
+ * This method must be called prior to claiming sequences otherwise
+ * a NullPointerException will be thrown.
+ *
+ * @param sequences to be to be gated on.
+ */
+ public void setGatingSequences(final Sequence... sequences)
+ {
+ this.gatingSequences = sequences;
+ }
+
+ /**
+ * Create a {@link SequenceBarrier} that gates on the the cursor and a list of {@link Sequence}s
+ *
+ * @param sequencesToTrack this barrier will track
+ * @return the barrier gated as required
+ */
+ public SequenceBarrier newBarrier(final Sequence... sequencesToTrack)
+ {
+ return new ProcessingSequenceBarrier(waitStrategy, cursor, sequencesToTrack);
+ }
+
+ /**
+ * Create a new {@link BatchDescriptor} that is the minimum of the requested size
+ * and the buffer size.
+ *
+ * @param size for the batch
+ * @return the new {@link BatchDescriptor}
+ */
+ public BatchDescriptor newBatchDescriptor(final int size)
+ {
+ return new BatchDescriptor(Math.min(size, claimStrategy.getBufferSize()));
+ }
+
+ /**
+ * The capacity of the data structure to hold entries.
+ *
+ * @return the size of the RingBuffer.
+ */
+ public int getBufferSize()
+ {
+ return claimStrategy.getBufferSize();
+ }
+
+ /**
+ * Get the value of the cursor indicating the published sequence.
+ *
+ * @return value of the cursor for events that have been published.
+ */
+ public long getCursor()
+ {
+ return cursor.get();
+ }
+
+ /**
+ * Has the buffer got capacity to allocate another sequence. This is a concurrent
+ * method so the response should only be taken as an indication of available capacity.
+ *
+ * @param availableCapacity in the buffer
+ * @return true if the buffer has the capacity to allocate the next sequence otherwise false.
+ */
+ public boolean hasAvailableCapacity(final int availableCapacity)
+ {
+ return claimStrategy.hasAvailableCapacity(availableCapacity, gatingSequences);
+ }
+
+ /**
+ * Claim the next event in sequence for publishing.
+ *
+ * @return the claimed sequence value
+ */
+ public long next()
+ {
+ if (null == gatingSequences)
+ {
+ throw new NullPointerException("gatingSequences must be set before claiming sequences");
+ }
+
+ return claimStrategy.incrementAndGet(gatingSequences);
+ }
+
+ /**
+ * Attempt to claim the next event in sequence for publishing. Will return the
+ * number of the slot if there is at least requiredCapacity slots
+ * available.
+ *
+ * @param requiredCapacity as slots in the data structure
+ * @return the claimed sequence value
+ * @throws InsufficientCapacityException when the requiredCapacity is not available
+ */
+ public long tryNext(int requiredCapacity) throws InsufficientCapacityException
+ {
+ if (null == gatingSequences)
+ {
+ throw new NullPointerException("gatingSequences must be set before claiming sequences");
+ }
+
+ if (requiredCapacity < 1)
+ {
+ throw new IllegalArgumentException("Required capacity must be greater than 0");
+ }
+
+ return claimStrategy.checkAndIncrement(requiredCapacity, 1, gatingSequences);
+ }
+
+ /**
+ * Claim the next batch of sequence numbers for publishing.
+ *
+ * @param batchDescriptor to be updated for the batch range.
+ * @return the updated batchDescriptor.
+ */
+ public BatchDescriptor next(final BatchDescriptor batchDescriptor)
+ {
+ if (null == gatingSequences)
+ {
+ throw new NullPointerException("gatingSequences must be set before claiming sequences");
+ }
+
+ final long sequence = claimStrategy.incrementAndGet(batchDescriptor.getSize(), gatingSequences);
+ batchDescriptor.setEnd(sequence);
+ return batchDescriptor;
+ }
+
+ /**
+ * Claim a specific sequence when only one publisher is involved.
+ *
+ * @param sequence to be claimed.
+ * @return sequence just claimed.
+ */
+ public long claim(final long sequence)
+ {
+ if (null == gatingSequences)
+ {
+ throw new NullPointerException("gatingSequences must be set before claiming sequences");
+ }
+
+ claimStrategy.setSequence(sequence, gatingSequences);
+
+ return sequence;
+ }
+
+ /**
+ * Publish an event and make it visible to {@link EventProcessor}s
+ *
+ * @param sequence to be published
+ */
+ public void publish(final long sequence)
+ {
+ publish(sequence, 1);
+ }
+
+ /**
+ * Publish the batch of events in sequence.
+ *
+ * @param batchDescriptor to be published.
+ */
+ public void publish(final BatchDescriptor batchDescriptor)
+ {
+ publish(batchDescriptor.getEnd(), batchDescriptor.getSize());
+ }
+
+ /**
+ * Force the publication of a cursor sequence.
+ *
+ * Only use this method when forcing a sequence and you are sure only one publisher exists.
+ * This will cause the cursor to advance to this sequence.
+ *
+ * @param sequence which is to be forced for publication.
+ */
+ public void forcePublish(final long sequence)
+ {
+ cursor.set(sequence);
+ waitStrategy.signalAllWhenBlocking();
+ }
+
+ private void publish(final long sequence, final int batchSize)
+ {
+ claimStrategy.serialisePublishing(sequence, cursor, batchSize);
+ waitStrategy.signalAllWhenBlocking();
+ }
+
+ public long remainingCapacity()
+ {
+ long consumed = Util.getMinimumSequence(gatingSequences);
+ long produced = cursor.get();
+ return getBufferSize() - (produced - consumed);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleThreadedClaimStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleThreadedClaimStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/SingleThreadedClaimStrategy.java (working copy)
@@ -0,0 +1,135 @@
+/*
+ * 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.PaddedLong;
+
+/**
+ * Optimised strategy can be used when there is a single publisher thread claiming sequences.
+ *
+ * This strategy must not be used when multiple threads are used for publishing concurrently on the same {@link Sequencer}
+ */
+public final class SingleThreadedClaimStrategy
+ implements ClaimStrategy
+{
+ private final int bufferSize;
+ private final PaddedLong minGatingSequence = new PaddedLong(Sequencer.INITIAL_CURSOR_VALUE);
+ private final PaddedLong claimSequence = new PaddedLong(Sequencer.INITIAL_CURSOR_VALUE);
+
+ /**
+ * Construct a new single threaded publisher {@link ClaimStrategy} for a given buffer size.
+ *
+ * @param bufferSize for the underlying data structure.
+ */
+ public SingleThreadedClaimStrategy(final int bufferSize)
+ {
+ this.bufferSize = bufferSize;
+ }
+
+// @Override
+ public int getBufferSize()
+ {
+ return bufferSize;
+ }
+
+// @Override
+ public long getSequence()
+ {
+ return claimSequence.get();
+ }
+
+// @Override
+ public boolean hasAvailableCapacity(final int availableCapacity, final Sequence[] dependentSequences)
+ {
+ final long wrapPoint = (claimSequence.get() + availableCapacity) - bufferSize;
+ if (wrapPoint > minGatingSequence.get())
+ {
+ long minSequence = getMinimumSequence(dependentSequences);
+ minGatingSequence.set(minSequence);
+
+ if (wrapPoint > minSequence)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+// @Override
+ public long incrementAndGet(final Sequence[] dependentSequences)
+ {
+ long nextSequence = claimSequence.get() + 1L;
+ claimSequence.set(nextSequence);
+ waitForFreeSlotAt(nextSequence, dependentSequences);
+
+ return nextSequence;
+ }
+
+// @Override
+ public long incrementAndGet(final int delta, final Sequence[] dependentSequences)
+ {
+ long nextSequence = claimSequence.get() + delta;
+ claimSequence.set(nextSequence);
+ waitForFreeSlotAt(nextSequence, dependentSequences);
+
+ return nextSequence;
+ }
+
+// @Override
+ public void setSequence(final long sequence, final Sequence[] dependentSequences)
+ {
+ claimSequence.set(sequence);
+ waitForFreeSlotAt(sequence, dependentSequences);
+ }
+
+// @Override
+ public void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize)
+ {
+ cursor.set(sequence);
+ }
+
+// @Override
+ public long checkAndIncrement(int availableCapacity, int delta, Sequence[] dependentSequences)
+ throws InsufficientCapacityException
+ {
+ if (!hasAvailableCapacity(availableCapacity, dependentSequences))
+ {
+ throw InsufficientCapacityException.INSTANCE;
+ }
+
+ return incrementAndGet(delta, dependentSequences);
+ }
+
+ private void waitForFreeSlotAt(final long sequence, final Sequence[] dependentSequences)
+ {
+ final long wrapPoint = sequence - bufferSize;
+ if (wrapPoint > minGatingSequence.get())
+ {
+ long minSequence;
+ while (wrapPoint > (minSequence = getMinimumSequence(dependentSequences)))
+ {
+ LockSupport.parkNanos(1L);
+ }
+
+ minGatingSequence.set(minSequence);
+ }
+ }
+}
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,124 @@
+/*
+ * 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.TimeUnit;
+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, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier)
+ throws AlertException, InterruptedException
+ {
+ long availableSequence;
+ int counter = RETRIES;
+
+ if (0 == dependents.length)
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+ }
+ }
+ else
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+ }
+ }
+
+ return availableSequence;
+ }
+
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier,
+ final long timeout, final TimeUnit sourceUnit)
+ throws AlertException, InterruptedException
+ {
+ final long timeoutMs = sourceUnit.toMillis(timeout);
+ final long startTime = System.currentTimeMillis();
+ long availableSequence;
+ int counter = RETRIES;
+
+ if (0 == dependents.length)
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+
+ final long elapsedTime = System.currentTimeMillis() - startTime;
+ if (elapsedTime > timeoutMs)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+
+ final long elapsedTime = System.currentTimeMillis() - startTime;
+ if (elapsedTime > timeoutMs)
+ {
+ break;
+ }
+ }
+ }
+
+ 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/TimeoutException.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutException.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/TimeoutException.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;
+
+
+/**
+ * Used to signal that an operation has timed out and been aborted.
+ *
+ * It does not fill in a stack trace for performance reasons.
+ */
+@SuppressWarnings("serial")
+public class TimeoutException extends Exception
+{
+ /** Pre-allocated exception to avoid garbage generation */
+ public static final TimeoutException INSTANCE = new TimeoutException();
+
+ /**
+ * Private constructor so only a single instance exists.
+ */
+ private TimeoutException()
+ {
+ }
+
+ /**
+ * 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/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,61 @@
+/*
+ * 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.TimeUnit;
+
+/**
+ * Strategy employed for making {@link EventProcessor}s wait on a cursor {@link Sequence}.
+ */
+public interface WaitStrategy
+{
+ /**
+ * Wait for the given sequence to be available
+ *
+ * @param sequence to be waited on.
+ * @param cursor on which to wait.
+ * @param dependents further back the chain that must advance first
+ * @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[] dependents, SequenceBarrier barrier)
+ throws AlertException, InterruptedException;
+
+ /**
+ * Wait for the given sequence to be available with a timeout specified.
+ *
+ * @param sequence to be waited on.
+ * @param cursor on which to wait.
+ * @param dependents further back the chain that must advance first
+ * @param barrier the processor is waiting on.
+ * @param timeout value to abort after.
+ * @param sourceUnit of the timeout value.
+ * @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.
+ * @deprecated Use a separate timeout event pushed into the Disruptor from different thread.
+ */
+ @Deprecated
+ long waitFor(long sequence, Sequence cursor, Sequence[] dependents, SequenceBarrier barrier, long timeout, TimeUnit sourceUnit)
+ throws AlertException, InterruptedException;
+
+ /**
+ * Signal those {@link EventProcessor}s waiting 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 event implementation storing the data for sharing during exchange or parallel coordination of an event.
+ */
+public interface WorkHandler
+{
+ /**
+ * Callback to indicate a unit of work needs to be processed.
+ *
+ * @param event published to the {@link RingBuffer}
+ * @throws Exception if the {@link WorkHandler} would like the exception handled further up the chain.
+ */
+ void onEvent(T event) throws Exception;
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkProcessor.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkProcessor.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkProcessor.java (working copy)
@@ -0,0 +1,156 @@
+/*
+ * 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;
+
+/**
+ * {@link WorkProcessor} for ensuring each sequence is handled by only a single processor, effectively consuming the sequence.
+ *
+ * No other {@link WorkProcessor}s in the {@link WorkerPool} will consume the same sequence.
+ *
+ * @param event implementation storing the details for the work to processed.
+ */
+public final class WorkProcessor
+ implements EventProcessor
+{
+ private final AtomicBoolean running = new AtomicBoolean(false);
+ private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
+ private final RingBuffer ringBuffer;
+ private final SequenceBarrier sequenceBarrier;
+ private final WorkHandler workHandler;
+ private final ExceptionHandler exceptionHandler;
+ private final Sequence workSequence;
+
+ /**
+ * Construct a {@link WorkProcessor}.
+ *
+ * @param ringBuffer to which events are published.
+ * @param sequenceBarrier on which it is waiting.
+ * @param workHandler is the delegate to which events are dispatched.
+ * @param exceptionHandler to be called back when an error occurs
+ * @param workSequence from which to claim the next event to be worked on. It should always be initialised
+ * as {@link Sequencer#INITIAL_CURSOR_VALUE}
+ */
+ public WorkProcessor(final RingBuffer ringBuffer,
+ final SequenceBarrier sequenceBarrier,
+ final WorkHandler workHandler,
+ final ExceptionHandler exceptionHandler,
+ final Sequence workSequence)
+ {
+ this.ringBuffer = ringBuffer;
+ this.sequenceBarrier = sequenceBarrier;
+ this.workHandler = workHandler;
+ this.exceptionHandler = exceptionHandler;
+ this.workSequence = workSequence;
+ }
+
+// @Override
+ public Sequence getSequence()
+ {
+ return sequence;
+ }
+
+// @Override
+ public void halt()
+ {
+ running.set(false);
+ sequenceBarrier.alert();
+ }
+
+ /**
+ * It is ok to have another thread re-run this method after a halt().
+ */
+// @Override
+ public void run()
+ {
+ if (!running.compareAndSet(false, true))
+ {
+ throw new IllegalStateException("Thread is already running");
+ }
+ sequenceBarrier.clearAlert();
+
+ notifyStart();
+
+ boolean processedSequence = true;
+ long nextSequence = sequence.get();
+ T event = null;
+ while (true)
+ {
+ try
+ {
+ if (processedSequence)
+ {
+ processedSequence = false;
+ nextSequence = workSequence.incrementAndGet();
+ sequence.set(nextSequence - 1L);
+ }
+
+ sequenceBarrier.waitFor(nextSequence);
+ event = ringBuffer.get(nextSequence);
+ workHandler.onEvent(event);
+
+ processedSequence = true;
+ }
+ catch (final AlertException ex)
+ {
+ if (!running.get())
+ {
+ break;
+ }
+ }
+ catch (final Throwable ex)
+ {
+ exceptionHandler.handleEventException(ex, nextSequence, event);
+ processedSequence = true;
+ }
+ }
+
+ notifyShutdown();
+
+ running.set(false);
+ }
+
+ private void notifyStart()
+ {
+ if (workHandler instanceof LifecycleAware)
+ {
+ try
+ {
+ ((LifecycleAware)workHandler).onStart();
+ }
+ catch (final Throwable ex)
+ {
+ exceptionHandler.handleOnStartException(ex);
+ }
+ }
+ }
+
+ private void notifyShutdown()
+ {
+ if (workHandler instanceof LifecycleAware)
+ {
+ try
+ {
+ ((LifecycleAware)workHandler).onShutdown();
+ }
+ catch (final Throwable ex)
+ {
+ exceptionHandler.handleOnShutdownException(ex);
+ }
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkerPool.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkerPool.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/WorkerPool.java (working copy)
@@ -0,0 +1,173 @@
+/*
+ * 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.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+/**
+ * A pool of {@link WorkProcessor}s that will consume sequences so jobs can be farmed out across a pool of workers
+ * which are implemented the {@link WorkHandler} interface.
+ *
+ * @param event to be processed by a pool of workers
+ */
+public final class WorkerPool
+{
+ private final AtomicBoolean started = new AtomicBoolean(false);
+ private final Sequence workSequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
+ private final RingBuffer ringBuffer;
+ private final WorkProcessor>[] workProcessors;
+
+ /**
+ * Create a worker pool to enable an array of {@link WorkHandler}s to consume published sequences.
+ *
+ * This option requires a pre-configured {@link RingBuffer} which must have {@link RingBuffer#setGatingSequences(Sequence...)}
+ * called before the work pool is started.
+ *
+ * @param ringBuffer of events to be consumed.
+ * @param sequenceBarrier on which the workers will depend.
+ * @param exceptionHandler to callback when an error occurs which is not handled by the {@link WorkHandler}s.
+ * @param workHandlers to distribute the work load across.
+ */
+ public WorkerPool(final RingBuffer ringBuffer,
+ final SequenceBarrier sequenceBarrier,
+ final ExceptionHandler exceptionHandler,
+ final WorkHandler... workHandlers)
+ {
+ this.ringBuffer = ringBuffer;
+ final int numWorkers = workHandlers.length;
+ workProcessors = new WorkProcessor[numWorkers];
+
+ for (int i = 0; i < numWorkers; i++)
+ {
+ workProcessors[i] = new WorkProcessor(ringBuffer,
+ sequenceBarrier,
+ workHandlers[i],
+ exceptionHandler,
+ workSequence);
+ }
+ }
+
+ /**
+ * Construct a work pool with an internal {@link RingBuffer} for convenience.
+ *
+ * This option does not require {@link RingBuffer#setGatingSequences(Sequence...)} to be called before the work pool is started.
+ *
+ * @param eventFactory for filling the {@link RingBuffer}
+ * @param claimStrategy for the {@link RingBuffer}
+ * @param waitStrategy for the {@link RingBuffer}
+ * @param exceptionHandler to callback when an error occurs which is not handled by the {@link WorkHandler}s.
+ * @param workHandlers to distribute the work load across.
+ */
+ public WorkerPool(final EventFactory eventFactory,
+ final ClaimStrategy claimStrategy,
+ final WaitStrategy waitStrategy,
+ final ExceptionHandler exceptionHandler,
+ final WorkHandler... workHandlers)
+ {
+ ringBuffer = new RingBuffer(eventFactory, claimStrategy, waitStrategy);
+ final SequenceBarrier barrier = ringBuffer.newBarrier();
+ final int numWorkers = workHandlers.length;
+ workProcessors = new WorkProcessor[numWorkers];
+
+ for (int i = 0; i < numWorkers; i++)
+ {
+ workProcessors[i] = new WorkProcessor(ringBuffer,
+ barrier,
+ workHandlers[i],
+ exceptionHandler,
+ workSequence);
+ }
+
+ ringBuffer.setGatingSequences(getWorkerSequences());
+ }
+
+ /**
+ * Get an array of {@link Sequence}s representing the progress of the workers.
+ *
+ * @return an array of {@link Sequence}s representing the progress of the workers.
+ */
+ public Sequence[] getWorkerSequences()
+ {
+ final Sequence[] sequences = new Sequence[workProcessors.length];
+ for (int i = 0, size = workProcessors.length; i < size; i++)
+ {
+ sequences[i] = workProcessors[i].getSequence();
+ }
+
+ return sequences;
+ }
+
+ /**
+ * Start the worker pool processing events in sequence.
+ *
+ * @param executor providing threads for running the workers.
+ * @return the {@link RingBuffer} used for the work queue.
+ * @throws IllegalStateException is the pool has already been started and not halted yet
+ */
+ public RingBuffer start(final Executor executor)
+ {
+ if (!started.compareAndSet(false, true))
+ {
+ throw new IllegalStateException("WorkerPool has already been started and cannot be restarted until halted.");
+ }
+
+ final long cursor = ringBuffer.getCursor();
+ workSequence.set(cursor);
+
+ for (WorkProcessor> processor : workProcessors)
+ {
+ processor.getSequence().set(cursor);
+ executor.execute(processor);
+ }
+
+ return ringBuffer;
+ }
+
+ /**
+ * Wait for the {@link RingBuffer} to drain of published events then halt the workers.
+ */
+ public void drainAndHalt()
+ {
+ Sequence[] workerSequences = getWorkerSequences();
+ while (ringBuffer.getCursor() > Util.getMinimumSequence(workerSequences))
+ {
+ Thread.yield();
+ }
+
+ for (WorkProcessor> processor : workProcessors)
+ {
+ processor.halt();
+ }
+
+ started.set(false);
+ }
+
+ /**
+ * Halt all workers immediately at then end of their current cycle.
+ */
+ public void halt()
+ {
+ for (WorkProcessor> processor : workProcessors)
+ {
+ processor.halt();
+ }
+
+ started.set(false);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/YieldingWaitStrategy.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/YieldingWaitStrategy.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/YieldingWaitStrategy.java (working copy)
@@ -0,0 +1,118 @@
+/*
+ * 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.TimeUnit;
+
+/**
+ * Yielding strategy that uses a Thread.yield() for {@link org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor}s waiting on a barrier
+ * after an initially spinning.
+ *
+ * This strategy is a good compromise between performance and CPU resource without incurring significant latency spikes.
+ */
+public final class YieldingWaitStrategy implements WaitStrategy
+{
+ private static final int SPIN_TRIES = 100;
+
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier)
+ throws AlertException, InterruptedException
+ {
+ long availableSequence;
+ int counter = SPIN_TRIES;
+
+ if (0 == dependents.length)
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+ }
+ }
+ else
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+ }
+ }
+
+ return availableSequence;
+ }
+
+// @Override
+ public long waitFor(final long sequence, final Sequence cursor, final Sequence[] dependents, final SequenceBarrier barrier,
+ final long timeout, final TimeUnit sourceUnit)
+ throws AlertException, InterruptedException
+ {
+ final long timeoutMs = sourceUnit.toMillis(timeout);
+ final long startTime = System.currentTimeMillis();
+ long availableSequence;
+ int counter = SPIN_TRIES;
+
+ if (0 == dependents.length)
+ {
+ while ((availableSequence = cursor.get()) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+
+ final long elapsedTime = System.currentTimeMillis() - startTime;
+ if (elapsedTime > timeoutMs)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ while ((availableSequence = getMinimumSequence(dependents)) < sequence)
+ {
+ counter = applyWaitMethod(barrier, counter);
+
+ final long elapsedTime = System.currentTimeMillis() - startTime;
+ if (elapsedTime > timeoutMs)
+ {
+ break;
+ }
+ }
+ }
+
+ return availableSequence;
+ }
+
+// @Override
+ public void signalAllWhenBlocking()
+ {
+ }
+
+ private int applyWaitMethod(final SequenceBarrier barrier, int counter)
+ throws AlertException
+ {
+ barrier.checkAlert();
+
+ if (0 == counter)
+ {
+ Thread.yield();
+ }
+ else
+ {
+ --counter;
+ }
+
+ return counter;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/package.html
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/package.html (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/package.html (working copy)
@@ -0,0 +1,174 @@
+
+
+
+ Disruptor
+
+
+
+ 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.
+
+ track to prevent wrap
+ +------------------+
+ | |
+ | v
++----+ +-----+ +----+ +====+ +====+ +-----+
+| P1 |--->| EP1 | | P1 |--->| RB |<---| SB | | EP1 |
++----+ +-----+ +----+ +====+ +====+ +-----+
+ claim get ^ |
+ | |
+ +--------+
+ waitFor
+
+
+
+ Sequence a series of messages from multiple publishers
+
+ track to prevent wrap
+ +--------------------+
+ | |
+ | v
++----+ +----+ +====+ +====+ +-----+
+| P1 |-------+ | P1 |--->| RB |<---| SB | | EP1 |
++----+ | +----+ +====+ +====+ +-----+
+ v ^ get ^ |
++----+ +-----+ +----+ | | |
+| P2 |--->| EP1 | | P2 |------+ +---------+
++----+ +-----+ +----+ | waitFor
+ ^ |
++----+ | +----+ |
+| P3 |-------+ | P3 |------+
++----+ +----+
+
+
+
+ Pipeline a series of messages
+
+ +----+ +-----+ +-----+ +-----+
+ | P1 |--->| EP1 |--->| EP2 |--->| EP3 |
+ +----+ +-----+ +-----+ +-----+
+
+
+
+ track to prevent wrap
+ +----------------------------------------------------------------+
+ | |
+ | v
++----+ +====+ +=====+ +-----+ +=====+ +-----+ +=====+ +-----+
+| P1 |--->| RB | | SB1 |<---| EP1 |<---| SB2 |<---| EP2 |<---| SB3 |<---| EP3 |
++----+ +====+ +=====+ +-----+ +=====+ +-----+ +=====+ +-----+
+ claim ^ get | waitFor | waitFor | waitFor
+ | | | |
+ +---------+---------------------+---------------------+
+
+
+
+ Multicast a series of messages to multiple EventProcessors
+
+ +-----+ track to prevent wrap
+ +----->| EP1 | +--------------------+----------+----------+
+ | +-----+ | | | |
+ | | v v v
++----+ +-----+ +----+ +====+ +====+ +-----+ +-----+ +-----+
+| P1 |--->| EP2 | | P1 |--->| RB |<---| SB | | EP1 | | EP2 | | EP3 |
++----+ +-----+ +----+ +====+ +====+ +-----+ +-----+ +-----+
+ | claim get ^ | | |
+ | +-----+ | | | |
+ +----->| EP3 | +---------+----------+----------+
+ +-----+ waitFor
+
+
+
+ Replicate a message then fold back the results
+
+ +-----+ 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,345 @@
+/*
+ * 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 in nanoseconds across a large number of observations
+ * when high performance is required.
+ */
+public final class Histogram
+{
+ private final long[] upperBounds;
+ private final long[] counts;
+ private long minValue = Long.MAX_VALUE;
+ private long maxValue = 0L;
+
+ /**
+ * Create a new Histogram with a provided list of interval bounds.
+ *
+ * @param upperBounds of the intervals.
+ */
+ public Histogram(final long[] upperBounds)
+ {
+ validateBounds(upperBounds);
+
+ this.upperBounds = Arrays.copyOf(upperBounds, upperBounds.length);
+ this.counts = new long[upperBounds.length];
+ }
+
+ private void validateBounds(final long[] upperBounds)
+ {
+ long lastBound = -1L;
+ 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.
+ *
+ * @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 otherwise false.
+ */
+ public boolean addObservation(final long value)
+ {
+ int low = 0;
+ int high = upperBounds.length - 1;
+
+ while (low < high)
+ {
+ int mid = low + ((high - low) >> 1);
+ if (upperBounds[mid] < value)
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ high = mid;
+ }
+ }
+
+ if (value <= upperBounds[high])
+ {
+ counts[high]++;
+ trackRange(value);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ 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.
+ */
+ public void addObservations(final Histogram histogram)
+ {
+ 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");
+ }
+ }
+
+ for (int i = 0, size = counts.length; i < size; i++)
+ {
+ counts[i] += histogram.counts[i];
+ }
+
+ 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 the 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.
+ *
+ * @return the mean of all recorded observations.
+ */
+ public BigDecimal getMean()
+ {
+ if (0L == getCount())
+ {
+ return BigDecimal.ZERO;
+ }
+
+ long lowerBound = counts[0] > 0L ? minValue : 0L;
+ BigDecimal total = BigDecimal.ZERO;
+
+ 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);
+ }
+
+ 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.
+ *
+ * @param factor representing the size of the population.
+ * @return the interval upper bound.
+ */
+ 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;
+
+ 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();
+ }
+}
\ No newline at end of file
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/Disruptor.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/Disruptor.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/Disruptor.java (working copy)
@@ -0,0 +1,331 @@
+/*
+ * 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.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.BatchEventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.ClaimStrategy;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventFactory;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventPublisher;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventTranslator;
+import org.apache.logging.log4j.async.com.lmax.disruptor.ExceptionHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.RingBuffer;
+import org.apache.logging.log4j.async.com.lmax.disruptor.SequenceBarrier;
+import org.apache.logging.log4j.async.com.lmax.disruptor.WaitStrategy;
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+/**
+ * A DSL-style API for setting up the disruptor pattern around a ring buffer.
+ *
+ * A simple example of setting up the disruptor with two event handlers that must process events in order:
+ *
+ * Disruptor disruptor = new Disruptor(MyEvent.FACTORY, 32, Executors.newCachedThreadPool());
+ * EventHandler handler1 = new EventHandler() { ... };
+ * EventHandler handler2 = new EventHandler() { ... };
+ * disruptor.handleEventsWith(handler1);
+ * disruptor.after(handler1).handleEventsWith(handler2);
+ *
+ * RingBuffer ringBuffer = disruptor.start();
+ *
+ * @param the type of event used.
+ */
+public class Disruptor
+{
+ private final RingBuffer ringBuffer;
+ private final Executor executor;
+ private final EventProcessorRepository eventProcessorRepository = new EventProcessorRepository();
+ private final AtomicBoolean started = new AtomicBoolean(false);
+ private final EventPublisher eventPublisher;
+ private ExceptionHandler exceptionHandler;
+
+ /**
+ * Create a new Disruptor.
+ *
+ * @param eventFactory the factory to create events in the ring buffer.
+ * @param ringBufferSize the size of the ring buffer.
+ * @param executor an {@link Executor} to execute event processors.
+ */
+ public Disruptor(final EventFactory eventFactory, final int ringBufferSize, final Executor executor)
+ {
+ this(new RingBuffer(eventFactory, ringBufferSize), executor);
+ }
+
+ /**
+ * Create a new Disruptor.
+ *
+ * @param eventFactory the factory to create events in the ring buffer.
+ * @param executor an {@link Executor} to execute event processors.
+ * @param claimStrategy the claim strategy to use for the ring buffer.
+ * @param waitStrategy the wait strategy to use for the ring buffer.
+ */
+ public Disruptor(final EventFactory eventFactory, final Executor executor,
+ final ClaimStrategy claimStrategy,
+ final WaitStrategy waitStrategy)
+ {
+ this(new RingBuffer(eventFactory, claimStrategy, waitStrategy), executor);
+ }
+
+ private Disruptor(final RingBuffer ringBuffer, final Executor executor)
+ {
+ this.ringBuffer = ringBuffer;
+ this.executor = executor;
+ eventPublisher = new EventPublisher(ringBuffer);
+ }
+
+ /**
+ * Set up event handlers to handle events from the ring buffer. These handlers will process events
+ * as soon as they become available, in parallel.
+ *
+ * This method can be used as the start of a chain. For example if the handler A must
+ * process events before handler B:
+ *
+ * dw.handleEventsWith(A).then(B);
+ *
+ * @param handlers the event handlers that will process events.
+ * @return a {@link EventHandlerGroup} that can be used to chain dependencies.
+ */
+ @SuppressWarnings("varargs")
+ public EventHandlerGroup handleEventsWith(final EventHandler... handlers)
+ {
+ return createEventProcessors(new EventProcessor[0], handlers);
+ }
+
+ /**
+ * Set up custom event processors to handle events from the ring buffer. The Disruptor will
+ * automatically start this processors when {@link #start()} is called.
+ *
+ * @param processors the event processors that will process events.
+ * @return a {@link EventHandlerGroup} that can be used to chain dependencies.
+ */
+ public EventHandlerGroup handleEventsWith(final EventProcessor... processors)
+ {
+ for (EventProcessor processor : processors)
+ {
+ eventProcessorRepository.add(processor);
+ }
+ return new EventHandlerGroup(this, eventProcessorRepository, processors);
+ }
+
+ /**
+ * Specify an exception handler to be used for any future event handlers.
+ * Note that only event handlers set up after calling this method will use the exception handler.
+ *
+ * @param exceptionHandler the exception handler to use for any future {@link EventProcessor}.
+ */
+ public void handleExceptionsWith(final ExceptionHandler exceptionHandler)
+ {
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ /**
+ * Override the default exception handler for a specific handler.
+ * 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 EventHandler eventHandler)
+ {
+ return new ExceptionHandlerSetting(eventHandler, eventProcessorRepository);
+ }
+
+ /**
+ * Create a group of event handlers to be used as a dependency.
+ * For example if the handler A must process events before handler B:
+ *
+ * dw.after(A).handleEventsWith(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 EventHandlerGroup after(final EventHandler... handlers)
+ {
+ EventProcessor[] selectedEventProcessors = new EventProcessor[handlers.length];
+ for (int i = 0, handlersLength = handlers.length; i < handlersLength; i++)
+ {
+ selectedEventProcessors[i] = eventProcessorRepository.getEventProcessorFor(handlers[i]);
+ }
+
+ return new EventHandlerGroup(this, eventProcessorRepository, selectedEventProcessors);
+ }
+
+ /**
+ * Create a group of event processors to be used as a dependency.
+ *
+ * @param processors the event processors, previously set up with {@link #handleEventsWith(org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor...)},
+ * that will form the barrier for subsequent handlers or processors.
+ * @return an {@link EventHandlerGroup} that can be used to setup a {@link SequenceBarrier} over hte specified event processors.
+ * @see #after(org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler[])
+ */
+ public EventHandlerGroup after(final EventProcessor... processors)
+ {
+ for (EventProcessor processor : processors)
+ {
+ eventProcessorRepository.add(processor);
+ }
+
+ return new EventHandlerGroup(this, eventProcessorRepository, processors);
+ }
+
+ /**
+ * Publish an event to the ring buffer.
+ *
+ * @param eventTranslator the translator that will load data into the event.
+ */
+ public void publishEvent(final EventTranslator eventTranslator)
+ {
+ eventPublisher.publishEvent(eventTranslator);
+ }
+
+ /**
+ * Starts the event processors and returns the fully configured ring buffer.
+ * The ring buffer is set up to prevent overwriting any entry that is yet to
+ * be processed by the slowest event processor.
+ * This method must only be called once after all event processors have been added.
+ *
+ * @return the configured ring buffer.
+ */
+ public RingBuffer start()
+ {
+ EventProcessor[] gatingProcessors = eventProcessorRepository.getLastEventProcessorsInChain();
+ ringBuffer.setGatingSequences(Util.getSequencesFor(gatingProcessors));
+
+ checkOnlyStartedOnce();
+ for (EventProcessorInfo eventProcessorInfo : eventProcessorRepository)
+ {
+ executor.execute(eventProcessorInfo.getEventProcessor());
+ }
+
+ return ringBuffer;
+ }
+
+ /**
+ * Calls {@link org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor#halt()} on all of the event processors created via this disruptor.
+ */
+ public void halt()
+ {
+ for (EventProcessorInfo> eventprocessorInfo : eventProcessorRepository)
+ {
+ eventprocessorInfo.getEventProcessor().halt();
+ }
+ }
+
+ /**
+ * Waits until all events currently in the disruptor have been processed by all event processors
+ * and then halts the processors. It is critical that publishing to the ring buffer has stopped
+ * before calling this method, otherwise it may never return.
+ *
+ * This method will not shutdown the executor, nor will it await the final termination of the
+ * processor threads.
+ */
+ public void shutdown()
+ {
+ while (hasBacklog())
+ {
+ // Busy spin
+ }
+ halt();
+ }
+
+ /**
+ * The the {@link RingBuffer} used by this Disruptor. This is useful for creating custom
+ * event processors if the behaviour of {@link BatchEventProcessor} is not suitable.
+ *
+ * @return the ring buffer used by this Disruptor.
+ */
+ public RingBuffer getRingBuffer()
+ {
+ return ringBuffer;
+ }
+
+ /**
+ * Get the {@link SequenceBarrier} used by a specific handler. Note that the {@link SequenceBarrier}
+ * may be shared by multiple event handlers.
+ *
+ * @param handler the handler to get the barrier for.
+ * @return the SequenceBarrier used by handler.
+ */
+ public SequenceBarrier getBarrierFor(final EventHandler handler)
+ {
+ return eventProcessorRepository.getBarrierFor(handler);
+ }
+
+ private boolean hasBacklog()
+ {
+ final long cursor = ringBuffer.getCursor();
+ for (EventProcessor consumer : eventProcessorRepository.getLastEventProcessorsInChain())
+ {
+ if (cursor != consumer.getSequence().get())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ EventHandlerGroup createEventProcessors(final EventProcessor[] barrierEventProcessors,
+ final EventHandler[] eventHandlers)
+ {
+ checkNotStarted();
+
+ final EventProcessor[] createdEventProcessors = new EventProcessor[eventHandlers.length];
+ final SequenceBarrier barrier = ringBuffer.newBarrier(Util.getSequencesFor(barrierEventProcessors));
+
+ for (int i = 0, eventHandlersLength = eventHandlers.length; i < eventHandlersLength; i++)
+ {
+ final EventHandler eventHandler = eventHandlers[i];
+
+ final BatchEventProcessor batchEventProcessor = new BatchEventProcessor(ringBuffer, barrier, eventHandler);
+
+ if (exceptionHandler != null)
+ {
+ batchEventProcessor.setExceptionHandler(exceptionHandler);
+ }
+
+ eventProcessorRepository.add(batchEventProcessor, eventHandler, barrier);
+ createdEventProcessors[i] = batchEventProcessor;
+ }
+
+ if (createdEventProcessors.length > 0)
+ {
+ eventProcessorRepository.unMarkEventProcessorsAsEndOfChain(barrierEventProcessors);
+ }
+
+ return new EventHandlerGroup(this, eventProcessorRepository, createdEventProcessors);
+ }
+
+ private void checkNotStarted()
+ {
+ if (started.get())
+ {
+ throw new IllegalStateException("All event handlers must be added before calling starts.");
+ }
+ }
+
+ private void checkOnlyStartedOnce()
+ {
+ if (!started.compareAndSet(false, true))
+ {
+ throw new IllegalStateException("Disruptor.start() must only be called once.");
+ }
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventHandlerGroup.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventHandlerGroup.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventHandlerGroup.java (working copy)
@@ -0,0 +1,128 @@
+/*
+ * 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 org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.SequenceBarrier;
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+
+/**
+ * A group of {@link EventProcessor}s used as part of the {@link Disruptor}.
+ *
+ * @param the type of entry used by the event processors.
+ */
+public class EventHandlerGroup
+{
+ private final Disruptor disruptor;
+ private final EventProcessorRepository eventProcessorRepository;
+ private final EventProcessor[] eventProcessors;
+
+ EventHandlerGroup(final Disruptor disruptor,
+ final EventProcessorRepository eventProcessorRepository,
+ final EventProcessor[] eventProcessors)
+ {
+ this.disruptor = disruptor;
+ this.eventProcessorRepository = eventProcessorRepository;
+ this.eventProcessors = eventProcessors;
+ }
+
+ /**
+ * Create a new event handler group that combines the handlers in this group with
+ * handlers.
+ *
+ * @param handlers the handlers to combine.
+ * @return a new EventHandlerGroup combining the existing and new handlers into a
+ * single dependency group.
+ */
+ public EventHandlerGroup and(final EventHandler... handlers)
+ {
+ EventProcessor[] combinedProcessors = new EventProcessor[eventProcessors.length + handlers.length];
+ for (int i = 0; i < handlers.length; i++)
+ {
+ combinedProcessors[i] = eventProcessorRepository.getEventProcessorFor(handlers[i]);
+ }
+ System.arraycopy(eventProcessors, 0, combinedProcessors, handlers.length, eventProcessors.length);
+
+ return new EventHandlerGroup(disruptor, eventProcessorRepository, combinedProcessors);
+ }
+
+ /**
+ * Create a new event handler group that combines the handlers in this group with processors.
+ *
+ * @param processors the processors to combine.
+ * @return a new EventHandlerGroup combining the existing and new processors into a single dependency group.
+ */
+ public EventHandlerGroup and(final EventProcessor... processors)
+ {
+ EventProcessor[] combinedProcessors = new EventProcessor[eventProcessors.length + processors.length];
+
+ for (EventProcessor processor : processors)
+ {
+ eventProcessorRepository.add(processor);
+ }
+ System.arraycopy(processors, 0, combinedProcessors, 0, processors.length);
+ System.arraycopy(eventProcessors, 0, combinedProcessors, processors.length, eventProcessors.length);
+
+ return new EventHandlerGroup(disruptor, eventProcessorRepository, combinedProcessors);
+ }
+
+ /**
+ * Set up batch handlers to consume events from the ring buffer. These handlers will only process events
+ * after every {@link EventProcessor} in this group has processed the event.
+ *
+ * This method is generally used as part of a chain. For example if the handler A must
+ * process events before handler B:
+ *
+ * dw.handleEventsWith(A).then(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 EventHandlerGroup then(final EventHandler... handlers)
+ {
+ return handleEventsWith(handlers);
+ }
+
+ /**
+ * Set up batch handlers to handleEventException events from the ring buffer. These handlers will only process events
+ * after every {@link EventProcessor} in this group has processed the event.
+ *
+ * This method is generally used as part of a chain. For example if the handler A must
+ * process events before handler B:
+ *
+ * dw.after(A).handleEventsWith(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 EventHandlerGroup handleEventsWith(final EventHandler... handlers)
+ {
+ return disruptor.createEventProcessors(eventProcessors, handlers);
+ }
+
+ /**
+ * Create a dependency barrier for the processors in this group.
+ * This allows custom event processors to have dependencies on
+ * {@link org.apache.logging.log4j.async.com.lmax.disruptor.BatchEventProcessor}s created by the disruptor.
+ *
+ * @return a {@link SequenceBarrier} including all the processors in this group.
+ */
+ public SequenceBarrier asSequenceBarrier()
+ {
+ return disruptor.getRingBuffer().newBarrier(Util.getSequencesFor(eventProcessors));
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventProcessorInfo.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventProcessorInfo.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventProcessorInfo.java (working copy)
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.SequenceBarrier;
+
+class EventProcessorInfo
+{
+ private final EventProcessor eventprocessor;
+ private final EventHandler handler;
+ private final SequenceBarrier barrier;
+ private boolean endOfChain = true;
+
+ EventProcessorInfo(final EventProcessor eventprocessor, final EventHandler handler, final SequenceBarrier barrier)
+ {
+ this.eventprocessor = eventprocessor;
+ this.handler = handler;
+ this.barrier = barrier;
+ }
+
+ public EventProcessor getEventProcessor()
+ {
+ return eventprocessor;
+ }
+
+ public EventHandler getHandler()
+ {
+ return handler;
+ }
+
+ public SequenceBarrier getBarrier()
+ {
+ return barrier;
+ }
+
+ public boolean isEndOfChain()
+ {
+ return endOfChain;
+ }
+
+ public void markAsUsedInBarrier()
+ {
+ endOfChain = false;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventProcessorRepository.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventProcessorRepository.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/EventProcessorRepository.java (working copy)
@@ -0,0 +1,101 @@
+/*
+ * 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.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.SequenceBarrier;
+
+class EventProcessorRepository implements Iterable>
+{
+ private final Map, EventProcessorInfo> eventProcessorInfoByHandler = new IdentityHashMap, EventProcessorInfo>();
+ private final Map> eventProcessorInfoByEventProcessor = new IdentityHashMap>();
+
+ public void add(final EventProcessor eventprocessor,
+ final EventHandler handler,
+ final SequenceBarrier barrier)
+ {
+ final EventProcessorInfo eventProcessorInfo = new EventProcessorInfo(eventprocessor, handler, barrier);
+ eventProcessorInfoByHandler.put(handler, eventProcessorInfo);
+ eventProcessorInfoByEventProcessor.put(eventprocessor, eventProcessorInfo);
+ }
+
+ public void add(final EventProcessor processor)
+ {
+ final EventProcessorInfo eventProcessorInfo = new EventProcessorInfo(processor, null, null);
+ eventProcessorInfoByEventProcessor.put(processor, eventProcessorInfo);
+ }
+
+ public EventProcessor[] getLastEventProcessorsInChain()
+ {
+ List lastEventProcessors = new ArrayList();
+ for (EventProcessorInfo eventProcessorInfo : eventProcessorInfoByEventProcessor.values())
+ {
+ if (eventProcessorInfo.isEndOfChain())
+ {
+ lastEventProcessors.add(eventProcessorInfo.getEventProcessor());
+ }
+ }
+
+ return lastEventProcessors.toArray(new EventProcessor[lastEventProcessors.size()]);
+ }
+
+ public EventProcessor getEventProcessorFor(final EventHandler handler)
+ {
+ final EventProcessorInfo> eventprocessorInfo = getEventProcessorInfo(handler);
+ if (eventprocessorInfo == null)
+ {
+ throw new IllegalArgumentException("The event handler " + handler + " is not processing events.");
+ }
+
+ return eventprocessorInfo.getEventProcessor();
+ }
+
+ public void unMarkEventProcessorsAsEndOfChain(final EventProcessor... barrierEventProcessors)
+ {
+ for (EventProcessor barrierEventProcessor : barrierEventProcessors)
+ {
+ getEventProcessorInfo(barrierEventProcessor).markAsUsedInBarrier();
+ }
+ }
+
+ public Iterator> iterator()
+ {
+ return eventProcessorInfoByEventProcessor.values().iterator();
+ }
+
+ public SequenceBarrier getBarrierFor(final EventHandler handler)
+ {
+ final EventProcessorInfo eventProcessorInfo = getEventProcessorInfo(handler);
+ return eventProcessorInfo != null ? eventProcessorInfo.getBarrier() : null;
+ }
+
+ private EventProcessorInfo getEventProcessorInfo(final EventHandler handler)
+ {
+ return eventProcessorInfoByHandler.get(handler);
+ }
+
+ private EventProcessorInfo getEventProcessorInfo(final EventProcessor barrierEventProcessor)
+ {
+ return eventProcessorInfoByEventProcessor.get(barrierEventProcessor);
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ExceptionHandlerSetting.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ExceptionHandlerSetting.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/dsl/ExceptionHandlerSetting.java (working copy)
@@ -0,0 +1,51 @@
+/*
+ * 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 org.apache.logging.log4j.async.com.lmax.disruptor.BatchEventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.ExceptionHandler;
+
+/**
+ * A support class used as part of setting an exception handler for a specific event handler.
+ * For example:
+ * disruptorWizard.handleExceptionsIn(eventHandler).with(exceptionHandler);
+ *
+ * @param the type of event being handled.
+ */
+public class ExceptionHandlerSetting
+{
+ private final EventHandler eventHandler;
+ private final EventProcessorRepository eventProcessorRepository;
+
+ ExceptionHandlerSetting(final EventHandler eventHandler,
+ final EventProcessorRepository eventProcessorRepository)
+ {
+ this.eventHandler = eventHandler;
+ this.eventProcessorRepository = eventProcessorRepository;
+ }
+
+ /**
+ * Specify the {@link ExceptionHandler} to use with the event handler.
+ *
+ * @param exceptionHandler the exception handler to use.
+ */
+ public void with(ExceptionHandler exceptionHandler)
+ {
+ ((BatchEventProcessor>)eventProcessorRepository.getEventProcessorFor(eventHandler)).setExceptionHandler(exceptionHandler);
+ eventProcessorRepository.getBarrierFor(eventHandler).alert();
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/MutableLong.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/MutableLong.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/MutableLong.java (working copy)
@@ -0,0 +1,61 @@
+/*
+ * 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.util;
+
+/**
+ * Holder class for a long value.
+ */
+public class MutableLong
+{
+ private long value = 0L;
+
+ /**
+ * Default constructor
+ */
+ public MutableLong()
+ {
+ }
+
+ /**
+ * Construct the holder with initial value.
+ *
+ * @param initialValue to be initially set.
+ */
+ public MutableLong(final long initialValue)
+ {
+ this.value = initialValue;
+ }
+
+ /**
+ * Get the long value.
+ *
+ * @return the long value.
+ */
+ public long get()
+ {
+ return value;
+ }
+
+ /**
+ * Set the long value.
+ *
+ * @param value to set.
+ */
+ public void set(final long value)
+ {
+ this.value = value;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/PaddedLong.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/PaddedLong.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/PaddedLong.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.util;
+
+
+/**
+ * Cache line padded long variable to be used when false sharing maybe an issue.
+ */
+public final class PaddedLong extends MutableLong
+{
+ public volatile long p1, p2, p3, p4, p5, p6 = 7L;
+
+ /**
+ * Default constructor
+ */
+ public PaddedLong()
+ {
+ }
+
+ /**
+ * Construct with an initial value.
+ *
+ * @param initialValue for construction
+ */
+ public PaddedLong(final long initialValue)
+ {
+ super(initialValue);
+ }
+
+ public long sumPaddingToPreventOptimisation()
+ {
+ return p1 + p2 + p3 + p4 + p5 + p6;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/Util.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/Util.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/com/lmax/disruptor/util/Util.java (working copy)
@@ -0,0 +1,153 @@
+/*
+ * 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.util;
+
+import java.lang.reflect.Field;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventProcessor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.Sequence;
+
+import sun.misc.Unsafe;
+
+
+/**
+ * Set of common functions used by the Disruptor
+ */
+public final class Util
+{
+ /**
+ * Calculate the next power of 2, greater than or equal to x.
+ * 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)
+ {
+ long minimum = Long.MAX_VALUE;
+
+ for (Sequence sequence : sequences)
+ {
+ long value = sequence.get();
+ minimum = minimum < value ? 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;
+ }
+
+ @SuppressWarnings("restriction")
+ private static final Unsafe THE_UNSAFE;
+ static
+ {
+ try
+ {
+ final PrivilegedExceptionAction action = new PrivilegedExceptionAction()
+ {
+ public Unsafe run() throws Exception
+ {
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ return (Unsafe) theUnsafe.get(null);
+ }
+ };
+
+ THE_UNSAFE = AccessController.doPrivileged(action);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Unable to load unsafe", e);
+ }
+ }
+
+ /**
+ * Get a handle on the Unsafe instance, used for accessing low-level concurrency
+ * and memory constructs.
+ * @return The Unsafe
+ */
+ public static Unsafe getUnsafe()
+ {
+ return THE_UNSAFE;
+ }
+
+ /**
+ * Gets the address value for the memory that backs a direct byte buffer.
+ * @param buffer
+ * @return The system address for the buffers
+ */
+ public static long getAddressFromDirectByteBuffer(ByteBuffer buffer)
+ {
+ try
+ {
+ Field addressField = Buffer.class.getDeclaredField("address");
+ addressField.setAccessible(true);
+ return addressField.getLong(buffer);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Unable to address field from ByteBuffer", e);
+ }
+ }
+
+
+ /**
+ * Calculate the log base 2 of the supplied integer, essentially reports the location
+ * of the highest bit.
+ *
+ * @param i Value to calculate log2 for.
+ * @return The log2 value
+ */
+ public static int log2(int i)
+ {
+ int r = 0;
+ while ((i >>= 1) != 0)
+ {
+ ++r;
+ }
+ return r;
+ }
+}
Index: log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java
===================================================================
--- log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java (revision 0)
+++ log4j-async/src/main/java/org/apache/logging/log4j/async/config/AsyncLoggerConfig.java (working copy)
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.async.config;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventFactory;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.EventTranslator;
+import org.apache.logging.log4j.async.com.lmax.disruptor.ExceptionHandler;
+import org.apache.logging.log4j.async.com.lmax.disruptor.RingBuffer;
+import org.apache.logging.log4j.async.com.lmax.disruptor.dsl.Disruptor;
+import org.apache.logging.log4j.async.com.lmax.disruptor.util.Util;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.LoggerContext.Status;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Asynchronous Logger object that is created via configuration.
+ */
+@Plugin(name = "asyncLogger", type = "Core", printObject = true)
+public class AsyncLoggerConfig extends LoggerConfig {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static volatile Disruptor disruptor;
+ private static ExecutorService executor = Executors
+ .newSingleThreadExecutor();
+
+ private ThreadLocal currentLogEvent = new ThreadLocal();
+
+ /**
+ * RingBuffer events contain all information necessary to perform the work
+ * in a separate thread.
+ */
+ private static class RingBufferLog4jEvent {
+ private AsyncLoggerConfig loggerConfig;
+ private LogEvent event;
+ }
+
+ /**
+ * Factory used to populate the RingBuffer with events. These event objects
+ * are then re-used during the life of the RingBuffer.
+ */
+ private static final EventFactory FACTORY = new EventFactory() {
+ // @Override
+ public RingBufferLog4jEvent newInstance() {
+ return new RingBufferLog4jEvent();
+ }
+ };
+
+ /**
+ * Object responsible for passing on data to a specific RingBuffer event.
+ */
+ private final EventTranslator translator = new EventTranslator() {
+ // @Override
+ public void translateTo(RingBufferLog4jEvent event, long sequence) {
+ event.event = currentLogEvent.get();
+ event.loggerConfig = AsyncLoggerConfig.this;
+ }
+ };
+
+ /**
+ * EventHandler performs the work in a separate thread.
+ */
+ private static class RingBufferLog4jEventHandler implements
+ EventHandler {
+ // @Override
+ public void onEvent(RingBufferLog4jEvent event, long sequence,
+ boolean endOfBatch) throws Exception {
+ event.loggerConfig.asyncCallAppenders(event.event, endOfBatch);
+ }
+ }
+
+ /**
+ * Default constructor.
+ */
+ public AsyncLoggerConfig() {
+ super();
+ }
+
+ /**
+ * Constructor that sets the name, level and additive values.
+ *
+ * @param name The Logger name.
+ * @param level The Level.
+ * @param additive true if the Logger is additive, false otherwise.
+ */
+ public AsyncLoggerConfig(final String name, final Level level,
+ final boolean additive) {
+ super(name, level, additive);
+ }
+
+ protected AsyncLoggerConfig(final String name,
+ final List appenders, final Filter filter,
+ final Level level, final boolean additive,
+ final Property[] properties, final Configuration config,
+ boolean isLocationRequired) {
+ super(name, appenders, filter, level, additive, properties, config,
+ isLocationRequired);
+ }
+
+ /**
+ * Passes on the event to a separate thread that will call
+ * {@link #asyncCallAppenders(LogEvent)}.
+ */
+ // @Override
+ protected void callAppenders(LogEvent event) {
+ // populate lazily initialized fields
+ event.getSource();
+ event.getThreadName();
+
+ // pass on the event to a separate thread
+ currentLogEvent.set(event);
+ disruptor.publishEvent(translator);
+ }
+
+ /** Called by RingBufferLog4jEventHandler. */
+ private void asyncCallAppenders(LogEvent event, boolean endOfBatch) {
+ event.setEndOfBatch(endOfBatch);
+ super.callAppenders(event);
+ }
+
+ @Override
+ public void startFilter() {
+ if (disruptor == null) {
+ int ringBufferSize = calculateRingBufferSize();
+
+ disruptor = new Disruptor(FACTORY,
+ ringBufferSize, executor);
+ EventHandler[] handlers = new RingBufferLog4jEventHandler[] { new RingBufferLog4jEventHandler() };
+ disruptor.handleExceptionsWith(getExceptionHandler());
+ disruptor.handleEventsWith(handlers);
+
+ LOGGER.debug(
+ "Starting AsyncLoggerConfig disruptor with ringbuffer size {}...",
+ disruptor.getRingBuffer().getBufferSize());
+ disruptor.start();
+ }
+ super.startFilter();
+ }
+
+ private static int calculateRingBufferSize() {
+ String userPreferredRBSize = System.getProperty(
+ "AsyncLoggerConfig.RingBufferSize", "256000");
+ int ringBufferSize = 256000; // default
+ try {
+ int size = Integer.parseInt(userPreferredRBSize);
+ if (size < 128) {
+ size = 128;
+ LOGGER.warn(
+ "Invalid RingBufferSize {}, using minimum size 128.",
+ userPreferredRBSize);
+ }
+ ringBufferSize = size;
+ } catch (Exception ex) {
+ LOGGER.warn("Invalid RingBufferSize {}, using default size.",
+ userPreferredRBSize);
+ }
+ return Util.ceilingNextPowerOfTwo(ringBufferSize);
+ }
+
+ private static ExceptionHandler getExceptionHandler() {
+ String cls = System.getProperty("AsyncLoggerConfig.ExceptionHandler");
+ if (cls == null) {
+ return null;
+ }
+ try {
+ @SuppressWarnings("unchecked")
+ Class extends ExceptionHandler> klass = (Class extends ExceptionHandler>) Class
+ .forName(cls);
+ return klass.newInstance();
+ } catch (Exception ignored) {
+ return null;
+ }
+ }
+
+ @Override
+ public void stopFilter() {
+ // only stop disruptor if shutting down logging subsystem
+ if (LogManager.getContext() instanceof LoggerContext) {
+ if (((LoggerContext) LogManager.getContext()).getStatus() != Status.STOPPING) {
+ return;
+ }
+ }
+ Disruptor temp = disruptor;
+
+ // Must guarantee that publishing to the RingBuffer has stopped
+ // before we call disruptor.shutdown()
+ disruptor = null; // client code fails with NPE if log after stop = OK
+ temp.shutdown();
+
+ // wait up to 10 seconds for the ringbuffer to drain
+ RingBuffer ringBuffer = temp.getRingBuffer();
+ for (int i = 0; i < 20; i++) {
+ if (ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize())) {
+ break;
+ }
+ try {
+ Thread.sleep(500); // give ringbuffer some time to drain...
+ } catch (InterruptedException e) {
+ }
+ }
+ executor.shutdown(); // finally, kill the processor thread
+ super.stopFilter();
+ }
+
+ /**
+ * Factory method to create a LoggerConfig.
+ *
+ * @param additivity True if additive, false otherwise.
+ * @param levelName The Level to be associated with the Logger.
+ * @param loggerName The name of the Logger.
+ * @param refs An array of Appender names.
+ * @param properties Properties to pass to the Logger.
+ * @param config The Configuration.
+ * @param filter A Filter.
+ * @return A new LoggerConfig.
+ */
+ @PluginFactory
+ public static LoggerConfig createLogger(
+ @PluginAttr("additivity") final String additivity,
+ @PluginAttr("level") final String levelName,
+ @PluginAttr("name") final String loggerName,
+ @PluginAttr("needsLocation") final String needsLocation,
+ @PluginElement("appender-ref") final AppenderRef[] refs,
+ @PluginElement("properties") final Property[] properties,
+ @PluginConfiguration final Configuration config,
+ @PluginElement("filters") final Filter filter) {
+ if (loggerName == null) {
+ LOGGER.error("Loggers cannot be configured without a name");
+ return null;
+ }
+
+ final List appenderRefs = Arrays.asList(refs);
+ Level level;
+ try {
+ level = Level.toLevel(levelName, Level.ERROR);
+ } catch (final Exception ex) {
+ LOGGER.error(
+ "Invalid Log level specified: {}. Defaulting to Error",
+ levelName);
+ level = Level.ERROR;
+ }
+ final String name = loggerName.equals("root") ? "" : loggerName;
+ final boolean additive = additivity == null ? true : Boolean
+ .parseBoolean(additivity);
+ final boolean isLocationRequired = needsLocation == null ? true
+ : Boolean.parseBoolean(needsLocation);
+
+ return new AsyncLoggerConfig(name, appenderRefs, filter, level,
+ additive, properties, config, isLocationRequired);
+ }
+
+ /**
+ * An asynchronous root Logger.
+ */
+ @Plugin(name = "asyncRoot", type = "Core", printObject = true)
+ public static class RootLogger extends LoggerConfig {
+
+ @PluginFactory
+ public static LoggerConfig createLogger(
+ @PluginAttr("additivity") final String additivity,
+ @PluginAttr("level") final String levelName,
+ @PluginAttr("needsLocation") final String needsLocation,
+ @PluginElement("appender-ref") final AppenderRef[] refs,
+ @PluginElement("properties") final Property[] properties,
+ @PluginConfiguration final Configuration config,
+ @PluginElement("filters") final Filter filter) {
+ final List appenderRefs = Arrays.asList(refs);
+ Level level;
+ try {
+ level = Level.toLevel(levelName, Level.ERROR);
+ } catch (final Exception ex) {
+ LOGGER.error(
+ "Invalid Log level specified: {}. Defaulting to Error",
+ levelName);
+ level = Level.ERROR;
+ }
+ final boolean additive = additivity == null ? true : Boolean
+ .parseBoolean(additivity);
+ final boolean isLocationRequired = needsLocation == null ? true
+ : Boolean.parseBoolean(needsLocation);
+
+ return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
+ appenderRefs, filter, level, additive, properties, config,
+ isLocationRequired);
+ }
+ }
+}
Index: log4j-async/src/test/java/AsyncLoggerConfigTest.xml
===================================================================
--- log4j-async/src/test/java/AsyncLoggerConfigTest.xml (revision 0)
+++ log4j-async/src/test/java/AsyncLoggerConfigTest.xml (working copy)
@@ -0,0 +1,19 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %m %location %ex%n
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/AsyncLoggerLocationTest.xml
===================================================================
--- log4j-async/src/test/java/AsyncLoggerLocationTest.xml (revision 0)
+++ log4j-async/src/test/java/AsyncLoggerLocationTest.xml (working copy)
@@ -0,0 +1,17 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %location %m %ex%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/AsyncLoggerTest.xml
===================================================================
--- log4j-async/src/test/java/AsyncLoggerTest.xml (revision 0)
+++ log4j-async/src/test/java/AsyncLoggerTest.xml (working copy)
@@ -0,0 +1,17 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %location %m %ex%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/FastFileAppenderLocationTest.xml
===================================================================
--- log4j-async/src/test/java/FastFileAppenderLocationTest.xml (revision 0)
+++ log4j-async/src/test/java/FastFileAppenderLocationTest.xml (working copy)
@@ -0,0 +1,19 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %m %location %ex%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/FastFileAppenderTest.xml
===================================================================
--- log4j-async/src/test/java/FastFileAppenderTest.xml (revision 0)
+++ log4j-async/src/test/java/FastFileAppenderTest.xml (working copy)
@@ -0,0 +1,19 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %m %location %ex%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/FastRollingFileAppenderLocationTest.xml
===================================================================
--- log4j-async/src/test/java/FastRollingFileAppenderLocationTest.xml (revision 0)
+++ log4j-async/src/test/java/FastRollingFileAppenderLocationTest.xml (working copy)
@@ -0,0 +1,21 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %m %location %ex%n
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/FastRollingFileAppenderTest.xml
===================================================================
--- log4j-async/src/test/java/FastRollingFileAppenderTest.xml (revision 0)
+++ log4j-async/src/test/java/FastRollingFileAppenderTest.xml (working copy)
@@ -0,0 +1,21 @@
+
+
+
+
+
+ %d %p %c{1.} [%t] %X{aKey} %m %location %ex%n
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextSelectorTest.java (working copy)
@@ -0,0 +1,37 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.junit.Test;
+
+public class AsyncLoggerContextSelectorTest {
+
+ @Test
+ public void testContextReturnsAsyncLoggerContext() {
+ AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
+ LoggerContext context = selector.getContext(null, null, false);
+
+ assertTrue(context instanceof AsyncLoggerContext);
+ }
+
+ @Test
+ public void testContext2ReturnsAsyncLoggerContext() {
+ AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
+ LoggerContext context = selector.getContext(null, null, false, null);
+
+ assertTrue(context instanceof AsyncLoggerContext);
+ }
+
+ @Test
+ public void testLoggerContextsReturnsAsyncLoggerContext() {
+ AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
+ List list = selector.getLoggerContexts();
+
+ assertEquals(1, list.size());
+ assertTrue(list.get(0) instanceof AsyncLoggerContext);
+ }
+
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerContextTest.java (working copy)
@@ -0,0 +1,21 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.junit.Test;
+
+public class AsyncLoggerContextTest {
+
+ @Test
+ public void testNewInstanceReturnsAsyncLogger() {
+ Logger logger = new AsyncLoggerContext("a").newInstance(
+ new LoggerContext("a"), "a", null);
+ assertTrue(logger instanceof AsyncLogger);
+
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+ }
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerLocationTest.java (working copy)
@@ -0,0 +1,54 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.apache.logging.log4j.core.helpers.Constants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AsyncLoggerLocationTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
+ AsyncLoggerContextSelector.class.getName());
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "AsyncLoggerLocationTest.xml");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, "");
+ }
+
+ @Test
+ public void testAsyncLogWritesToLog() throws Exception {
+ File f = new File("AsyncLoggerLocationTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Async logger msg with location";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertTrue("line1 correct", line1.contains(msg));
+
+ String location = "testAsyncLogWritesToLog";
+ assertTrue("has location", line1.contains(location));
+ }
+
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/AsyncLoggerTest.java (working copy)
@@ -0,0 +1,54 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.apache.logging.log4j.core.helpers.Constants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AsyncLoggerTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
+ AsyncLoggerContextSelector.class.getName());
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "AsyncLoggerTest.xml");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, "");
+ }
+
+ @Test
+ public void testAsyncLogWritesToLog() throws Exception {
+ File f = new File("AsyncLoggerTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Async logger msg";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertTrue("line1 correct", line1.contains(msg));
+
+ String location = "testAsyncLogWritesToLog";
+ assertTrue("no location", !line1.contains(location));
+ }
+
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/CachedClockTest.java (working copy)
@@ -0,0 +1,30 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class CachedClockTest {
+
+ @Test
+ public void testLessThan17Millis() {
+ long millis1 = CachedClock.instance().currentTimeMillis();
+ long sysMillis = System.currentTimeMillis();
+
+ long diff = sysMillis - millis1;
+
+ assertTrue("diff too large: " + diff, diff <= 16);
+ }
+
+ @Test
+ public void testAfterWaitStillLessThan17Millis() throws Exception {
+ Thread.sleep(100);
+ long millis1 = CachedClock.instance().currentTimeMillis();
+ long sysMillis = System.currentTimeMillis();
+
+ long diff = sysMillis - millis1;
+
+ assertTrue("diff too large: " + diff, diff <= 16);
+ }
+
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/SystemClockTest.java (working copy)
@@ -0,0 +1,30 @@
+package org.apache.logging.log4j.async;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class SystemClockTest {
+
+ @Test
+ public void testLessThan2Millis() {
+ long millis1 = new SystemClock().currentTimeMillis();
+ long sysMillis = System.currentTimeMillis();
+
+ long diff = sysMillis - millis1;
+
+ assertTrue("diff too large: " + diff, diff <= 1);
+ }
+
+ @Test
+ public void testAfterWaitStillLessThan2Millis() throws Exception {
+ Thread.sleep(100);
+ long millis1 = new SystemClock().currentTimeMillis();
+ long sysMillis = System.currentTimeMillis();
+
+ long diff = sysMillis - millis1;
+
+ assertTrue("diff too large: " + diff, diff <= 1);
+ }
+
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderLocationTest.java (working copy)
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastFileAppenderLocationTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "FastFileAppenderLocationTest.xml");
+ }
+
+ @Test
+ public void testLocationIncluded() throws Exception {
+ File f = new File("FastFileAppenderLocationTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Message with location, flushed with immediate flush=false";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertTrue("line1 correct", line1.contains(msg));
+
+ String location = "testLocationIncluded";
+ assertTrue("has location", line1.contains(location));
+ }
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastFileAppenderTest.java (working copy)
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastFileAppenderTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "FastFileAppenderTest.xml");
+ }
+
+ @Test
+ public void testFlushAtEndOfBatch() throws Exception {
+ File f = new File("FastFileAppenderTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Message flushed with immediate flush=false";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertTrue("line1 correct", line1.contains(msg));
+
+ String location = "testFlushAtEndOfBatch";
+ assertTrue("no location", !line1.contains(location));
+ }
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderLocationTest.java (working copy)
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastRollingFileAppenderLocationTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "FastRollingFileAppenderLocationTest.xml");
+ }
+
+ @Test
+ public void testLocationIncluded() throws Exception {
+ File f = new File("FastRollingFileAppenderLocationTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Message with location, flushed with immediate flush=false";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertTrue("line1 correct", line1.contains(msg));
+
+ String location = "testLocationIncluded";
+ assertTrue("has location", line1.contains(location));
+ }
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/appender/FastRollingFileAppenderTest.java (working copy)
@@ -0,0 +1,44 @@
+package org.apache.logging.log4j.async.appender;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FastRollingFileAppenderTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "FastRollingFileAppenderTest.xml");
+ }
+
+ @Test
+ public void testFlushAtEndOfBatch() throws Exception {
+ File f = new File("FastRollingFileAppenderTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Message flushed with immediate flush=false";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertTrue("line1 correct", line1.contains(msg));
+
+ String location = "testFlushAtEndOfBatch";
+ assertTrue("no location", !line1.contains(location));
+ }
+}
Index: log4j-async/src/test/java/org/apache/logging/log4j/async/config/AsyncLoggerConfigTest.java
===================================================================
--- log4j-async/src/test/java/org/apache/logging/log4j/async/config/AsyncLoggerConfigTest.java (revision 0)
+++ log4j-async/src/test/java/org/apache/logging/log4j/async/config/AsyncLoggerConfigTest.java (working copy)
@@ -0,0 +1,49 @@
+package org.apache.logging.log4j.async.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class AsyncLoggerConfigTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "AsyncLoggerConfigTest.xml");
+ }
+
+ @Test
+ public void testAdditivity() throws Exception {
+ File f = new File("AsyncLoggerConfigTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ Logger log = LogManager.getLogger("com.foo.Bar");
+ String msg = "Additive logging: 2 for the price of 1!";
+ log.info(msg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1 = reader.readLine();
+ String line2 = reader.readLine();
+ reader.close();
+ f.delete();
+ assertNotNull("line1", line1);
+ assertNotNull("line2", line2);
+ assertTrue("line1 correct", line1.contains(msg));
+ assertTrue("line2 correct", line2.contains(msg));
+
+ String location = "testAdditivity";
+ assertTrue("location",
+ line1.contains(location) || line2.contains(location));
+ }
+
+}