On Sat, 2005-01-22 at 04:58, Ceki GÃlcà wrote:
> 
> Since what you are doing is not clear, your enthusiasm, although nice to 
> see, is hard to share.

Here are the files plus a patch to the pattern layout classes. 
(Modifying the layout classes to be thread-safe was a separate step.)

It's still a bit rough around the edges, especially the javadoc.  I
pretty much copied/pasted the implementations of Writer and Console
appender, with a few changes here and there.

I use ThreadLocal in a few places, which is only available in JVM 1.2+

/*
 * 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.Level;
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 [EMAIL PROTECTED] #append} method
 * called by multiple threads.  In some cases, derived classes must provide
 * their own internal locking to prevent race conditions.
 * Derived classes must also override [EMAIL PROTECTED] #close}.
 * <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 a [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #append} method.
 * </p>
 */
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;

  /**
   * Appenders are named.
   */
  protected String name;

  /**
   * There is no level threshold filtering by default.
   */
  private Level threshold;

  /**
   * Internal class, internally locked.
   */
  private FilterChain filter = new FilterChain();

  /**
   * Is this appender closed?
   */
  private SynchronizedBoolean closed = new SynchronizedBoolean(false);

  /**
   * The guard prevents an appender from repeatedly calling its own doAppend
   * method.
   */
  private ThreadLocal guard = new ThreadLocal();

  /**
   * Write obtained to change the options, Read obtained to
   * append event.
   */
  private ReaderWriterLock lock = new ReaderWriterLock();

  /**
   * Default constructor.
   */
  public ConcurrentAppender() {
  }

  /**
   * Derived appenders should override this method if option structure
   * requires it.
   */
  public void activateOptions() {
  }

  /**
   * Add a filter to end of the filter list.
   */
  public void addFilter(Filter newFilter) {
    filter.addFilter(newFilter);
  }

  /**
   * Clear the filters chain.
   */
  public void clearFilters() {
    filter.clear();
  }

  /**
   * Returns the head Filter.
   */
  public Filter getFilter() {
    return filter.getHead();
  }

  /**
   * Returns the layout of this appender. The value may be null.
   */
  public Layout getLayout() {
    return this.layout;
  }

  /**
   * Returns the name of this FileAppender.
   */
  public final String getName() {
    return this.name;
  }

  /**
   * Returns this appenders threshold level. See the [EMAIL PROTECTED] #setThreshold}
   * method for the meaning of this option.
   */
  public Level getThreshold() {
    return threshold;
  }

  /**
   * Check whether the message level is below the appender's threshold. If
   * there is no threshold set, then the return value is always
   * <code>true</code>.
   */
  public boolean isAsSevereAsThreshold(Level level) {
    Level copy = threshold;
    return ((copy == null) || copy.isGreaterOrEqual(level));
  }

  /**
   * This method performs threshold checks and invokes filters before
   * delegating actual logging to the subclasses specific [EMAIL PROTECTED]
   * AppenderSkeleton#append} method.
   */
  public final void doAppend(LoggingEvent event) {

    if (!isAsSevereAsThreshold(event.getLevel()))
      return;

    if (!filter.accept(event))
      return;

    try {

      // Prevent concurrent re-entry
      // (There might be a cheaper way to do this)
      if (guard.get() != null)
        return;
      guard.set(this); // arbitrary lock

      try {

        lock.getReadLock();

        if (closed.get()) {
          getLogger().error(
              "Attempted to use closed appender named [" + name + "].");
          return;
        }

        append(event);

      } finally {
        lock.releaseReadLock();
      }

    } finally {
      guard.set(null);
    }
  }

  /**
   * Set the layout for this appender. Note that some appenders have their own
   * (fixed) layouts or do not use one. For example, the [EMAIL PROTECTED]
   * org.apache.log4j.net.SocketAppender} ignores the layout set here.
   */
  public void setLayout(Layout layout) {
    this.layout = layout;
  }

  /**
   * Set the name of this Appender.
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Set the threshold level. All log events with lower level than the
   * threshold level are ignored by the appender.
   */
  public void setThreshold(Level threshold) {
    this.threshold = threshold;
  }

  /**
   * Returns true if this appender is closed.
   * An appender can be closed only once.
   */
  public boolean getClosed() {
    return closed.get();
  }

  /**
   * Called to clean up the appender.
   * Marked as <code>final</code> to prevent subclasses from accidentally not
   * calling <code>super.close()</code>.
   * Calls [EMAIL PROTECTED] #internalClose} when completed.
   * Implementation note:  Obtains a write lock before starting close.
   */
  public final void close() {
    boolean wasClosed;
    try {
      lock.getWriteLock();
      wasClosed = closed.set(true);
    } finally {
      lock.releaseWriteLock();
    }

    if (!wasClosed)
      internalClose();
  }

  /**
   * Returns a string representation of this object.
   */
  public String toString() {
    return super.toString() + " name=" + name + 
      " threshold=" + threshold + " layout=" + layout;
  }

  // PROTECTED METHODS

  /**
   * Subclasses of <code>ConcurrentAppender</code> should implement this method
   * to perform actual logging. 
   */
  protected abstract void append(LoggingEvent event);

  /**
   * Subclasses must implement their own close routines.
   * This is guaranteed to be called only once.
   */
  protected abstract void internalClose();

  /**
   * Prevents concurrently executing logs by this class.
   * This is normally done when changing output behavior, closing and reopening
   * streams, etc.  Call [EMAIL PROTECTED] #releaseWriteLock} when modification is complete.
   * <p>
   * It is preferable (and likely safer) to use an internal locking strategy than 
   * reuse these locks.
   * </p>
   */
  protected void getWriteLock() {
    lock.getWriteLock();
  }

  /**
   * Caling this method allows concurrently executing logs by this class.
   */
  protected void releaseWriteLock() {
    lock.releaseWriteLock();
  }

  /**
   * Finalize this appender by calling the derived class' <code>close</code>
   * method.
   */
  protected void finalize() {
    if (!getClosed())
      getLogger().debug("Finalizing appender named [{}].", name);
    close();
  }

  /**
   * Convenience class.
   */
  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 (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;
    }

  }

}
/*
 * 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;

/**
  * 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 Ceki G&uuml;lc&uuml;
  * @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;

  /**
   * As in most cases, the default constructor does nothing.
   */
  public ConsoleAppender() {
  }

  public ConsoleAppender(Layout layout) {
    this(layout, SYSTEM_OUT);
  }

  public ConsoleAppender(Layout layout, 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(String value) {
    String v = value.trim();

    if (SYSTEM_OUT.equalsIgnoreCase(v)) {
      target = SYSTEM_OUT;
    } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
      target = SYSTEM_ERR;
    } else {
      targetWarn(value);
    }
  }

  /**
   * Returns the current value of the <b>Target</b> property. The
   * default value of the option is "System.out".
   *
   * See also [EMAIL PROTECTED] #setTarget}.
   * */
  public String getTarget() {
    return target;
  }

  void targetWarn(String val) {
    getLogger().warn("[{}] should be System.out or System.err.", val);
    getLogger().warn("Using previously set target, System.out by default.");
  }

  public void activateOptions() {
    if (target.equals(SYSTEM_OUT)) {
      setWriter(createWriter(System.out));
    } else {
      setWriter(createWriter(System.err));
    }
    super.activateOptions();
  }

  /**
   *  This method overrides the parent [EMAIL PROTECTED]
   *  WriterAppender#closeWriter} implementation to do nothing because
   *  the console stream is not ours to close.
   * */
  protected final void closeWriter() {
  }
}
/*
 * 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.StringWriter;
import java.io.FileWriter;

import org.apache.log4j.Logger;
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 implements Runnable {

  static Logger log = Logger.getLogger(PerformanceTest.class);

  String LAYOUT = "%d{ABSOLUTE} [%24c{1}] (%-8X{con} %X{opr})  %m%n";
  int times = 1000;
  int threads = 5;

  public void run() {
    for (int i = 0; i < times; i++) {
      log.info("Hello world", new Exception());
    }
  }

  public PerformanceTest(Appender a) throws Exception {

    log.removeAllAppenders();
    log.addAppender(a);

    Thread t[] = new Thread[threads];
    a.setLayout(new PatternLayout(LAYOUT));
    ((OptionHandler)a).activateOptions();

    long start = System.currentTimeMillis();

    for (int i = 0; i < threads; i++) {
      t[i] = new Thread(this);
      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 static void main(String s[]) throws Exception {
    
    System.out.println("Hit CTRL-\\ now");
    Thread.sleep(1000);

    Writer w;
    for (int i = 0; i < 10; i++) {

      /*
      ConcurrentAppender ca = new ConcurrentAppender() {
        protected void append(LoggingEvent event) {
          try { Thread.sleep(1); } catch (InterruptedException e) {}
        }
        protected void internalClose() {}
      };

      AppenderSkeleton as = new AppenderSkeleton() {
        protected void append(LoggingEvent event) {
          try { Thread.sleep(1); } catch (InterruptedException e) {}
        }
        public void close() {}
      };

      System.out.println("ConcurrentAppender");
      new PerformanceTest(ca);

      System.out.println("AppenderSkeleton");
      new PerformanceTest(as);
      */

      w = new FileWriter("/tmp/blah");

      org.apache.log4j.WriterAppender wa = new org.apache.log4j.WriterAppender();
      wa.setWriter(w);
      new PerformanceTest(wa);

      /*
      w = new FileWriter("/tmp/blah");

      org.apache.log4j.concurrent.WriterAppender wa2 = new org.apache.log4j.concurrent.WriterAppender();
      wa2.setWriter(w);
      new PerformanceTest(wa2);
      */

    }

    System.out.println("Hit CTRL-\\ now");
    Thread.sleep(1000);

  }

}
/*
  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;
  }

  public String toString() { return String.valueOf(get()); }

}

/*
 * 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 <[EMAIL PROTECTED]>
//              Ben Sandee

/**
   WriterAppender appends log events to a [EMAIL PROTECTED] java.io.Writer} or an
   [EMAIL PROTECTED] java.io.OutputStream} depending on the user's choice.

   @author Ceki G&uuml;lc&uuml;
   @since 1.1 */
public class WriterAppender extends ConcurrentAppender {
  
  /**
   * Set to true when the appender is in functioning order.
   * Subclasses can set this to false to indicate things are not in order.
   */
  protected SynchronizedBoolean inOrder = new SynchronizedBoolean(false);
  
  /**
     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 [EMAIL PROTECTED] Writer Writer} where we will write to.
   * Do not modify this class without obtaining a write lock.
   */
  protected Writer writer;

  /**
   * This default constructor does nothing.  
   */
  public WriterAppender() {
  }

  /**
     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 (inOrder.get())
      throw new IllegalStateException();

    if (getLayout() == null) {
      getLogger().error(
        "No layout set for the appender named [{}].", name);
      return;
    }
    
    if (this.writer != null) {
      inOrder.set(true);
    }
  }

  /**
     This method is called by the [EMAIL PROTECTED] 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) {

    if (!inOrder.get())
      return;

    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 [EMAIL PROTECTED] java.io.Writer}.
   */
  protected void closeWriter() {
    try {
      getWriteLock();
      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);
      }
    } finally {
      releaseWriteLock();
    }
  }

  /**
     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
    closeWriter();
    
    try {
      getWriteLock();
      this.writer = writer;
      writeHeader();
    } finally {
      releaseWriteLock();
    }
  }

  /**
   * Actual writing occurs here. 
   * <p>Most subclasses of <code>WriterAppender</code> will need to override 
   * this method.
   *  
   * @since 0.9.0 
   * */
  protected void subAppend(LoggingEvent event) {
    try {

      // Format internally
      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 (immediateFlush)
          this.writer.flush();

      }

    } catch (IOException ioe) {
      boolean wasOrder = inOrder.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 [EMAIL PROTECTED] 
   * 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);
          this.writer.flush();
        } catch(IOException ioe) {
          inOrder.set(false);
          getLogger().error("Failed to write footer for Appender named "+name, ioe);
        }
      }
    }
  }

  /**
   * Write a header as produced by the embedded layout's [EMAIL PROTECTED] 
   * 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) {
          inOrder.set(false);
          getLogger().error("Failed to write header for WriterAppender named "+name, ioe);
        }
      }
    }
  }

}
? src/java/org/apache/log4j/concurrent
Index: src/java/org/apache/log4j/Layout.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/Layout.java,v
retrieving revision 1.14
diff -b -B -U8 -r1.14 Layout.java
--- src/java/org/apache/log4j/Layout.java	4 Jan 2005 15:09:54 -0000	1.14
+++ src/java/org/apache/log4j/Layout.java	22 Jan 2005 17:36:23 -0000
@@ -33,31 +33,26 @@
 
 */
 public abstract class Layout extends ComponentBase implements OptionHandler {
   // Note that the line.separator property can be looked up even by
   // applets.
   public static final String LINE_SEP = System.getProperty("line.separator");
   public static final int LINE_SEP_LEN = LINE_SEP.length();
   
-  
-  
-  
-  public CharArrayWriter charArrayWriter = new CharArrayWriter(1024);
-
   String header;
   String footer;
 
   protected boolean ignoresThrowable = true;
   
   /**
    * Implement this method to create your own layout format.
    * */
   public String format(LoggingEvent event) {
-	  charArrayWriter.reset();
+    CharArrayWriter charArrayWriter = new CharArrayWriter(80);
 	  try {
   	  format(charArrayWriter, event);
 	  } catch(IOException ie) {
 	  	// There cannot be an IoException while writing to a CharArrayWriter
 	  	getLogger().error("Unexpected IOException while writing to CharArrayWriter", ie);
 	  }
   	return charArrayWriter.toString();
   }
Index: src/java/org/apache/log4j/PatternLayout.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/PatternLayout.java,v
retrieving revision 1.35
diff -b -B -U8 -r1.35 PatternLayout.java
Index: src/java/org/apache/log4j/pattern/CachedDateFormat.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/CachedDateFormat.java,v
retrieving revision 1.16
diff -b -B -U8 -r1.16 CachedDateFormat.java
--- src/java/org/apache/log4j/pattern/CachedDateFormat.java	27 Dec 2004 08:10:15 -0000	1.16
+++ src/java/org/apache/log4j/pattern/CachedDateFormat.java	22 Jan 2005 17:36:23 -0000
@@ -357,9 +356,14 @@
      //      one millisecond which should only perform duplicate request caching.
      //
      int firstS = pattern.indexOf('S');
      if (firstS >= 0 && firstS != pattern.lastIndexOf("SSS")) {
          return 1;
      }
      return 1000;
   }
+
+  public Object clone() {
+    return new CachedDateFormat((DateFormat)formatter.clone(), expiration);
+  }
+
 }
Index: src/java/org/apache/log4j/pattern/DatePatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/DatePatternConverter.java,v
retrieving revision 1.19
diff -b -B -U8 -r1.19 DatePatternConverter.java
--- src/java/org/apache/log4j/pattern/DatePatternConverter.java	6 Jan 2005 19:27:03 -0000	1.19
+++ src/java/org/apache/log4j/pattern/DatePatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -24,22 +24,24 @@
 
 
 /**
  * Convert and format the event's date in a StringBuffer.
  *
  * @author Ceki G&uuml;lc&uuml;
  */
 public class DatePatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that calls to the 
-  // appender method are serialized (per appender).
 
   private CachedDateFormat df;
-  private final StringBuffer buf = new StringBuffer(30); 
+
+  private ThreadLocal tl = new ThreadLocal() {
+    protected Object initialValue() {
+      return df.clone();
+    }
+  };
 
   //  public DatePatternConverter(FormattingInfo formattingInfo) {
   //    super(formattingInfo);
   //    this.buf = new StringBuffer(32);
   //    date = new Date();
   //  }
   public DatePatternConverter() {
   }
@@ -90,20 +92,19 @@
     if (optionList != null && optionList.size() > 1) {
       TimeZone tz = TimeZone.getTimeZone((String) optionList.get(1));
       simpleFormat.setTimeZone(tz);
     }
 
     df = new CachedDateFormat(simpleFormat, maximumCacheValidity);
   }
   
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
-    df.format(event.getTimeStamp(), buf);
-    return buf;
+  public void convert(StringBuffer buf, LoggingEvent event) {
+    CachedDateFormat cdf = (CachedDateFormat)tl.get();
+    cdf.format(event.getTimeStamp(), buf);
   }
 
   public String getName() {
     return "Date";
   }
 
   public String getStyleClass(LoggingEvent e) {
     return "date";
Index: src/java/org/apache/log4j/pattern/FileLocationPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/FileLocationPatternConverter.java,v
retrieving revision 1.7
diff -b -B -U8 -r1.7 FileLocationPatternConverter.java
--- src/java/org/apache/log4j/pattern/FileLocationPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.7
+++ src/java/org/apache/log4j/pattern/FileLocationPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -21,35 +21,26 @@
 
 /**
  * Return the event's line location information in a StringBuffer.
  * This buffer is recycled!
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class FileLocationPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public FileLocationPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
-
+  public void convert(StringBuffer buf, LoggingEvent event) {
 	LocationInfo locationInfo = event.getLocationInformation();
-    if (locationInfo!=null) {
+    if (locationInfo != null) {
     	buf.append(locationInfo.getFileName());
     }
-
-    return buf;
   }
   
   public String getName() {
       return "File Location";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "file";
Index: src/java/org/apache/log4j/pattern/FullLocationPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/FullLocationPatternConverter.java,v
retrieving revision 1.7
diff -b -B -U8 -r1.7 FullLocationPatternConverter.java
--- src/java/org/apache/log4j/pattern/FullLocationPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.7
+++ src/java/org/apache/log4j/pattern/FullLocationPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -21,35 +21,26 @@
 
 /**
  * Return the event's line location information in a StringBuffer.
  * This buffer is recycled!
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class FullLocationPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public FullLocationPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
-
+  public void convert(StringBuffer buf, LoggingEvent event) {
 	  LocationInfo locationInfo = event.getLocationInformation();
     if (locationInfo!=null) {
 			buf.append(locationInfo.getFullInfo());
 		}
-
-    return buf;
   }
   
   public String getName() {
       return "Full Location";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "fullLocation";
Index: src/java/org/apache/log4j/pattern/LevelPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/LevelPatternConverter.java,v
retrieving revision 1.6
diff -b -B -U8 -r1.6 LevelPatternConverter.java
--- src/java/org/apache/log4j/pattern/LevelPatternConverter.java	4 Nov 2004 15:44:56 -0000	1.6
+++ src/java/org/apache/log4j/pattern/LevelPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -20,31 +20,23 @@
 import org.apache.log4j.spi.LoggingEvent;
 
 /**
  * Return the event's level in a StringBuffer.
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class LevelPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public LevelPatternConverter() {
     super();
-    this.buf = new StringBuffer(5);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
+  public void convert(StringBuffer buf, LoggingEvent event) {
     buf.append(event.getLevel().toString());
-
-    return buf;
   }
   
   public String getName() {
       return "Level";
   }
   
   public String getStyleClass(LoggingEvent e) {
     if(e == null) {
Index: src/java/org/apache/log4j/pattern/LineLocationPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/LineLocationPatternConverter.java,v
retrieving revision 1.7
diff -b -B -U8 -r1.7 LineLocationPatternConverter.java
--- src/java/org/apache/log4j/pattern/LineLocationPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.7
+++ src/java/org/apache/log4j/pattern/LineLocationPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -21,35 +21,27 @@
 
 /**
  * Return the event's line location information in a StringBuffer.
  * This buffer is recycled!
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class LineLocationPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public LineLocationPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
+  public void convert(StringBuffer buf, LoggingEvent event) {
 
 	  LocationInfo locationInfo = event.getLocationInformation();
     if (locationInfo!=null) {
 			buf.append(locationInfo.getLineNumber());
 		}
-
-    return buf;
   }
   
   public String getName() {
       return "Line";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "line";
Index: src/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java,v
retrieving revision 1.4
diff -b -B -U8 -r1.4 LineSeparatorPatternConverter.java
--- src/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.4
+++ src/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -26,22 +26,20 @@
  * @author Ceki G&uuml;lc&uuml;
  */
 public class LineSeparatorPatternConverter extends PatternConverter {
 
   StringBuffer buf;
 
   public LineSeparatorPatternConverter() {
     super();
-    this.buf = new StringBuffer(Layout.LINE_SEP_LEN);
-    buf.append(Layout.LINE_SEP);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    return buf;
+  public void convert(StringBuffer buf, LoggingEvent event) {
+    buf.append(Layout.LINE_SEP);
   }
   
   public String getName() {
       return "Line Sep";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "lineSep";
Index: src/java/org/apache/log4j/pattern/LiteralPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/LiteralPatternConverter.java,v
retrieving revision 1.4
diff -b -B -U8 -r1.4 LiteralPatternConverter.java
--- src/java/org/apache/log4j/pattern/LiteralPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.4
+++ src/java/org/apache/log4j/pattern/LiteralPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -13,32 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package org.apache.log4j.pattern;
 import org.apache.log4j.spi.LoggingEvent;
 
 public class LiteralPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
   private String literal;
 
   LiteralPatternConverter(String value) {
     literal = value;
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
+  public void convert(StringBuffer buf, LoggingEvent event) {
     buf.append(literal);
-    
-    return buf;
   }
   
   public String getName() {
       return "Literal";
   }
   public String getStyleClass(LoggingEvent e) {
     return "literal";
   }
Index: src/java/org/apache/log4j/pattern/MessagePatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/MessagePatternConverter.java,v
retrieving revision 1.5
diff -b -B -U8 -r1.5 MessagePatternConverter.java
--- src/java/org/apache/log4j/pattern/MessagePatternConverter.java	16 Sep 2004 19:18:58 -0000	1.5
+++ src/java/org/apache/log4j/pattern/MessagePatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -30,30 +30,20 @@
 
   // We assume that each PatternConveter instance is unique within a layout, 
   // which is unique within an appender. We further assume that callas to the 
   // appender method are serialized (per appender).
   StringBuffer buf;
 
   public MessagePatternConverter() {
     super();
-    this.buf = new StringBuffer(BUF_SIZE);
-  }
-
-  public StringBuffer convert(LoggingEvent event) {
-    // Reset working stringbuffer
-    if (buf.capacity() > MAX_CAPACITY) {
-      buf = new StringBuffer(BUF_SIZE);
-    } else {
-      buf.setLength(0);
     }
 
+  public void convert(StringBuffer buf, LoggingEvent event) {
     buf.append(event.getRenderedMessage());
-
-    return buf;
   }
   
   public String getName() {
       return "Message";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "message";
Index: src/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java,v
retrieving revision 1.7
diff -b -B -U8 -r1.7 MethodLocationPatternConverter.java
--- src/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.7
+++ src/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -21,35 +21,26 @@
 
 /**
  * Return the event's line location information in a StringBuffer.
  * This buffer is recycled!
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class MethodLocationPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public MethodLocationPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
-
+  public void convert(StringBuffer buf, LoggingEvent event) {
 	  LocationInfo locationInfo = event.getLocationInformation();
     if (locationInfo!=null) {
 			buf.append(locationInfo.getMethodName());
 		}
-
-    return buf;
   }
   
   public String getName() {
       return "Method";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "method";
Index: src/java/org/apache/log4j/pattern/NDCPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/NDCPatternConverter.java,v
retrieving revision 1.5
diff -b -B -U8 -r1.5 NDCPatternConverter.java
--- src/java/org/apache/log4j/pattern/NDCPatternConverter.java	16 Sep 2004 19:18:58 -0000	1.5
+++ src/java/org/apache/log4j/pattern/NDCPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -19,31 +19,23 @@
 import org.apache.log4j.spi.LoggingEvent;
 
 /**
  * Return the event's NDC in a StringBuffer.
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class NDCPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public NDCPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
+  public void convert(StringBuffer buf, LoggingEvent event) {
     buf.append(event.getNDC());
-
-    return buf;
   }
   
   public String getName() {
       return "NDC";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "ndc";
Index: src/java/org/apache/log4j/pattern/NamedPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/NamedPatternConverter.java,v
retrieving revision 1.6
diff -b -B -U8 -r1.6 NamedPatternConverter.java
--- src/java/org/apache/log4j/pattern/NamedPatternConverter.java	6 Jan 2005 19:27:03 -0000	1.6
+++ src/java/org/apache/log4j/pattern/NamedPatternConverter.java	22 Jan 2005 17:36:23 -0000
@@ -24,25 +24,20 @@
 /**
  * 
  * Base class for other pattern converters which can return only parts of their name.
  *  
  * @author Ceki G&uuml;lc&uuml;
  */
 abstract class NamedPatternConverter extends PatternConverter {
 	
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
   int precision;
 
   public NamedPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
   abstract String getFullyQualifiedName(LoggingEvent event);
 
   public void setOptions(List optionList) {
     if(optionList == null || optionList.size() == 0) {
       return;
     }
@@ -65,38 +60,36 @@
         }
       } catch (NumberFormatException e) {
         getLogger().error(
           "Category option \"" + option + "\" not a decimal integer.", e);
       }
     }
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
-
+  public void convert(StringBuffer buf, LoggingEvent event) {
     String n = getFullyQualifiedName(event);
     if (precision <= 0) {
       buf.append(n);
     } else {
       int len = n.length();
 
       // We substract 1 from 'len' when assigning to 'end' to avoid out of
       // bounds exception in return r.substring(end+1, len). This can happen if
       // precision is 1 and the category name ends with a dot.
       int end = len - 1;
 
       for (int i = precision; i > 0; i--) {
         end = n.lastIndexOf('.', end - 1);
 
         if (end == -1) {
         	// not enough dot characters. The whole string should be returned
-          return buf.append(n);
+          buf.append(n);
+          return;
         }
       }
 
       // The end variable should point to the left-most dot character to
       // the right of which all character should be returned.
       buf.append(n.substring(end + 1, len));
     }
-    return buf;
   }
 }
Index: src/java/org/apache/log4j/pattern/PatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/PatternConverter.java,v
retrieving revision 1.16
diff -b -B -U8 -r1.16 PatternConverter.java
--- src/java/org/apache/log4j/pattern/PatternConverter.java	4 Jan 2005 19:42:11 -0000	1.16
+++ src/java/org/apache/log4j/pattern/PatternConverter.java	22 Jan 2005 17:36:24 -0000
@@ -68,27 +68,25 @@
     min = fi.min;
     max = fi.max;
     leftAlign = fi.leftAlign;
   }
 
   /**
      Derived pattern converters must override this method in order to
      convert conversion specifiers in the correct way.
-     
-     IMPORTANT: Note that an implementing class may always return the same
-     StringBuffer instance in order to avoid superflous object creation.
   */
-  protected abstract StringBuffer convert(LoggingEvent event);
+  protected abstract void convert(StringBuffer sb, LoggingEvent event);
 
   /**
      A template method for formatting in a converter specific way.
    */
   public void format(Writer output, LoggingEvent e) throws IOException {
-    StringBuffer s = convert(e);
+    StringBuffer s = new StringBuffer();
+    convert(s, e);
 
     if (s == null) {
       if (0 < min) {
         spacePad(output, min);
       }
 
       return;
     }
Index: src/java/org/apache/log4j/pattern/PropertiesPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/PropertiesPatternConverter.java,v
retrieving revision 1.9
diff -b -B -U8 -r1.9 PropertiesPatternConverter.java
--- src/java/org/apache/log4j/pattern/PropertiesPatternConverter.java	22 Dec 2004 21:56:35 -0000	1.9
+++ src/java/org/apache/log4j/pattern/PropertiesPatternConverter.java	22 Jan 2005 17:36:24 -0000
@@ -32,46 +32,41 @@
  * @author Ceki G&uuml;lc&uuml;
  [EMAIL PROTECTED] 1.3
  */
 public class PropertiesPatternConverter extends PatternConverter {
   
   String name;
   String option;
   
-  public StringBuffer convert(LoggingEvent event) {
-
-    StringBuffer buf = new StringBuffer(32);
-
+  public void convert(StringBuffer buf, LoggingEvent event) {
     // if there is no additional options, we output every single
     // Key/Value pair for the MDC in a similar format to Hashtable.toString()
     if (option == null) {
       buf.append("{");
 
       Set keySet = event.getPropertyKeySet();
 
       for (Iterator i = keySet.iterator(); i.hasNext();) {
         Object item = i.next();
         Object val = event.getProperty(item.toString());
         buf.append("{").append(item).append(",").append(val).append("}");
       }
 
       buf.append("}");
 
-      return buf;
+      return;
     }
 
     // otherwise they just want a single key output
     Object val = event.getProperty(option);
 
     if (val != null) {
-      return buf.append(val);
+      buf.append(val);
     }
-
-    return buf;
   }
 
   public void setOptions(List optionList) {
     if(optionList == null || optionList.size() == 0) {
       return;
     }
     option = (String) optionList.get(0);
   }
Index: src/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java,v
retrieving revision 1.7
diff -b -B -U8 -r1.7 RelativeTimePatternConverter.java
--- src/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java	23 Dec 2004 18:04:46 -0000	1.7
+++ src/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java	22 Jan 2005 17:36:24 -0000
@@ -20,40 +20,32 @@
 /*
  * Return the relative time in milliseconds since loading of the LoggingEvent 
  * class.
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class RelativeTimePatternConverter extends PatternConverter {
 	
-	// We assume that each PatternConveter instance is unique within a layout, 
-	// which is unique within an appender. We further assume that callas to the 
-	// appender method are serialized (per appender).
-  StringBuffer buf;
   long lastTimestamp = 0;
-  
+  String last;
   
   public RelativeTimePatternConverter() {
     super();
-    this.buf = new StringBuffer(9);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
+  public void convert(StringBuffer buf, LoggingEvent event) {
     long timestamp = event.getTimeStamp();
     // if called multiple times within the same milliseconds
     // return old value
-    if(timestamp == lastTimestamp) {
-      return buf;
-    } else {
-      buf.setLength(0);
+    if (timestamp != lastTimestamp) {
       lastTimestamp = timestamp;
-      buf.append(Long.toString(timestamp - LoggingEvent.getStartTime()));
+      last = Long.toString(timestamp - LoggingEvent.getStartTime());
     }
-    return buf;
+    buf.append(last);
   }
   
   public String getName() {
       return "Time";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "time";
Index: src/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java,v
retrieving revision 1.1
diff -b -B -U8 -r1.1 SequenceNumberPatternConverter.java
--- src/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java	4 Nov 2004 15:44:56 -0000	1.1
+++ src/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java	22 Jan 2005 17:36:24 -0000
@@ -19,30 +19,22 @@
 
 /*
  * Return the sequence number for the event.
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class SequenceNumberPatternConverter extends PatternConverter {
 	
-	// We assume that each PatternConveter instance is unique within a layout, 
-	// which is unique within an appender. We further assume that calls to the 
-	// appender method are serialized (per appender).
-  StringBuffer buf;
-
   public SequenceNumberPatternConverter() {
     super();
-    this.buf = new StringBuffer(9);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
+  public void convert(StringBuffer buf, LoggingEvent event) {
     buf.append(Long.toString(event.getSequenceNumber()));
-    return buf;
   }
   
   public String getName() {
       return "Sequence Number";
   }
   
   public String getStyleClass(LoggingEvent e) {
     return "sn";
Index: src/java/org/apache/log4j/pattern/ThreadPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/ThreadPatternConverter.java,v
retrieving revision 1.5
diff -b -B -U8 -r1.5 ThreadPatternConverter.java
--- src/java/org/apache/log4j/pattern/ThreadPatternConverter.java	17 Sep 2004 10:15:32 -0000	1.5
+++ src/java/org/apache/log4j/pattern/ThreadPatternConverter.java	22 Jan 2005 17:36:24 -0000
@@ -20,31 +20,23 @@
 
 /**
  * Return the events thread (usually the current thread) in a StringBuffer.
  * This buffer is recycled!
  * 
  * @author Ceki G&uuml;lc&uuml;
  */
 public class ThreadPatternConverter extends PatternConverter {
-  // We assume that each PatternConveter instance is unique within a layout, 
-  // which is unique within an appender. We further assume that callas to the 
-  // appender method are serialized (per appender).
-  StringBuffer buf;
 
   public ThreadPatternConverter() {
     super();
-    this.buf = new StringBuffer(32);
   }
 
-  public StringBuffer convert(LoggingEvent event) {
-    buf.setLength(0);
+  public void convert(StringBuffer buf, LoggingEvent event) {
     buf.append(event.getThreadName());
-
-    return buf;
   }
   
   public String getName()
   {
       return "Thread";
   }
   
   public String getStyleClass(LoggingEvent e) {
Index: src/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java
===================================================================
RCS file: /home/cvspublic/logging-log4j/src/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java,v
retrieving revision 1.6
diff -b -B -U8 -r1.6 ThrowableInformationPatternConverter.java
--- src/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java	22 Dec 2004 21:58:48 -0000	1.6
+++ src/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java	22 Jan 2005 17:36:24 -0000
@@ -27,57 +27,51 @@
  * unless this converter's option is 'short', where it just outputs the first line of the trace.
  *
  * @author Paul Smith
  * @since 1.3
  *
  */
 public class ThrowableInformationPatternConverter extends PatternConverter {
   
-  String option;
+  private String option;
+  private boolean full;
   
   /* (non-Javadoc)
    * @see org.apache.log4j.pattern.PatternConverter#convert(org.apache.log4j.spi.LoggingEvent)
    */
-  protected StringBuffer convert(LoggingEvent event) {
-    StringBuffer buf = new StringBuffer(32);
-
+  protected void convert(StringBuffer buf, LoggingEvent event) {
     ThrowableInformation information = event.getThrowableInformation();
 
     if (information == null) {
-      return buf;
+      return;
     }
 
     String[] stringRep = information.getThrowableStrRep();
 
     int length = 0;
 
-    if (option == null) {
+    if (full) {
       length = stringRep.length;
-    } else if (option.equals("full")) {
-      length = stringRep.length;
-    } else if (option.equals("short")) {
-      length = 1;
     } else {
-      length = stringRep.length;
+      length = 1;
     }
 
     for (int i = 0; i < length; i++) {
       String string = stringRep[i];
       buf.append(string).append("\n");
     }
-
-    return buf;
   }
 
   public void setOptions(List optionList) {
     if(optionList == null || optionList.size() == 0) {
       return;
     }
     option = (String) optionList.get(0);
+    full = "short".equals(option);
   }
   
   public String getName() {
     return "Throwable";
   }
 
   public String getStyleClass(LoggingEvent e) {
     return "throwable";

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to