logging-log4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From carn...@apache.org
Subject svn commit: r376902 - in /logging/sandbox/log4j/concurrent: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/log4j/ src/main/java/org/apache/log4j/concurrent/ src/test/ src/test/java/ src/test/java/...
Date Sat, 11 Feb 2006 01:10:12 GMT
Author: carnold
Date: Fri Feb 10 17:10:09 2006
New Revision: 376902

URL: http://svn.apache.org/viewcvs?rev=376902&view=rev
Log:
Bug 24159: Adding concurrent appender to sandbox

Added:
    logging/sandbox/log4j/concurrent/
    logging/sandbox/log4j/concurrent/build.xml
    logging/sandbox/log4j/concurrent/pom.xml
    logging/sandbox/log4j/concurrent/src/
    logging/sandbox/log4j/concurrent/src/main/
    logging/sandbox/log4j/concurrent/src/main/java/
    logging/sandbox/log4j/concurrent/src/main/java/org/
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConcurrentAppender.java
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConsoleAppender.java
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/FileAppender.java
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/PatternLayout.java
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/SynchronizedBoolean.java
    logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/WriterAppender.java
    logging/sandbox/log4j/concurrent/src/test/
    logging/sandbox/log4j/concurrent/src/test/java/
    logging/sandbox/log4j/concurrent/src/test/java/org/
    logging/sandbox/log4j/concurrent/src/test/java/org/apache/
    logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/
    logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/
    logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/DeadlockTest.java
    logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/PerformanceTest.java

Added: logging/sandbox/log4j/concurrent/build.xml
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/build.xml?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/build.xml (added)
+++ logging/sandbox/log4j/concurrent/build.xml Fri Feb 10 17:10:09 2006
@@ -0,0 +1,122 @@
+<!--
+  Copyright 2006 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.
+  
+-->
+<project name="log4j-concurrent" default="dist" basedir="." >
+	  <!-- The build.properties file defines the path to local jar files -->
+	  <property file="build.properties"/>
+
+	  <property name="version" value="0.1"/>
+      <property name="target.dir" value="target"/>
+	  <property name="classes.dir" value="${target.dir}/classes"/>
+	  <property name="test-classes.dir" value="${target.dir}/test-classes"/>
+
+	  <property name="M2_REPO" value="${user.home}/.m2/repository"/>
+	  <property name="log4j.version" value="1.3alpha8"/>
+	  <property name="log4j.jar" value="${M2_REPO}/log4j/log4j/${log4j.version}/log4j-${log4j.version}.jar"/>
+	  <property name="junit.version" value="3.8.1"/>
+	  <property name="junit.jar" value="${M2_REPO}/junit/junit/${junit.version}/junit-${junit.version}.jar"/>
+	
+	  <target name="clean" description="Remove generated artifacts">
+	  	  <delete dir="${target.dir}"/>
+	  </target>
+
+	  <target name="init">
+	  </target>
+	  	
+	  <target name="compile" depends="init" description="Compile">
+         <mkdir dir="${classes.dir}"/>
+	  	 <javac destdir="${classes.dir}" 
+	  	           srcdir="src/main/java"
+	  			   includes="**/*.java"
+	  	           source="1.3"
+	  	           target="1.3"
+	  		       classpath="${log4j.jar}"/>
+	  </target>
+
+
+
+	  <target name="jar" depends="compile" description="Prepare jar">	    
+	    <delete file="${target.dir}/log4j-concurrent-${version}.jar"/>
+	    <mkdir dir="${target.dir}/"/>
+	    
+	    <jar jarfile="${target.dir}/log4j-concurrent-${version}.jar" 
+	    	 basedir="${classes.dir}">
+	      <manifest>
+	        <attribute name="Manifest-version" value="1.0"/>
+	        <section name="org/apache/log4j/formatter/">
+	          <attribute name="Implementation-Title" value="Concurrent appenders for log4j 1.3"/>
+	          <attribute name="Implementation-Version" value="${version}"/>
+	          <attribute name="Implementation-Vendor" value="Apache Software Foundation"/>
+	        </section>
+	      </manifest>
+	    </jar>
+	  </target>
+
+	  <target name="testCompile" depends="compile" description="Compile tests">
+         <mkdir dir="${test-classes.dir}"/>
+	  	 <javac destdir="${test-classes.dir}" 
+	  	           srcdir="src/test/java"
+	  			   includes="**/*.java"
+	  	           source="1.3"
+	  	           target="1.3"
+	  		       classpath="${classes.dir};${log4j.jar};${junit.jar}"/>
+	  </target>
+
+	  <target name="test" depends="testCompile" description="Run tests">
+	    <junit printsummary="yes" 
+	    	fork="yes">
+	  	  <classpath path="${test-classes.dir};${classes.dir};${log4j.jar};${junit.jar}"/>
+	  	  <formatter type="plain" usefile="false"/>
+	  	  <batchtest>
+	  	    <fileset dir="src/test/java" 
+	  	    	includes="**/*.java"/>
+	  	  </batchtest>
+	    </junit>
+	  </target>
+
+	 <target name="javadoc" description="Prepare Javadocs">	    
+	    <mkdir dir="${target.dir}/javadoc" />	    
+	    <javadoc
+	             destdir="${target.dir}/javadoc"
+	             packagenames="org.apache.log4j.concurrent"
+	             version="true"
+	             author="true"
+	             use="true"
+	             doctitle="log4j concurrent version ${version}&lt;br&gt;API Specification"
+	             windowtitle="Log4j Concurrent Version ${version}"
+	             bottom="Copyright 2006 Apache Software Foundation.">
+	         <sourcepath>
+	            <pathelement path="src/main/java"/>
+	         </sourcepath>
+	        <classpath path="${log4j.jar}"/>
+	    </javadoc>
+	 </target>
+	
+	  <target name="dist" depends="test, jar, javadoc"
+	     description="Prepare distribution">
+	    <tar tarfile="${target.dir}/logging-log4j-concurrent-${version}.tar.gz"
+	         compression="gzip"
+	         longfile="gnu">
+	    	 <tarfileset dir="." 
+	  				prefix="logging-log4j-concurrent-${version}"
+	  	            excludes="target/** .* .settings/**"/>
+	    	 <tarfileset dir="target" 
+	  				prefix="logging-log4j-concurrent-${version}"
+	  	            includes="*.jar javadoc/**"/>
+	    </tar>
+	  </target>
+
+</project>
\ No newline at end of file

Added: logging/sandbox/log4j/concurrent/pom.xml
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/pom.xml?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/pom.xml (added)
+++ logging/sandbox/log4j/concurrent/pom.xml Fri Feb 10 17:10:09 2006
@@ -0,0 +1,111 @@
+<!--
+  Copyright 2006 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.
+  
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.log4j.concurrent</groupId>
+  <artifactId>log4j-concurrent</artifactId>
+  <packaging>jar</packaging>
+  <version>0.1</version>
+  <name>log4j-concurrent</name>
+  <description>Concurrent appenders for log4j</description>
+  <url>http://logging.apache.org/sandbox/concurrent</url>
+  <issueManagement>
+    <system>Bugzilla</system>
+    <url>http://issues.apache.org/bugzilla</url>
+  </issueManagement>
+  <inceptionYear>2006</inceptionYear>
+<mailingLists>
+	<mailingList>
+		<name>log4j-user</name>
+		<subscribe>log4j-user-subscribe@logging.apache.org</subscribe>
+		<unsubscribe>log4j-user-unsubscribe@logging.apache.org</unsubscribe>
+		<post>log4j-user@logging.apache.org</post>
+		<archive>http://mail-archives.apache.org/mod_mbox/logging-log4j-dev/</archive>
+		<otherArchives>
+			<otherArchive>http://marc.theaimsgroup.com/?l=log4j-user&amp;r=1&amp;w=2</otherArchive>
+		    <otherArchive>http://dir.gmane.org/gmane.comp.jakarta.log4j.user</otherArchive>
+		</otherArchives>
+	</mailingList>
+	<mailingList>
+		<name>log4j-dev</name>
+		<subscribe>log4j-dev-subscribe@logging.apache.org</subscribe>
+		<unsubscribe>log4j-dev-unsubscribe@logging.apache.org</unsubscribe>
+		<post>log4j-dev@logging.apache.org</post>
+		<archive>http://mail-archives.apache.org/mod_mbox/logging-log4j-dev/</archive>
+		<otherArchives>
+		    <otherArchive>http://marc.theaimsgroup.com/?l=log4j-dev&amp;r=1&amp;w=2</otherArchive>
+		    <otherArchive>http://dir.gmane.org/gmane.comp.jakarta.log4j.devel</otherArchive>
+		</otherArchives>
+	</mailingList>
+</mailingLists>
+<developers>
+   <developer>
+   	   <id>eross</id>
+   	   <name>Elias Ross</name>
+   	   <email>eross@m-qube.com</email>
+   	</developer>
+   <developer>
+   	   <id>carnold</id>
+   	   <name>Curt Arnold</name>
+   	   <email>carnold@apache.org</email>
+   	</developer>
+</developers>
+<licenses>
+	<license>
+		<name>Apache License, Version 2.0</name>
+		<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+		<distribution>repo</distribution>
+	</license>
+</licenses>
+<scm>
+	<connection>scm:svn:http://svn.apache.org/repos/asf/logging/sandbox/log4j/concurrent</connection>
+	<developerConnection>scm:svn:https://svn.apache.org/repos/asf/logging/sandbox/log4j/concurrent</developerConnection>
+	<url>http://svn.apache.org/viewcvs.cgi/logging/sandbox/log4j/concurrent</url>
+</scm>
+<organization>
+    <name>Apache Software Foundation</name>
+    <url>http://www.apache.org</url>
+</organization>
+<build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.13</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+    </plugins>
+</build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.3alpha8</version>
+    </dependency>
+  </dependencies>
+</project>

Added: logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConcurrentAppender.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConcurrentAppender.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConcurrentAppender.java (added)
+++ logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConcurrentAppender.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,433 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j.concurrent;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.ReaderWriterLock;
+import org.apache.log4j.spi.ComponentBase;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+
+/**
+ * Base class for appenders that can benefit from a concurrency strategy.
+ * Classes derived from this appender may have the {@link #append} method
+ * called by multiple threads.  Derived classes must also override {@link
+ * #internalClose}.
+ * <p>
+ * Locking strategy:  Internally, there is a read-write lock to handle
+ * concurrent modification.  A <i>write</i> lock is obtained to change states
+ * (including before {@link #close}.)  A <i>read</i> lock is obtained to read
+ * options.  Subclasses interested in state may check state using a public
+ * method or within their own {@link #append} method.
+ * </p>
+ * <p>
+ * This class is heavily based on the {@link
+ * #org.apache.log4j.AppenderSkeleton} class.  It may be a useful base class
+ * for creating appenders that can benefit from concurrent I/O access.
+ * </p>
+ *
+ * @see #getWriteLock
+ */
+public abstract class ConcurrentAppender 
+  extends ComponentBase implements Appender, OptionHandler
+{
+  
+  /**
+   * The layout variable does not need to be set if the appender
+   * implementation has its own layout.
+   */
+  private Layout layout;
+
+  /**
+   * The name of this appender.
+   */
+  protected String name;
+
+  /**
+   * There is no level threshold filtering by default.
+   */
+  private volatile Priority threshold;
+
+  /**
+   * Internal class, internally locked.
+   */
+  private FilterChain filters = new FilterChain();
+
+  /**
+   * Is this appender closed?
+   */
+  private SynchronizedBoolean closed = new SynchronizedBoolean(false);
+
+  /**
+   * Set to true when the appender is activated.
+   * Subclasses can set this to false to indicate things are not in order.
+   */
+  protected SynchronizedBoolean active = new SynchronizedBoolean(false);
+  
+  /**
+   * The guard prevents an appender from repeatedly calling its own doAppend
+   * method.  This prevents same-thread re-entry looping.
+   */
+  private ThreadLocal guard = new ThreadLocal();
+
+  /**
+   * A write lock is obtained to change options, a read lock is obtained to
+   * append events.
+   */
+  private ReaderWriterLock lock = new ReaderWriterLock();
+
+
+  /**
+   * Constructs a ConcurrentAppender.
+   *
+   * @param isActive true if appender is ready for use upon construction.
+   */
+  protected ConcurrentAppender(final boolean isActive) {
+    active.set(isActive);
+  }
+
+  /**
+   * Derived appenders should override this method if option structure
+   * requires it.
+   * By default, sets {@link #active} to true.
+   */
+  public void activateOptions() {
+    active.set(true);
+  }
+
+  /**
+   * Indicates if the appender is active and not closed.
+   */
+  public boolean isActive() {
+    return active.get() && !closed.get();
+  }
+
+  /**
+   * Adds a filter to end of the filter list.
+   * @param filter filter to use; cannot be null
+   */
+  public void addFilter(Filter filter) {
+    filters.addFilter(filter);
+  }
+
+  /**
+   * Clears the filters chain.
+   */
+  public void clearFilters() {
+    filters.clear();
+  }
+
+  /**
+   * Returns the first {@link Filter}.
+   */
+  public Filter getFilter() {
+    return filters.getHead();
+  }
+
+  /**
+   * Returns the layout of this appender. May return null if not set.
+   */
+  public Layout getLayout() {
+    return this.layout;
+  }
+
+  /**
+   * Returns the name of this appender.
+   */
+  public final String getName() {
+    return this.name;
+  }
+
+  /**
+   * Returns this appender's threshold level. See the {@link #setThreshold}
+   * method for the meaning of this option.
+   */
+  public Priority getThreshold() {
+    return threshold;
+  }
+
+  /**
+   * Returns true if the message level is below the appender's threshold. If
+   * there is no threshold set, returns <code>true</code>.
+   */
+  public boolean isAsSevereAsThreshold(final Priority level) {
+    Priority copy = threshold;
+    return ((copy == null) || copy.isGreaterOrEqual(level));
+  }
+
+  /**
+   * Performs threshold checks and checks filters before delegating actual
+   * logging to the subclasses specific {@link #append} method.
+   * This implementation also checks if this thread already is logging using
+   * this appender, preventing possible stack overflow.
+   */
+  public final void doAppend(LoggingEvent event) {
+
+    if (!isAsSevereAsThreshold(event.getLevel()))
+      return;
+
+    if (!filters.accept(event))
+      return;
+
+    // Prevent concurrent re-entry by this thread
+    // (There might be a cheaper way to do this)
+    // (Or maybe this lock is not necessary)
+    if (guard.get() != null)
+      return;
+
+    guard.set(this); // arbitrary thread lock object
+    try {
+
+      lock.getReadLock();
+      try {
+
+
+        if (closed.get()) {
+          getNonFloodingLogger().error(
+              "Attempted to use closed appender named [" + name + "].");
+          return;
+        }
+
+        if (!active.get()) {
+          getNonFloodingLogger().error(
+              "Attempted to log with inactive named [" + name + "].");
+          return;
+        }
+
+        append(event);
+
+      } finally {
+        lock.releaseReadLock();
+      }
+
+    } finally {
+      guard.set(null);
+    }
+  }
+
+  /**
+   * Sets the layout for this appender. Note that some appenders have their own
+   * (fixed) layouts or do not use one. For example, the {@link
+   * org.apache.log4j.net.SocketAppender} ignores the layout set here.
+   * <p>
+   * Note that the implementation of {@link Layout} must be thread-safe.
+   * Common layouts such as {@link org.apache.log4j.PatternLayout} are
+   * thread-safe.
+   * </p>
+   *
+   * @param layout new layout to use; may be null
+   */
+  public void setLayout(Layout layout) {
+    this.layout = layout;
+  }
+
+  /**
+   * Sets the name of this Appender.
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Sets the threshold level. 
+   * All log events with lower level than the threshold level are ignored by
+   * the appender.
+   *
+   * @param threshold new threshold; may be null
+   */
+  public void setThreshold(final Priority threshold) {
+    this.threshold = threshold;
+  }
+
+  /**
+   * Returns true if this appender is closed.
+   * An appender, once closed, is closed forever.
+   */
+  public boolean getClosed() {
+    return closed.get();
+  }
+
+  /**
+   * Cleans up this appender.
+   * Marked as <code>final</code> to prevent subclasses from accidentally
+   * overriding and forgetting to call <code>super.close()</code> or obtain a
+   * write lock.
+   * Calls {@link #internalClose} when completed.
+   * Implementation note:  Obtains a write lock before starting close.
+   * Calling this method more than once does nothing.
+   */
+  public final void close() {
+    boolean wasClosed;
+    getWriteLock();
+    try {
+      wasClosed = closed.set(true);
+    } finally {
+      lock.releaseWriteLock();
+    }
+
+    if (!wasClosed)
+      internalClose();
+  }
+
+  /**
+   * Called to check if the appender is closed.
+   */
+  public boolean isClosed() {
+    return closed.get();
+  }
+  
+  private static final org.apache.log4j.spi.ErrorHandler ERROR_HANDLER = 
+      new org.apache.log4j.helpers.OnlyOnceErrorHandler();
+
+  public final org.apache.log4j.spi.ErrorHandler getErrorHandler() {
+    return ERROR_HANDLER;
+  }
+
+  public final void setErrorHandler(org.apache.log4j.spi.ErrorHandler eh) {
+  }
+
+  /**
+   * Returns a string representation of this object.
+   */
+  public String toString() {
+    return super.toString() + " name=" + name + 
+      " threshold=" + threshold + 
+      " layout=" + layout +
+      " filters=" + filters;
+  }
+
+  // PROTECTED METHODS
+
+  /**
+   * Subclasses of <code>ConcurrentAppender</code> should implement this method
+   * to perform actual logging. 
+   * This object holds a read lock during this method.  This method may be
+   * called simultaneously by multiple threads.
+   */
+  protected abstract void append(LoggingEvent event);
+
+  /**
+   * Subclasses must implement their own close routines.
+   * This method is called by the {@link #close} method.
+   * This is guaranteed to be called only once, even if {@link #close} is
+   * invoked more than once.
+   * Note that further locking is not required, as {@link #append} can no
+   * longer be called.
+   */
+  protected abstract void internalClose();
+
+  /**
+   * Obtains a write lock that blocks logging to {@link #append}.
+   * This is normally done when changing output behavior, closing and reopening
+   * streams, etc.  Call {@link #releaseWriteLock} to resume logging.
+   * <p>
+   * Usage pattern:
+   <pre>
+   getWriteLock();
+   try {
+      // ...
+   } finally {
+      releaseWriteLock();
+   }
+   </pre>
+   * Note:  Do not attempt to re-acquire this lock.  This lock should only be
+   * used for critical sections and not for long periods of time.
+   */
+  protected void getWriteLock() {
+    lock.getWriteLock();
+  }
+
+  /**
+   * Releases a write lock; allows calls to the {@link #append} method.
+   * @see #getWriteLock
+   */
+  protected void releaseWriteLock() {
+    lock.releaseWriteLock();
+  }
+
+  /**
+   * Finalizes this appender by calling this {@link #close} method.
+   */
+  protected void finalize() {
+    if (!getClosed())
+      getLogger().debug("Finalizing appender named [{}].", name);
+    close();
+  }
+
+  /**
+   * A simple linked-list data structure containing filters.
+   */
+  private static class FilterChain {
+
+    private Filter headFilter = null;
+    private Filter tailFilter = null;
+   
+    public synchronized boolean accept(LoggingEvent event) {
+      Filter f = headFilter;
+      while (f != null) {
+        switch (f.decide(event)) {
+          case Filter.DENY:
+            return false;
+          case Filter.ACCEPT:
+            return true;
+          case Filter.NEUTRAL:
+            f = f.getNext();
+        }
+      }
+      return true;
+    }
+
+    public synchronized void addFilter(Filter newFilter) {
+      if (newFilter == null)
+        throw new NullPointerException();
+      if (headFilter == null) {
+        headFilter = newFilter;
+        tailFilter = newFilter;
+      } else {
+        tailFilter.setNext(newFilter);
+        tailFilter = newFilter;
+      }
+    }
+
+    public synchronized Filter getHead() {
+      return headFilter;
+    }
+
+    public synchronized void clear() {
+      headFilter = null;
+      tailFilter = null;
+    }
+
+    public synchronized String toString() {
+      StringBuffer sb = new StringBuffer();
+      Filter f = headFilter;
+      while (f != null) {
+        sb.append(f);
+        f = f.getNext();
+        if (f != null)
+          sb.append(',');
+      }
+      return f.toString();
+    }
+
+  }
+
+}
+

Added: logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConsoleAppender.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConsoleAppender.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConsoleAppender.java (added)
+++ logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/ConsoleAppender.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+package org.apache.log4j.concurrent;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.log4j.Layout;
+
+
+/**
+  * ConsoleAppender appends log events to <code>System.out</code> or
+  * <code>System.err</code> using a layout specified by the user. The
+  * default target is <code>System.out</code>.
+  *
+  * @author <a href="http://www.qos.ch/log4j/">Ceki G&uuml;lc&uuml;</a>
+  * @author Curt Arnold
+  * @since 1.1 */
+public class ConsoleAppender extends WriterAppender {
+    public static final String SYSTEM_OUT = "System.out";
+    public static final String SYSTEM_ERR = "System.err";
+    protected String target = SYSTEM_OUT;
+
+    /**
+     *  Determines if the appender honors reassignments of System.out
+     *  or System.err made after configuration.
+     */
+    private boolean follow = false;
+
+
+    /**
+     * Constructs an unconfigured appender.
+     */
+    public ConsoleAppender() {
+    }
+
+    /**
+     * Creates a configured appender.
+     *
+     * @param layout layout, may not be null.
+     */
+    public ConsoleAppender(final Layout layout) {
+        setLayout(layout);
+        activateOptions();
+    }
+
+    /**
+     *   Creates a configured appender.
+     * @param layout layout, may not be null.
+     * @param targetStr target, either "System.err" or "System.out".
+     */
+    public ConsoleAppender(final Layout layout, final String targetStr) {
+        setLayout(layout);
+        setTarget(targetStr);
+        activateOptions();
+    }
+
+    /**
+     *  Sets the value of the <b>Target</b> option. Recognized values
+     *  are "System.out" and "System.err". Any other value will be
+     *  ignored.
+     * */
+    public void setTarget(final String value) {
+        String v = value.trim();
+
+        if (SYSTEM_OUT.equalsIgnoreCase(v)) {
+            target = SYSTEM_OUT;
+        } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
+            target = SYSTEM_ERR;
+        } else {
+            getLogger().warn("[{}] should be System.out or System.err.", value);
+            getLogger().warn("Using previously set target, System.out by default.");
+        }
+    }
+
+    /**
+     * Returns the current value of the <b>Target</b> property. The
+     * default value of the option is "System.out".
+     *
+     * See also {@link #setTarget}.
+     * */
+    public String getTarget() {
+        return target;
+    }
+
+   /**
+    *  Sets whether the appender honors reassignments of System.out
+    *  or System.err made after configuration.
+    *  @param newValue if true, appender will use value of System.out or
+    *  System.err in force at the time when logging events are appended.
+    *  @since 1.2.13
+    */
+    public final void setFollow(final boolean newValue) {
+       follow = newValue;
+    }
+  
+   /**
+    *  Gets whether the appender honors reassignments of System.out
+    *  or System.err made after configuration.
+    *  @return true if appender will use value of System.out or
+    *  System.err in force at the time when logging events are appended.
+    *  @since 1.2.13
+    */
+    public final boolean getFollow() {
+         return follow;
+    }
+
+
+    /**
+     *   Prepares the appender for use.
+     */
+    public void activateOptions() {
+        if (follow) {
+            if (target.equals(SYSTEM_ERR)) {
+               setWriter(createWriter(new SystemErrStream()));
+            } else {
+               setWriter(createWriter(new SystemOutStream()));
+            }
+        } else {
+            if (target.equals(SYSTEM_ERR)) {
+               setWriter(createWriter(System.err));
+            } else {
+               setWriter(createWriter(System.out));
+            }
+        }
+
+        super.activateOptions();
+    }
+
+  /**
+   *  {@inheritDoc}
+   */
+  protected
+  final
+  void closeWriter() {
+     if (follow) {
+        super.closeWriter();
+     }
+  }
+
+
+    /**
+     * An implementation of OutputStream that redirects to the
+     * current System.err.
+     *
+     */
+    private static class SystemErrStream extends OutputStream {
+        public SystemErrStream() {
+        }
+
+        public void close() {
+        }
+
+        public void flush() {
+            System.err.flush();
+        }
+
+        public void write(final byte[] b) throws IOException {
+            System.err.write(b);
+        }
+
+        public void write(final byte[] b, final int off, final int len)
+            throws IOException {
+            System.err.write(b, off, len);
+        }
+
+        public void write(final int b) throws IOException {
+            System.err.write(b);
+        }
+    }
+
+    /**
+     * An implementation of OutputStream that redirects to the
+     * current System.out.
+     *
+     */
+    private static class SystemOutStream extends OutputStream {
+        public SystemOutStream() {
+        }
+
+        public void close() {
+        }
+
+        public void flush() {
+            System.out.flush();
+        }
+
+        public void write(final byte[] b) throws IOException {
+            System.out.write(b);
+        }
+
+        public void write(final byte[] b, final int off, final int len)
+            throws IOException {
+            System.out.write(b, off, len);
+        }
+
+        public void write(final int b) throws IOException {
+            System.out.write(b);
+        }
+    }
+}
+
+

Added: logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/FileAppender.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/FileAppender.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/FileAppender.java (added)
+++ logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/FileAppender.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j.concurrent;
+
+import java.io.Writer;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.OptionConverter;
+
+
+// Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
+//              Ben Sandee
+
+/**
+ *  FileAppender appends log events to a file.
+ *
+ *  <p>Support for <code>java.io.Writer</code> and console appending
+ *  has been deprecated and then removed. See the replacement
+ *  solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * */
+public class FileAppender extends WriterAppender {
+
+  public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
+  /** 
+   * Controls whether to append to or truncate an existing file. 
+   * The default value for this variable is 
+   * <code>true</code>, meaning that by default a <code>FileAppender</code> will
+   *  append to an existing file and not truncate it. 
+   * 
+   * <p>This option is meaningful only if the FileAppender opens the file.
+  */
+  protected boolean fileAppend = true;
+
+  /**
+     The name of the log file. */
+  protected String fileName = null;
+
+  /**
+     Do we do bufferedIO? */
+  protected boolean bufferedIO = true;
+
+  /**
+     The size of the IO buffer. Default is 8K. */
+  protected int bufferSize = DEFAULT_BUFFER_SIZE;
+
+  /**
+     The default constructor does not do anything.
+  */
+  public FileAppender() {
+  }
+
+  /**
+    Instantiate a <code>FileAppender</code> and open the file
+    designated by <code>filename</code>. The opened filename will
+    become the output destination for this appender.
+
+    <p>If the <code>append</code> parameter is true, the file will be
+    appended to. Otherwise, the file designated by
+    <code>filename</code> will be truncated before being opened.
+
+    <p>If the <code>bufferedIO</code> parameter is <code>true</code>,
+    then buffered IO will be used to write to the output file.
+
+  */
+  public FileAppender(
+    Layout layout, String filename, boolean append, boolean bufferedIO,
+    int bufferSize) throws IOException {
+    setLayout(layout);
+    this.setFile(filename, append, bufferedIO, bufferSize);
+    activateOptions();
+  }
+
+  /**
+    Instantiate a FileAppender and open the file designated by
+    <code>filename</code>. The opened filename will become the output
+    destination for this appender.
+
+    <p>If the <code>append</code> parameter is true, the file will be
+    appended to. Otherwise, the file designated by
+    <code>filename</code> will be truncated before being opened.
+  */
+  public FileAppender(Layout layout, String filename, boolean append)
+    throws IOException {
+    this(layout, filename, append, false, DEFAULT_BUFFER_SIZE);
+  }
+
+  /**
+     Instantiate a FileAppender and open the file designated by
+    <code>filename</code>. The opened filename will become the output
+    destination for this appender.
+
+    <p>The file will be appended to.  */
+  public FileAppender(Layout layout, String filename) throws IOException {
+    this(layout, filename, true);
+    activateOptions();
+  }
+
+  /**
+     The <b>File</b> property takes a string value which should be the
+     name of the file to append to.
+
+     <p><font color="#DD0044"><b>Note that the special values
+     "System.out" or "System.err" are no longer honored.</b></font>
+
+     <p>Note: Actual opening of the file is made when {@link
+     #activateOptions} is called, not when the options are set.  */
+  public void setFile(String file) {
+    // Trim spaces from both ends. The users probably does not want
+    // trailing spaces in file names.
+    String val = file.trim();
+    fileName = OptionConverter.stripDuplicateBackslashes(val);
+  }
+
+  /**
+      Returns the value of the <b>Append</b> option.
+   */
+  public boolean getAppend() {
+    return fileAppend;
+  }
+
+  /** Returns the value of the <b>File</b> option. */
+  public String getFile() {
+    return fileName;
+  }
+
+  /**
+     If the value of <b>File</b> is not <code>null</code>, then {@link
+     #setFile} is called with the values of <b>File</b>  and
+     <b>Append</b> properties.
+
+     @since 0.8.1 */
+  public void activateOptions() {
+    if (fileName != null) {
+      try {
+        setFile(fileName, fileAppend, bufferedIO, bufferSize);
+        super.activateOptions();
+      } catch (java.io.IOException e) {
+        getLogger().error(
+          "setFile(" + fileName + "," + fileAppend + ") call failed.", e);
+      }
+    } else {
+      getLogger().error("File option not set for appender [{}].", name);
+      getLogger().warn("Are you using FileAppender instead of ConsoleAppender?");
+    }
+  }
+
+  /**
+   * Closes the previously opened file.
+   * 
+   * @deprecated Use the super class' {@link #closeWriter} method instead.
+   */
+  protected void closeFile() {
+    closeWriter();
+  }
+
+  /**
+     Get the value of the <b>BufferedIO</b> option.
+
+     <p>BufferedIO will significantly increase performance on heavily
+     loaded systems.
+
+  */
+  public boolean getBufferedIO() {
+    return this.bufferedIO;
+  }
+
+  /**
+     Get the size of the IO buffer.
+  */
+  public int getBufferSize() {
+    return this.bufferSize;
+  }
+
+  /**
+     The <b>Append</b> option takes a boolean value. It is set to
+     <code>true</code> by default. If true, then <code>File</code>
+     will be opened in append mode by {@link #setFile setFile} (see
+     above). Otherwise, {@link #setFile setFile} will open
+     <code>File</code> in truncate mode.
+
+     <p>Note: Actual opening of the file is made when {@link
+     #activateOptions} is called, not when the options are set.
+   */
+  public void setAppend(boolean flag) {
+    fileAppend = flag;
+  }
+
+  /**
+     The <b>BufferedIO</b> option takes a boolean value. It is set to
+     <code>false</code> by default. If true, then <code>File</code>
+     will be opened and the resulting {@link java.io.Writer} wrapped
+     around a {@link BufferedWriter}.
+
+     BufferedIO will significantly increase performance on heavily
+     loaded systems.
+
+  */
+  public void setBufferedIO(boolean bufferedIO) {
+    this.bufferedIO = bufferedIO;
+  }
+
+  /**
+     Set the size of the IO buffer.
+  */
+  public void setBufferSize(int bufferSize) {
+    this.bufferSize = bufferSize;
+  }
+
+  /**
+    <p>Sets and <i>opens</i> the file where the log output will
+    go. The specified file must be writable.
+
+    <p>If there was already an opened file, then the previous file
+    is closed first.
+
+    <p><b>Do not use this method directly. To configure a FileAppender
+    or one of its subclasses, set its properties one by one and then
+    call activateOptions.</b>
+
+    @param filename The path to the log file.
+    @param append   If true will append to fileName. Otherwise will
+        truncate fileName.
+    @param bufferedIO
+    @param bufferSize
+    
+    @throws IOException
+        
+   */
+  public void setFile(
+    String filename, boolean append, boolean bufferedIO, int bufferSize)
+    throws IOException {
+    getLogger().debug("setFile called: {}, {}", fileName, append?"true":"false");
+
+    FileOutputStream ostream;
+    try {
+        //
+        //   attempt to create file
+        //
+        ostream = new FileOutputStream(filename, append);
+    } catch(FileNotFoundException ex) {
+        //
+        //   if parent directory does not exist then
+        //      attempt to create it and try to create file
+        //      see bug 9150
+        //
+        File parentDir = new File(new File(filename).getParent());
+        if(!parentDir.exists() && parentDir.mkdirs()) {
+            ostream = new FileOutputStream(filename, append);
+        } else {
+            throw ex;
+        }
+    }
+    Writer writer = createWriter(ostream);
+
+    if (bufferedIO) {
+      writer = new BufferedWriter(writer, bufferSize);
+    }
+
+    this.fileAppend = append;
+    this.bufferedIO = bufferedIO;
+    this.fileName = filename;
+    this.bufferSize = bufferSize;
+
+    setWriter(writer);
+    getLogger().debug("setFile ended");
+  }
+
+}
+

Added: logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/PatternLayout.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/PatternLayout.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/PatternLayout.java (added)
+++ logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/PatternLayout.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,579 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j.concurrent;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.pattern.FormattingInfo;
+import org.apache.log4j.pattern.LiteralPatternConverter;
+import org.apache.log4j.pattern.LoggingEventPatternConverter;
+import org.apache.log4j.pattern.PatternParser;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggerRepositoryEx;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+// Contributors:   Nelson Minar <nelson@monkey.org>
+//                 Anders Kristensen <akristensen@dynamicsoft.com>
+
+/**
+  * <p>A flexible layout configurable with pattern string. The goal of this class
+  * is to {@link #format format} a {@link LoggingEvent} and return the results
+  * in a {#link StringBuffer}. The format of the result depends on the
+  * <em>conversion pattern</em>.
+  * <p>
+  *
+  * <p>The conversion pattern is closely related to the conversion
+  * pattern of the printf function in C. A conversion pattern is
+  * composed of literal text and format control expressions called
+  * <em>conversion specifiers</em>.
+  *
+  * <p><i>Note that you are free to insert any literal text within the
+  * conversion pattern.</i>
+  * </p>
+
+   <p>Each conversion specifier starts with a percent sign (%) and is
+   followed by optional <em>format modifiers</em> and a <em>conversion
+   character</em>. The conversion character specifies the type of
+   data, e.g. category, priority, date, thread name. The format
+   modifiers control such things as field width, padding, left and
+   right justification. The following is a simple example.
+
+   <p>Let the conversion pattern be <b>"%-5p [%t]: %m%n"</b> and assume
+   that the log4j environment was set to use a PatternLayout. Then the
+   statements
+   <pre>
+   Category root = Category.getRoot();
+   root.debug("Message 1");
+   root.warn("Message 2");
+   </pre>
+   would yield the output
+   <pre>
+   DEBUG [main]: Message 1
+   WARN  [main]: Message 2
+   </pre>
+
+   <p>Note that there is no explicit separator between text and
+   conversion specifiers. The pattern parser knows when it has reached
+   the end of a conversion specifier when it reads a conversion
+   character. In the example above the conversion specifier
+   <b>%-5p</b> means the priority of the logging event should be left
+   justified to a width of five characters.
+
+   The recognized conversion characters are
+
+   <p>
+   <table border="1" CELLPADDING="8">
+   <th>Conversion Character</th>
+   <th>Effect</th>
+
+   <tr>
+     <td align=center><b>c</b></td>
+
+     <td>Used to output the category of the logging event. The
+     category conversion specifier can be optionally followed by
+     <em>precision specifier</em>, that is a decimal constant in
+     brackets.
+
+     <p>If a precision specifier is given, then only the corresponding
+     number of right most components of the category name will be
+     printed. By default the category name is printed in full.
+
+     <p>For example, for the category name "a.b.c" the pattern
+     <b>%c{2}</b> will output "b.c".
+
+     </td>
+   </tr>
+
+   <tr>
+     <td align=center><b>C</b></td>
+
+     <td>Used to output the fully qualified class name of the caller
+     issuing the logging request. This conversion specifier
+     can be optionally followed by <em>precision specifier</em>, that
+     is a decimal constant in brackets.
+
+     <p>If a precision specifier is given, then only the corresponding
+     number of right most components of the class name will be
+     printed. By default the class name is output in fully qualified form.
+
+     <p>For example, for the class name "org.apache.xyz.SomeClass", the
+     pattern <b>%C{1}</b> will output "SomeClass".
+
+     <p><b>WARNING</b> Generating the caller class information is
+     slow. Thus, it's use should be avoided unless execution speed is
+     not an issue.
+
+     </td>
+     </tr>
+
+   <tr> <td align=center><b>d</b></td> <td>Used to output the date of
+         the logging event. The date conversion specifier may be
+         followed by a set of braces containing a
+         date and time pattern strings {@link java.text.SimpleDateFormat},
+         <em>ABSOLUTE</em>, <em>DATE</em> or <em>ISO8601</em>.
+         For example, <b>%d{HH:mm:ss,SSS}</b>,
+         <b>%d{dd&nbsp;MMM&nbsp;yyyy&nbsp;HH:mm:ss,SSS}</b> or
+         <b>%d{DATE}</b>.  If no date format specifier is given then
+         ISO8601 format is assumed.
+     </td>
+   </tr>
+
+   <tr>
+   <td align=center><b>F</b></td>
+
+   <td>Used to output the file name where the logging request was
+   issued.
+
+   <p><b>WARNING</b> Generating caller location information is
+   extremely slow. Its use should be avoided unless execution speed
+   is not an issue.
+
+   </tr>
+
+   <tr>
+   <td align=center><b>l</b></td>
+
+     <td>Used to output location information of the caller which generated
+     the logging event.
+
+     <p>The location information depends on the JVM implementation but
+     usually consists of the fully qualified name of the calling
+     method followed by the callers source the file name and line
+     number between parentheses.
+
+     <p>The location information can be very useful. However, it's
+     generation is <em>extremely</em> slow. It's use should be avoided
+     unless execution speed is not an issue.
+
+     </td>
+   </tr>
+
+   <tr>
+   <td align=center><b>L</b></td>
+
+   <td>Used to output the line number from where the logging request
+   was issued.
+
+   <p><b>WARNING</b> Generating caller location information is
+   extremely slow. It's use should be avoided unless execution speed
+   is not an issue.
+
+   </tr>
+
+
+   <tr>
+     <td align=center><b>m</b></td>
+     <td>Used to output the application supplied message associated with
+     the logging event.</td>
+   </tr>
+
+   <tr>
+   <td align=center><b>M</b></td>
+
+   <td>Used to output the method name where the logging request was
+   issued.
+
+   <p><b>WARNING</b> Generating caller location information is
+   extremely slow. It's use should be avoided unless execution speed
+   is not an issue.
+
+   </tr>
+
+   <tr>
+     <td align=center><b>n</b></td>
+
+     <td>Outputs the platform dependent line separator character or
+     characters.
+
+     <p>This conversion character offers practically the same
+     performance as using non-portable line separator strings such as
+     "\n", or "\r\n". Thus, it is the preferred way of specifying a
+     line separator.
+
+
+   </tr>
+
+   <tr>
+     <td align=center><b>p</b></td>
+     <td>Used to output the priority of the logging event.</td>
+   </tr>
+
+   <tr>
+
+     <td align=center><b>r</b></td>
+
+     <td>Used to output the number of milliseconds elapsed since the start
+     of the application until the creation of the logging event.</td>
+   </tr>
+
+
+   <tr>
+     <td align=center><b>t</b></td>
+
+     <td>Used to output the name of the thread that generated the
+     logging event.</td>
+
+   </tr>
+
+   <tr>
+
+     <td align=center><b>x</b></td>
+
+     <td>Used to output the NDC (nested diagnostic context) associated
+     with the thread that generated the logging event.
+     </td>
+   </tr>
+
+
+   <tr>
+     <td align=center><b>X</b></td>
+
+     <td>
+
+     <p>Used to output the MDC (mapped diagnostic context) associated
+     with the thread that generated the logging event. The <b>X</b>
+     conversion character can be followed by the key for the
+     map placed between braces, as in <b>%X{clientNumber}</b> where
+     <code>clientNumber</code> is the key. The value in the MDC
+     corresponding to the key will be output. If no additional sub-option
+     is specified, then the entire contents of the MDC key value pair set
+     is output using a format {{key1,val1},{key2,val2}}</p>
+
+     <p>See {@link MDC} class for more details.
+     </p>
+
+     </td>
+   </tr>
+
+      <tr>
+     <td align=center><b>properties</b></td>
+
+     <td>
+     <p>Used to output the Properties associated
+     with the logging event. The <b>properties</b>
+     conversion word can be followed by the key for the
+     map placed between braces, as in <b>%properties{application}</b> where
+     <code>application</code> is the key. The value in the Properties bundle
+     corresponding to the key will be output. If no additional sub-option
+     is specified, then the entire contents of the Properties key value pair set
+     is output using a format {{key1,val1},{key2,val2}}</p>
+     </td>
+   </tr>
+
+            <tr>
+     <td align=center><b>throwable</b></td>
+
+     <td>
+     <p>Used to output the Throwable trace that has been bound to the LoggingEvent, by
+     default this will output the full trace as one would normally find by a call to Throwable.printStackTrace().
+     The throwable conversion word can be followed by an option in the form <b>%throwable{short}</b>
+     which will only output the first line of the ThrowableInformation.</p>
+     </td>
+   </tr>
+
+   <tr>
+
+     <td align=center><b>%</b></td>
+
+     <td>The sequence %% outputs a single percent sign.
+     </td>
+   </tr>
+
+   </table>
+
+   <p>By default the relevant information is output as is. However,
+   with the aid of format modifiers it is possible to change the
+   minimum field width, the maximum field width and justification.
+
+   <p>The optional format modifier is placed between the percent sign
+   and the conversion character.
+
+   <p>The first optional format modifier is the <em>left justification
+   flag</em> which is just the minus (-) character. Then comes the
+   optional <em>minimum field width</em> modifier. This is a decimal
+   constant that represents the minimum number of characters to
+   output. If the data item requires fewer characters, it is padded on
+   either the left or the right until the minimum width is
+   reached. The default is to pad on the left (right justify) but you
+   can specify right padding with the left justification flag. The
+   padding character is space. If the data item is larger than the
+   minimum field width, the field is expanded to accommodate the
+   data. The value is never truncated.
+
+   <p>This behavior can be changed using the <em>maximum field
+   width</em> modifier which is designated by a period followed by a
+   decimal constant. If the data item is longer than the maximum
+   field, then the extra characters are removed from the
+   <em>beginning</em> of the data item and not from the end. For
+   example, it the maximum field width is eight and the data item is
+   ten characters long, then the first two characters of the data item
+   are dropped. This behavior deviates from the printf function in C
+   where truncation is done from the end.
+
+   <p>Below are various format modifier examples for the category
+   conversion specifier.
+
+   <p>
+   <TABLE BORDER=1 CELLPADDING=8>
+   <th>Format modifier
+   <th>left justify
+   <th>minimum width
+   <th>maximum width
+   <th>comment
+
+   <tr>
+   <td align=center>%20c</td>
+   <td align=center>false</td>
+   <td align=center>20</td>
+   <td align=center>none</td>
+
+   <td>Left pad with spaces if the category name is less than 20
+   characters long.
+
+   <tr> <td align=center>%-20c</td> <td align=center>true</td> <td
+   align=center>20</td> <td align=center>none</td> <td>Right pad with
+   spaces if the category name is less than 20 characters long.
+
+   <tr>
+   <td align=center>%.30c</td>
+   <td align=center>NA</td>
+   <td align=center>none</td>
+   <td align=center>30</td>
+
+   <td>Truncate from the beginning if the category name is longer than 30
+   characters.
+
+   <tr>
+   <td align=center>%20.30c</td>
+   <td align=center>false</td>
+   <td align=center>20</td>
+   <td align=center>30</td>
+
+   <td>Left pad with spaces if the category name is shorter than 20
+   characters. However, if category name is longer than 30 characters,
+   then truncate from the beginning.
+
+   <tr>
+   <td align=center>%-20.30c</td>
+   <td align=center>true</td>
+   <td align=center>20</td>
+   <td align=center>30</td>
+
+   <td>Right pad with spaces if the category name is shorter than 20
+   characters. However, if category name is longer than 30 characters,
+   then truncate from the beginning.
+
+   </table>
+
+   <p>Below are some examples of conversion patterns.
+
+   <dl>
+
+   <p><dt><b>%r [%t] %-5p %c %x - %m\n</b>
+   <p><dd>This is essentially the TTCC layout.
+
+   <p><dt><b>%-6r [%15.15t] %-5p %30.30c %x - %m\n</b>
+
+   <p><dd>Similar to the TTCC layout except that the relative time is
+   right padded if less than 6 digits, thread name is right padded if
+   less than 15 characters and truncated if longer and the category
+   name is left padded if shorter than 30 characters and truncated if
+   longer.
+
+  </dl>
+
+   <p>The above text is largely inspired from Peter A. Darnell and
+   Philip E. Margolis' highly recommended book "C -- a Software
+   Engineering Approach", ISBN 0-387-97389-3.
+
+ 
+   <p>This class is thread safe;  it may be called by multiple threads
+   simultaneously to format messages.
+
+
+   @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+   @author Ceki G&uuml;lc&uuml;
+
+
+   @since 0.8.2 */
+public class PatternLayout extends Layout {
+  /** Default pattern string for log output. Currently set to the
+      string <b>"%m%n"</b> which just prints the application supplied
+      message. */
+  public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
+
+  /** A conversion pattern equivalent to the TTCCCLayout.
+      Current value is <b>%r [%t] %p %c %x - %m%n</b>. */
+  public static final String TTCC_CONVERSION_PATTERN =
+    "%r [%t] %p %c %x - %m%n";
+
+    /**
+     * Initial size of internal buffer, no longer used.
+     * @deprecated since 1.3
+     */
+  protected final int BUF_SIZE = 256;
+
+    /**
+     * Maximum capacity of internal buffer, no longer used.
+     * @deprecated since 1.3
+     */
+  protected final int MAX_CAPACITY = 1024;
+
+  /**
+   * Customized pattern conversion rules are stored under this key in the
+   * {@link org.apache.log4j.spi.LoggerRepository LoggerRepository} object store.
+   */
+  public static final String PATTERN_RULE_REGISTRY = "PATTERN_RULE_REGISTRY";
+
+  /**
+   * Conversion pattern.
+   */
+  private String conversionPattern;
+
+  /**
+   * Pattern converters.
+   */
+  private LoggingEventPatternConverter[] patternConverters;
+
+  /**
+   * Field widths and alignment corresponding to pattern converters.
+   */
+  private FormattingInfo[] patternFields;
+
+
+  /**
+   * True if any element in pattern formats information from exceptions.
+   */
+  private boolean handlesExceptions;
+
+  /**
+     Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
+
+     The default pattern just produces the application supplied message.
+  */
+  public PatternLayout() {
+    this(DEFAULT_CONVERSION_PATTERN);
+  }
+
+  /**
+    * Constructs a PatternLayout using the supplied conversion pattern.
+   * @param pattern conversion pattern.
+  */
+  public PatternLayout(String pattern) {
+    this.conversionPattern = pattern;
+    activateOptions();
+  }
+
+  /**
+   * Set the <b>ConversionPattern</b> option. This is the string which
+   * controls formatting and consists of a mix of literal content and
+   * conversion specifiers.
+   *
+   * @param conversionPattern conversion pattern.
+  */
+  public void setConversionPattern(String conversionPattern) {
+    this.conversionPattern =
+      OptionConverter.convertSpecialChars(conversionPattern);
+  }
+
+  /**
+   *  Returns the value of the <b>ConversionPattern</b> option.
+   * @return conversion pattern.
+   */
+  public String getConversionPattern() {
+    return conversionPattern;
+  }
+
+  /**
+    Activates the conversion pattern. Do not forget to call this method after
+    you change the parameters of the PatternLayout instance.
+  */
+  public void activateOptions() {
+    List converters = new ArrayList();
+    List fields = new ArrayList();
+    Map converterRegistry = null;
+
+    if (this.repository != null) {
+      if (repository instanceof LoggerRepositoryEx) {
+        converterRegistry =
+            (Map) ((LoggerRepositoryEx) repository).getObject(PATTERN_RULE_REGISTRY);
+      }
+    }
+
+    PatternParser.parse(
+      conversionPattern, converters, fields, converterRegistry,
+      PatternParser.getPatternLayoutRules(), getLogger());
+
+    patternConverters = new LoggingEventPatternConverter[converters.size()];
+    patternFields = new FormattingInfo[converters.size()];
+
+    int i = 0;
+    Iterator converterIter = converters.iterator();
+    Iterator fieldIter = fields.iterator();
+
+    while (converterIter.hasNext()) {
+      Object converter = converterIter.next();
+
+      if (converter instanceof LoggingEventPatternConverter) {
+        patternConverters[i] = (LoggingEventPatternConverter) converter;
+        handlesExceptions |= patternConverters[i].handlesThrowable();
+      } else {
+        patternConverters[i] = new LiteralPatternConverter("");
+      }
+
+      if (fieldIter.hasNext()) {
+        patternFields[i] = (FormattingInfo) fieldIter.next();
+      } else {
+        patternFields[i] = FormattingInfo.getDefault();
+      }
+
+      i++;
+    }
+  }
+
+  /**
+   *  Formats a logging event to a writer.
+   * @param event logging event to be formatted.
+  */
+  public String format(final LoggingEvent event) {
+    StringBuffer buf = new StringBuffer();
+
+    for (int i = 0; i < patternConverters.length; i++) {
+      int startField = buf.length();
+      patternConverters[i].format(event, buf);
+      patternFields[i].format(startField, buf);
+    }
+
+    return buf.toString();
+  }
+
+  /**
+   * Will return false if any of the conversion specifiers in the pattern
+   * handles {@link Exception Exceptions}.
+   * @return true if the pattern formats any information from exceptions.
+   */
+  public boolean ignoresThrowable() {
+    return !handlesExceptions;
+  }
+}

Added: logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/SynchronizedBoolean.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/SynchronizedBoolean.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/SynchronizedBoolean.java (added)
+++ logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/SynchronizedBoolean.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,55 @@
+
+/*
+  File: SynchronizedBoolean.java
+
+  Originally written by Doug Lea and released into the public domain.
+  This may be used for any purposes whatsoever without acknowledgment.
+  Thanks for the assistance and support of Sun Microsystems Labs,
+  and everyone contributing, testing, and using this code.
+
+  History:
+  Date       Who                What
+  19Jun1998  dl               Create public version
+*/
+
+package org.apache.log4j.concurrent;
+
+/**
+ * A class useful for offloading synch for boolean instance variables.
+ * A cut down version of the original Doug Lea class.
+ */
+public final class SynchronizedBoolean {
+
+  private boolean value;
+
+  /** 
+   * Make a new SynchronizedBoolean with the given initial value,
+   * and using its own internal lock.
+   **/
+  public SynchronizedBoolean(boolean initialValue) { 
+    value = initialValue; 
+  }
+
+  /** 
+   * Return the current value 
+   **/
+  public synchronized boolean get() { return value; }
+
+  /** 
+   * Set to newValue.
+   * @return the old value 
+   **/
+  public synchronized boolean set(boolean newValue) { 
+    boolean old = value;
+    value = newValue; 
+    return old;
+  }
+
+  /**
+   * Returns <code>String.valueOf(get()))</code>.
+   */
+  public String toString() { return String.valueOf(get()); }
+
+}
+
+

Added: logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/WriterAppender.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/WriterAppender.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/WriterAppender.java (added)
+++ logging/sandbox/log4j/concurrent/src/main/java/org/apache/log4j/concurrent/WriterAppender.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+package org.apache.log4j.concurrent;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import org.apache.log4j.Layout;
+
+// Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
+//              Ben Sandee
+
+/**
+   WriterAppender appends log events to a {@link java.io.Writer} or an
+   {@link java.io.OutputStream} depending on the user's choice.
+
+   @author Ceki G&uuml;lc&uuml;
+   @since 1.1 */
+public class WriterAppender extends ConcurrentAppender {
+  
+  /**
+     Immediate flush means that the underlying writer or output stream
+     will be flushed at the end of each append operation. Immediate
+     flush is slower but ensures that each append request is actually
+     written. If <code>immediateFlush</code> is set to
+     <code>false</code>, then there is a good chance that the last few
+     logs events are not actually written to persistent media if and
+     when the application crashes.
+
+     <p>The <code>immediateFlush</code> variable is set to
+     <code>true</code> by default.
+
+  */
+  protected boolean immediateFlush = true;
+
+  /**
+     The encoding to use when opening an InputStream.  <p>The
+     <code>encoding</code> variable is set to <code>null</null> by
+     default which results in the utilization of the system's default
+     encoding.  */
+  protected String encoding;
+
+  /**
+   * This is the {@link Writer Writer} where we will write to.
+   * Do not directly use this object without obtaining a write lock.
+   */
+  protected Writer writer;
+
+  /**
+   * The default constructor does nothing.  
+   * */
+  public WriterAppender() {
+      super(false);
+  }
+
+  /**
+     If the <b>ImmediateFlush</b> option is set to
+     <code>true</code>, the appender will flush at the end of each
+     write. This is the default behavior. If the option is set to
+     <code>false</code>, then the underlying stream can defer writing
+     to physical medium to a later time.
+
+     <p>Avoiding the flush operation at the end of each append results in
+     a performance gain of 10 to 20 percent. However, there is safety
+     tradeoff involved in skipping flushing. Indeed, when flushing is
+     skipped, then it is likely that the last few log events will not
+     be recorded on disk when the application exits. This is a high
+     price to pay even for a 20% performance gain.
+   */
+  public void setImmediateFlush(boolean value) {
+    immediateFlush = value;
+  }
+
+  /**
+     Returns value of the <b>ImmediateFlush</b> option.
+   */
+  public boolean getImmediateFlush() {
+    return immediateFlush;
+  }
+
+  /**
+   * Activates options.  Should be called only once.
+   */
+  public void activateOptions() {
+
+    if (getLayout() == null) {
+      getLogger().error(
+        "No layout set for the appender named [{}].", name);
+      return;
+    }
+
+    if (this.writer == null) {
+      getLogger().error(
+        "No writer set for the appender named [{}].", name);
+      return;
+    }
+    
+    active.set(true);
+
+  }
+
+  /**
+     This method is called by the {@link AppenderSkeleton#doAppend}
+     method.
+
+     <p>If the output stream exists and is writable then write a log
+     statement to the output stream. Otherwise, write a single warning
+     message to <code>System.err</code>.
+
+     <p>The format of the output will depend on this appender's
+     layout.
+
+  */
+  public void append(LoggingEvent event) {
+    subAppend(event);
+  }
+
+  /**
+     Close this appender instance. The underlying stream or writer is
+     also closed.
+     <p>Closed appenders cannot be reused.
+   */
+  protected void internalClose() {
+    closeWriter();
+  }
+
+  /**
+   * Close the underlying {@link java.io.Writer}.
+   */
+  protected void closeWriter() {
+    getWriteLock();
+    try {
+      closeWriter0();
+    } finally {
+      releaseWriteLock();
+    }
+  }
+
+  /**
+   * Closes with no locks held.
+   */
+  private void closeWriter0() {
+    if (this.writer == null)
+      return;
+    try {
+      // before closing we have to output the footer
+      writeFooter();
+      this.writer.close();
+      this.writer = null;
+    } catch (IOException e) {
+      getLogger().error("Could not close writer for WriterAppener named "+name, e);
+    }
+  }
+
+  /**
+     Returns an OutputStreamWriter when passed an OutputStream.  The
+     encoding used will depend on the value of the
+     <code>encoding</code> property.  If the encoding value is
+     specified incorrectly the writer will be opened using the default
+     system encoding (an error message will be printed to the loglog.  */
+  protected OutputStreamWriter createWriter(OutputStream os) {
+    OutputStreamWriter retval = null;
+
+    String enc = getEncoding();
+
+    if (enc != null) {
+      try {
+        retval = new OutputStreamWriter(os, enc);
+      } catch (IOException e) {
+        getLogger().warn("Error initializing output writer.");
+        getLogger().warn("Unsupported encoding?");
+      }
+    }
+
+    if (retval == null) {
+      retval = new OutputStreamWriter(os);
+    }
+
+    return retval;
+  }
+
+  public String getEncoding() {
+    return encoding;
+  }
+
+  public void setEncoding(String value) {
+    encoding = value;
+  }
+
+  /**
+    <p>Sets the Writer where the log output will go. The
+    specified Writer must be opened by the user and be
+    writable.
+
+    <p>The <code>java.io.Writer</code> will be closed when the
+    appender instance is closed.
+
+
+    <p><b>WARNING:</b> Logging to an unopened Writer will fail.
+    <p>
+    @param writer An already opened Writer.  */
+  public void setWriter(Writer writer) {
+    // close any previously opened writer
+    getWriteLock();
+    try {
+      closeWriter0(); 
+      this.writer = writer;
+      writeHeader();
+    } finally {
+      releaseWriteLock();
+    }
+  }
+
+  /**
+   * Actual writing occurs here. 
+   * <p>Most subclasses of <code>WriterAppender</code> will need to override 
+   * this method.
+   */
+  protected void subAppend(LoggingEvent event) {
+    try {
+
+      // Format first
+      Layout layout = getLayout();
+      String se = layout.format(event);
+      String st[] = null;
+      if (layout.ignoresThrowable()) {
+        st = event.getThrowableStrRep();
+      }
+
+      // Write as one message
+      synchronized (this.writer) {
+        this.writer.write(se);
+        if (st != null) {
+          int len = st.length;
+          for (int i = 0; i < len; i++) {
+            this.writer.write(st[i]);
+            this.writer.write(Layout.LINE_SEP);
+          }
+        }
+      }
+
+      if (this.immediateFlush)
+        this.writer.flush();
+
+    } catch (IOException ioe) {
+      boolean wasOrder = active.set(false);
+      if (wasOrder) {
+        getLogger().error("IO failure for appender named "+name, ioe);
+      }
+    }
+  }
+
+  /**
+     The WriterAppender requires a layout. Hence, this method returns
+     <code>true</code>.
+  */
+  public boolean requiresLayout() {
+    return true;
+  }
+
+  /**
+   * Write a footer as produced by the embedded layout's {@link 
+   * Layout#getFooter} method.  
+   */
+  protected void writeFooter() {
+    Layout layout = getLayout();
+    if (layout != null) {
+      String f = layout.getFooter();
+
+      if ((f != null) && (this.writer != null)) {
+        try {
+          this.writer.write(f);
+        } catch(IOException ioe) {
+          active.set(false);
+          getLogger().error("Failed to write footer for Appender named "+name, ioe);
+        }
+      }
+    }
+  }
+
+  /**
+   * Write a header as produced by the embedded layout's {@link 
+   * Layout#getHeader} method.  
+   */
+  protected void writeHeader() {
+    Layout layout = getLayout();
+    if (layout != null) {
+      String h = layout.getHeader();
+
+      if ((h != null) && (this.writer != null)) {
+        try {
+          this.writer.write(h);
+          this.writer.flush();
+        } catch(IOException ioe) {
+          active.set(false);
+          getLogger().error("Failed to write header for WriterAppender named "+name, ioe);
+        }
+      }
+    }
+  }
+
+
+}
+

Added: logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/DeadlockTest.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/DeadlockTest.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/DeadlockTest.java (added)
+++ logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/DeadlockTest.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999,2006 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.
+ */
+
+package org.apache.log4j.concurrent;
+import org.apache.log4j.Logger;
+import org.apache.log4j.LogManager;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test case for bug http://issues.apache.org/bugzilla/show_bug.cgi?id=24159
+ *
+ * @author Elias Ross
+ * @author Ceki Gulcu
+ */
+public class DeadlockTest extends TestCase {
+  static long RUNLENGTH = 10000;
+  Logger logger = Logger.getLogger("DeadlockTest");
+
+  public DeadlockTest(final String name) {
+      super(name);
+  }
+  
+  protected void setUp() throws Exception {
+    super.setUp();
+    System.out.println("in setup");
+    
+    Logger root = Logger.getRootLogger();
+    root.addAppender(new org.apache.log4j.concurrent.ConsoleAppender(
+        new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));
+  }
+
+  protected void tearDown() throws Exception {
+    super.tearDown();
+    System.out.println("tear down");
+    LogManager.shutdown();
+  }
+
+
+  private static final class Deadlock {
+     static final Logger log = Logger.getLogger(Deadlock.class);
+     String var;
+
+     public synchronized void setVar(String var) {
+        log.debug(this);
+     }
+
+     public synchronized String getVar() {
+        return var;
+     }
+
+     public String toString() {
+       return "Value x=" + getVar();
+     }
+  }
+
+  public void testDeadlock() throws InterruptedException {
+    System.out.println("in testDeadlock()");
+
+    final Deadlock d = new Deadlock();
+
+    Thread t1 =
+      new Thread() {
+        public void run() {
+          long start = System.currentTimeMillis();
+
+          while ((System.currentTimeMillis() - start) < RUNLENGTH) {
+            logger.debug(d);
+          }
+        }
+      };
+
+    Thread t2 =
+      new Thread() {
+        public void run() {
+          long start = System.currentTimeMillis();
+
+          while ((System.currentTimeMillis() - start) < RUNLENGTH) {
+            d.setVar("n");
+          }
+        }
+      };
+
+    t1.start();
+    t2.start();
+    System.out.println("Waiting to join t1.");
+    t1.join();
+    System.out.println("======================Joined t1=====================");
+  }
+}
+
+

Added: logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/PerformanceTest.java
URL: http://svn.apache.org/viewcvs/logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/PerformanceTest.java?rev=376902&view=auto
==============================================================================
--- logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/PerformanceTest.java (added)
+++ logging/sandbox/log4j/concurrent/src/test/java/org/apache/log4j/concurrent/PerformanceTest.java Fri Feb 10 17:10:09 2006
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999,2006 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.
+ */
+
+package org.apache.log4j.concurrent;
+
+import junit.framework.TestCase;
+import java.io.Writer;
+import java.io.StringWriter;
+import java.io.FileWriter;
+import java.io.File;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Appender;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+public class PerformanceTest extends TestCase {
+
+  private static final String LAYOUT = "%d{ABSOLUTE} [%24c{1}] (%-8X{con} %X{opr})  %m%n";
+  private int threads = 5;
+  private static int times = 1000;
+  private static final Logger log = Logger.getLogger(PerformanceTest.class);
+
+
+  private static class Greeter implements Runnable {
+      private static final Logger log = Logger.getLogger(PerformanceTest.class);
+      public static final Greeter INSTANCE = new Greeter();
+      private Greeter() {
+      }
+
+
+     public void run() {
+        for (int i = 0; i < PerformanceTest.times; i++) {
+           log.info("Hello world", new Exception());
+        }
+     }
+  }
+
+  private void performanceTest(Appender a, Layout layout) throws Exception {
+
+    log.removeAllAppenders();
+    log.addAppender(a);
+
+    Thread t[] = new Thread[threads];
+    a.setLayout(layout);
+    ((OptionHandler)a).activateOptions();
+
+    long start = System.currentTimeMillis();
+
+    for (int i = 0; i < threads; i++) {
+      t[i] = new Thread(Greeter.INSTANCE);
+      t[i].start();
+    }
+    for (int i = 0; i < threads; i++) {
+      t[i].join();
+    }
+
+    long end = System.currentTimeMillis();
+    a.close();
+
+    System.out.println("Appender " + a.getClass());
+    String msg = "Took " + (end - start) + "ms for " + times + " logs * " + " threads " + threads;
+    System.out.println(msg);
+    System.out.println();
+  }
+    
+  public void testMain() throws Exception {
+    
+    Thread.sleep(1000);
+
+    File tempFile = File.createTempFile("blah", ".log");
+    for (int i = 0; i < 5; i++) {
+
+
+      org.apache.log4j.FileAppender wa = new org.apache.log4j.FileAppender();
+      org.apache.log4j.Layout layout = new org.apache.log4j.PatternLayout(LAYOUT);
+      
+      wa.setFile(tempFile.toString());
+      wa.setAppend(false);
+      performanceTest(wa, layout);
+
+      org.apache.log4j.concurrent.FileAppender wa2 = new org.apache.log4j.concurrent.FileAppender();
+      org.apache.log4j.Layout layout2 = new org.apache.log4j.concurrent.PatternLayout(LAYOUT);
+      wa2.setFile(tempFile.toString());
+      wa2.setAppend(false);
+      performanceTest(wa2, layout2);
+
+    }
+    tempFile.delete();
+
+    Thread.sleep(1000);
+
+  }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Mime
View raw message