Package org.apache.log4j

Source Code of org.apache.log4j.Dispatcher

/*
* Copyright 1999,2004 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>
package org.apache.log4j;

import org.apache.log4j.helpers.AppenderAttachableImpl;
import org.apache.log4j.helpers.BoundedFIFO;
import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.LoggingEvent;

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>
*
* <p>
* The AsyncAppender uses a separate thread to serve the events in its bounded
* buffer.
* </p>

*  * <p>
* <b>Important note:</b> The <code>AsyncAppender</code> can only be script
* configured using the {@link org.apache.log4j.joran.JoranConfigurator}.
* </p>
*
* @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());
  private BoundedFIFO bf = new BoundedFIFO(DEFAULT_BUFFER_SIZE);
  AppenderAttachableImpl aai;
  private Dispatcher dispatcher;
  private boolean locationInfo = false;
  private boolean interruptedWarningMessage = false;

  public AsyncAppender() {
    // Note: The dispatcher code assumes that the aai is set once and
    // for all.
    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) {
    // extract all the thread dependent information now as later it will
    // be too late.
    event.prepareForDeferredProcessing();
    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;
            getLogger().warn("AsyncAppender interrupted.", e);
          } else {
            getLogger().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) {
      getLogger().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);
  }

  /**
   * @deprecated Will be removed with no replacement.
   */
  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.
   * </p>
   */
  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() {
      // Bugzilla 23912
      synchronized(bf) { 
          return bf.getMaxSize();
      }
  }
}


// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// ----------------------------------------------------------------------------
class Dispatcher extends Thread {
  private BoundedFIFO bf;
  private AppenderAttachableImpl aai;
  private 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.
   * </p>
   */
  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) {
            break;
          }
        }

        event = bf.get();

        if (bf.wasFull()) {
          //LogLog.debug("Notifying AsyncAppender about freed space.");
          bf.notify();
        }
      }
       // synchronized

      synchronized (container.aai) {
        if ((aai != null) && (event != null)) {
          aai.appendLoopOnAppenders(event);
        }
      }
    }
     // while

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

Related Classes of org.apache.log4j.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.