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()); } } }