// AsyncAppender.java -- // // AsyncAppender.java is part of the ElectricCommander server. // // Copyright (c) 2005-2009 Electric Cloud, Inc. // All rights reserved. // package com.electriccloud.log; import java.util.Iterator; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.spi.AppenderAttachable; import ch.qos.logback.core.spi.AppenderAttachableImpl; @SuppressWarnings({"ParameterHidesMemberVariable"}) public class AsyncAppender extends AppenderBase implements AppenderAttachable { //~ Static fields/initializers --------------------------------------------- /** * Bound the queue used to forward events to the dispatcher. When it fills * up, {@link #append(LoggingEvent)} will block. */ private static final int QUEUE_SIZE = 1000; //~ Instance fields -------------------------------------------------------- /** The appenders we are forwarding events to. */ private final AppenderAttachableImpl m_appenders = new AppenderAttachableImpl(); /** Queue that is used to forward events to the dispatcher. */ private final BlockingQueue m_queue = new LinkedBlockingQueue( QUEUE_SIZE); /** Thread that dispatches queued events to the appenders. */ private final Dispatcher m_dispatcher = new Dispatcher(m_appenders, m_queue); private final Thread m_thread = new Thread(m_dispatcher, "AsyncLogDispatcher"); //~ Methods ---------------------------------------------------------------- @Override public void addAppender(Appender newAppender) { m_appenders.addAppender(newAppender); } @Override public void detachAndStopAllAppenders() { m_appenders.detachAndStopAllAppenders(); } @Override public boolean detachAppender(Appender appender) { return m_appenders.detachAppender(appender); } @Override public boolean detachAppender(String name) { return m_appenders.detachAppender(name); } @Override public Iterator> iteratorForAppenders() { return m_appenders.iteratorForAppenders(); } @Override public void start() { super.start(); // Since (most) logging packages don't have a shutdown method, make this // a daemon thread so it exits by itself m_thread.setDaemon(true); m_thread.start(); } @Override public void stop() { // Tell the dispatcher we want to stop m_dispatcher.stop(); // Interrupt the thread to allow it to exit if it's blocking on the // queue m_thread.interrupt(); super.stop(); } @Override protected void append(E event) { if (!m_dispatcher.isStopRequested() && m_thread.isAlive()) { // Popuplates the event with information from this thread event.prepareForDeferredProcessing(); // Queue up the event -- it will be processed by the dispatcher // thread try { m_queue.put(event); } catch (InterruptedException ignored) { // Restore interrupted status Thread.currentThread() .interrupt(); } } else { // If the dispatcher is no longer running, handle events // synchronously m_appenders.appendLoopOnAppenders(event); } } @Override public Appender getAppender(String name) { return m_appenders.getAppender(name); } @Override public boolean isAttached(Appender appender) { return m_appenders.isAttached(appender); } //~ Inner Classes ---------------------------------------------------------- private static class Dispatcher implements Runnable { //~ Instance fields ---------------------------------------------------- private final BlockingQueue m_queue; private final AppenderAttachableImpl m_appenders; // Set to true when the dispatcher thread should exit private final AtomicBoolean m_stopRequested = new AtomicBoolean(false); //~ Constructors ------------------------------------------------------- private Dispatcher(AppenderAttachableImpl appenders, BlockingQueue queue) { m_appenders = appenders; m_queue = queue; } //~ Methods ------------------------------------------------------------ @Override public void run() { while (!m_stopRequested.get()) { E event = null; try { event = m_queue.take(); } catch (InterruptedException ignored) { // Restore interrupted status Thread.currentThread() .interrupt(); } if (event != null) { m_appenders.appendLoopOnAppenders(event); } } } public void stop() { m_stopRequested.set(true); } public boolean isStopRequested() { return m_stopRequested.get(); } } }