package demo;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
/**
*
* CompletelyAsyncAppender
*
*
* The CompletelyAsyncAppender lets users log event asynchronously.
*
*
*
* The AsyncAppender will collect the events sent to it and then dispatch them
* to all the appenders that are attached to it. You can attach multiple
* appenders to an AsyncAppender.
*
*
*
* The AsyncAppender uses a separate thread pool to serve the events in its
* buffer.
*
*
*
* This implementation improves logback default implementation, that will block
* new logging requests if the queue is filled up (pseudo-synchronous when the
* appender is operating at or near the capacity of its event buffer). This
* implementation will drop logging events if the queue is full.
*
*
* @author Maxim Kirilov
*
*/
public class CompletelyAsyncAppender extends UnsynchronizedAppenderBase
implements AppenderAttachable {
/** The default buffer size. */
public static final int DEFAULT_QUEUE_SIZE = 256;
/** The default number of workers that handle the logging requests. */
public static final int DEFAULT_WORKERS_COUNT = 1;
/**
* Logging events queue size.
*/
private int queueSize = DEFAULT_QUEUE_SIZE;
private int appenderCount = 0;
private int workersCount = DEFAULT_WORKERS_COUNT;
/**
* Holds the dispatching workers.
*/
private ExecutorService threadPool;
/**
*
*/
private AppenderAttachableImpl aai = new AppenderAttachableImpl<>();
@Override
public void start() {
if (appenderCount == 0) {
addError("No attached appenders found.");
return;
}
if (queueSize < 1) {
addError("Invalid queue size [" + queueSize + "]");
return;
}
addInfo("Starting ThreadPoolExecutor with " + getWorkersCount()
+ " workers.");
threadPool = new ThreadPoolExecutor(1, workersCount, 1L,
TimeUnit.MINUTES, new ArrayBlockingQueue(queueSize),
new ThreadPoolExecutor.DiscardPolicy());
super.start();
}
@Override
public void stop() {
super.stop();
if (threadPool != null) {
threadPool.shutdownNow();
}
}
@Override
public void addAppender(Appender newAppender) {
appenderCount++;
addInfo("Attaching appender named [" + newAppender.getName() + "] to AsyncAppender.");
aai.addAppender(newAppender);
}
@Override
public void detachAndStopAllAppenders() {
aai.detachAndStopAllAppenders();
}
@Override
public boolean detachAppender(Appender eAppender) {
return aai.detachAppender(eAppender);
}
@Override
public boolean detachAppender(String name) {
return aai.detachAppender(name);
}
@Override
public Appender getAppender(String name) {
return aai.getAppender(name);
}
@Override
public boolean isAttached(Appender eAppender) {
return aai.isAttached(eAppender);
}
@Override
public Iterator> iteratorForAppenders() {
return aai.iteratorForAppenders();
}
public int getQueueSize() {
return queueSize;
}
public void setQueueSize(int queueSize) {
this.queueSize = queueSize;
}
public int getWorkersCount() {
return workersCount;
}
public void setWorkersCount(int workersCount) {
this.workersCount = workersCount;
}
@Override
protected void append(final ILoggingEvent event) {
if (aai == null || event == null) {
return;
}
try {
threadPool.execute(new Runnable() {
@Override
public void run() {
if (isStarted()) {
aai.appendLoopOnAppenders(event);
}
}
});
} catch (RejectedExecutionException e) {
addWarn("Logging Request rejected due to: " + e.getMessage());
}
}
}