Package org.commoncrawl.util.shared

Source Code of org.commoncrawl.util.shared.Dispatcher

package org.commoncrawl.util.shared;

/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* 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.
*/

// Contibutors:  Aaron Greenhouse <aarong@cs.cmu.edu>
//               Thomas Tuft Muller <ttm@online.no>

import org.apache.log4j.Appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.helpers.BoundedFIFO;
import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.helpers.AppenderAttachableImpl;
import org.apache.log4j.helpers.LogLog;
import java.util.Enumeration;

/**
* The AsyncAppender lets users log events asynchronously. It uses a bounded
* buffer to store logging events.
*
* <p>
* 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.
*
* <p>
* The AsyncAppender uses a separate thread to serve the events in its bounded
* buffer.
*
* <p>
* Refer to the results in {@link org.apache.log4j.performance.Logging} for the
* impact of using this appender.
*
* <p>
* <b>Important note:</b> The <code>AsyncAppender</code> can only be script
* configured using the {@link org.apache.log4j.xml.DOMConfigurator}.
*
* @author Ceki G&uuml;lc&uuml;
* @since 0.9.1
*/
public class AsyncAppender extends AppenderSkeleton implements
    AppenderAttachable {

  /** The default buffer size is set to 128 events. */
  public static final int DEFAULT_BUFFER_SIZE       = 128;

  // static Category cat = Category.getInstance(AsyncAppender.class.getName());

  BoundedFIFO             bf;

  AppenderAttachableImpl  aai;
  Dispatcher              dispatcher;
  boolean                 locationInfo              = false;

  boolean                 interruptedWarningMessage = false;

  public AsyncAppender(int logBufferQueueSize) {
    // Note: The dispatcher code assumes that the aai is set once and
    // for all.
    bf = new BoundedFIFO(Math.max(DEFAULT_BUFFER_SIZE, logBufferQueueSize));
    aai = new AppenderAttachableImpl();
    dispatcher = new Dispatcher(bf, this);
    dispatcher.start();
  }

  public void addAppender(Appender newAppender) {
    synchronized (aai) {
      aai.addAppender(newAppender);
    }
  }

  public void append(LoggingEvent event) {
    //
    // if dispatcher thread has died then
    // append subsequent events synchronously
    // See bug 23021
    if (!dispatcher.isAlive()) {
      synchronized (aai) {
        aai.appendLoopOnAppenders(event);
      }
      return;
    }
    // Set the NDC and thread name for the calling thread as these
    // LoggingEvent fields were not set at event creation time.
    event.getNDC();
    event.getThreadName();
    // Get a copy of this thread's MDC.
    event.getMDCCopy();
    if (locationInfo) {
      event.getLocationInformation();
    }
    synchronized (bf) {
      while (bf.isFull()) {
        try {
          // LogLog.debug("Waiting for free space in buffer, "+bf.length());
          bf.wait();
        } catch (InterruptedException e) {
          if (!interruptedWarningMessage) {
            interruptedWarningMessage = true;
            LogLog.warn("AsyncAppender interrupted.", e);
          } else {
            LogLog.warn("AsyncAppender interrupted again.");
          }
        }
      }

      // cat.debug("About to put new event in buffer.");
      bf.put(event);
      if (bf.wasEmpty()) {
        // cat.debug("Notifying dispatcher to process events.");
        bf.notify();
      }
    }
  }

  /**
   * Close this <code>AsyncAppender</code> by interrupting the dispatcher thread
   * which will process all pending events before exiting.
   */
  public void close() {
    synchronized (this) {
      // avoid multiple close, otherwise one gets NullPointerException
      if (closed) {
        return;
      }
      closed = true;
    }

    // The following cannot be synchronized on "this" because the
    // dispatcher synchronizes with "this" in its while loop. If we
    // did synchronize we would systematically get deadlocks when
    // close was called.
    dispatcher.close();
    try {
      dispatcher.join();
    } catch (InterruptedException e) {
      LogLog.error("Got an InterruptedException while waiting for the "
          + "dispatcher to finish.", e);
    }
    dispatcher = null;
    bf = null;
  }

  public Enumeration getAllAppenders() {
    synchronized (aai) {
      return aai.getAllAppenders();
    }
  }

  public Appender getAppender(String name) {
    synchronized (aai) {
      return aai.getAppender(name);
    }
  }

  /**
   * Returns the current value of the <b>LocationInfo</b> option.
   */
  public boolean getLocationInfo() {
    return locationInfo;
  }

  /**
   * Is the appender passed as parameter attached to this category?
   */
  public boolean isAttached(Appender appender) {
    return aai.isAttached(appender);
  }

  /**
   * The <code>AsyncAppender</code> does not require a layout. Hence, this
   * method always returns <code>false</code>.
   */
  public boolean requiresLayout() {
    return false;
  }

  public void removeAllAppenders() {
    synchronized (aai) {
      aai.removeAllAppenders();
    }
  }

  public void removeAppender(Appender appender) {
    synchronized (aai) {
      aai.removeAppender(appender);
    }
  }

  public void removeAppender(String name) {
    synchronized (aai) {
      aai.removeAppender(name);
    }
  }

  /**
   * The <b>LocationInfo</b> option takes a boolean value. By default, it is set
   * to false which means there will be no effort to extract the location
   * information related to the event. As a result, the event that will be
   * ultimately logged will likely to contain the wrong location information (if
   * present in the log format).
   *
   * <p>
   * Location information extraction is comparatively very slow and should be
   * avoided unless performance is not a concern.
   * */
  public void setLocationInfo(boolean flag) {
    locationInfo = flag;
  }

  /**
   * The <b>BufferSize</b> option takes a non-negative integer value. This
   * integer value determines the maximum size of the bounded buffer. Increasing
   * the size of the buffer is always safe. However, if an existing buffer holds
   * unwritten elements, then
   * <em>decreasing the buffer size will result in event
   * loss.</em> Nevertheless, while script configuring the AsyncAppender, it is
   * safe to set a buffer size smaller than the {@link #DEFAULT_BUFFER_SIZE
   * default buffer size} because configurators guarantee that an appender
   * cannot be used before being completely configured.
   * */
  public void setBufferSize(int size) {
    bf.resize(size);
  }

  /**
   * Returns the current value of the <b>BufferSize</b> option.
   */
  public int getBufferSize() {
    return bf.getMaxSize();
  }

}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// ----------------------------------------------------------------------------
class Dispatcher extends Thread {

  BoundedFIFO            bf;
  AppenderAttachableImpl aai;
  boolean                interrupted = false;
  AsyncAppender          container;

  Dispatcher(BoundedFIFO bf, AsyncAppender container) {
    this.bf = bf;
    this.container = container;
    this.aai = container.aai;
    // It is the user's responsibility to close appenders before
    // exiting.
    this.setDaemon(true);
    // set the dispatcher priority to lowest possible value
    this.setPriority(Thread.MIN_PRIORITY);
    this.setName("Dispatcher-" + getName());

    // set the dispatcher priority to MIN_PRIORITY plus or minus 2
    // depending on the direction of MIN to MAX_PRIORITY.
    // + (Thread.MAX_PRIORITY > Thread.MIN_PRIORITY ? 1 : -1)*2);

  }

  void close() {
    synchronized (bf) {
      interrupted = true;
      // We have a waiting dispacther if and only if bf.length is
      // zero. In that case, we need to give it a death kiss.
      if (bf.length() == 0) {
        bf.notify();
      }
    }
  }

  /**
   * The dispatching strategy is to wait until there are events in the buffer to
   * process. After having processed an event, we release the monitor (variable
   * bf) so that new events can be placed in the buffer, instead of keeping the
   * monitor and processing the remaining events in the buffer.
   *
   * <p>
   * Other approaches might yield better results.
   */
  public void run() {

    // Category cat = Category.getInstance(Dispatcher.class.getName());

    LoggingEvent event;

    while (true) {
      synchronized (bf) {
        if (bf.length() == 0) {
          // Exit loop if interrupted but only if the the buffer is empty.
          if (interrupted) {
            // cat.info("Exiting.");
            break;
          }
          try {
            // LogLog.debug("Waiting for new event to dispatch.");
            bf.wait();
          } catch (InterruptedException e) {
            LogLog.error("The dispathcer should not be interrupted.");
            break;
          }
        }
        event = bf.get();
        if (bf.wasFull()) {
          // LogLog.debug("Notifying AsyncAppender about freed space.");
          bf.notify();
        }
      } // synchronized

      // The synchronization on parent is necessary to protect against
      // operations on the aai object of the parent
      synchronized (container.aai) {
        if (aai != null && event != null) {
          aai.appendLoopOnAppenders(event);
        }
      }
    } // while

    // close and remove all appenders
    aai.removeAllAppenders();
  }
}
TOP

Related Classes of org.commoncrawl.util.shared.Dispatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.