You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/09/29 16:33:39 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util/log DefaultLogger.java Log.java LogAware.java Logger.java QueueLogger.java

costin      00/09/29 07:33:38

  Added:       src/share/org/apache/tomcat/util/log DefaultLogger.java
                        Log.java LogAware.java Logger.java QueueLogger.java
  Log:
  Forgot to add the log :-)
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/log/DefaultLogger.java
  
  Index: DefaultLogger.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.tomcat.util.log;
  
  import java.io.Writer;
  import java.io.IOException;
  
  /**
   * Trivial logger that sends all messages to the default sink.  To
   * change default sink, call Logger.setDefaultSink(Writer)
   *
   * @author Alex Chaffee (alex@jguru.com)
   * @since  Tomcat 3.1
   **/
  public class DefaultLogger extends Logger {
  
      /**
       * Prints log message to default sink
       * 
       * @param	message		the message to log.
       */
      protected void realLog(String message) {
  	try {
  	    defaultSink.write(message);
  	    defaultSink.write(NEWLINE);
  	    flush();
  	}
  	catch (IOException e) {
  	    bad(e, message, null);
  	}
      }
      
      /**
       * Prints log message to default sink
       * 
       * @param	message		the message to log.
       * @param	t		the exception that was thrown.
       */
      protected void realLog(String message, Throwable t) {
  	try {
  	    defaultSink.write(message);
  	    defaultSink.write(NEWLINE);
  	    defaultSink.write(throwableToString(t));
  	    defaultSink.write(NEWLINE);
  	    flush();
  	}
  	catch (IOException e) {
  	    bad(e, message, t);
  	}
      }
  
      private void bad(Throwable t1, String message, Throwable t2)
      {
  	System.err.println("Default sink is unwritable! Reason:");
  	if (t1 != null) t1.printStackTrace();
  	if (message != null) System.err.println(message);
  	if (t2 != null) t2.printStackTrace();
      }	
      
      /**
       * Flush the log. 
       */
      public void flush() {
  	try {
  	    defaultSink.flush();
  	}
  	catch (IOException e) {
  	}
      }    
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/log/Log.java
  
  Index: Log.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.tomcat.util.log;
  
  import java.io.*;
  import java.lang.reflect.*;
  import java.util.*;
  
  
  /**
   * Corresponds to a log chanel - this is the main class
   * seen by objects that need to log. 
   * 
   * It has a preferred log name to write to; if
   * it can't find a log with that name, it outputs to the default
   * sink.  Also prepends a descriptive name to each message
   * (usually the toString() of the calling object), so it's easier
   * to identify the source.<p>
   *
   * Intended for use by client classes to make it easy to do
   * reliable, consistent logging behavior, even if you don't
   * necessarily have a context, or if you haven't registered any
   * log files yet, or if you're in a non-Tomcat application.  Not
   * intended to supplant Logger, but to allow client objects a
   * consistent bit of code that prepares log messages before they
   * reach logger (and does the right thing if there is no logger).
   * <p>
   * Usage: <pre>
   * class Foo {
   *   Log log = new Log("tc_log", "Foo"); // or...
   *   Log log = new Log("tc_log", this); // fills in "Foo" for you
   *   ...
   *     log.log("Something happened");
   *     ...
   *     log.log("Starting something", Logger.DEBUG);
   *     ...
   *     catch (IOException e) {
   *       log.log("While doing something", e);
   *     }
   * </pre>
   *
   * @author Alex Chaffee [alex@jguru.com]
   **/
  public class Log {
  
      // name of the logger ( each logger has a unique name,
      // used as a key internally )
      private String logname;
      // string displayed at the beginning of each log line,
      // to identify the source
      private String prefix;
  
      // The real logger object ( that knows to write to
      // files, optimizations, etc)
      private Logger logger;
  
      // Do we need that? 
      //    private Log proxy;
  
      // -------------------- Various constructors --------------------
  
      public Log() {
      }
  
      /**
       * Subclass constructor, for classes that want to *be* a
       * LogHelper, and get the log methods for free (like a mixin)
       **/
      public Log(String logname) {
  	this.logname = logname;
  	String cname=this.getClass().getName();
  	this.prefix = cname.substring( cname.lastIndexOf(".") +1);
      }
      
      /**
       * @param logname name of log to use
       * @param owner object whose class name to use as prefix
       **/
      public Log(String logname, Object owner) 
      {
  	this.logname = logname;
  	String cname = owner.getClass().getName();
  	this.prefix = cname.substring( cname.lastIndexOf(".") +1);
      }	
      
      /**
       * @param logname name of log to use
       * @param prefix string to prepend to each message
       **/
      public Log(String logname, String prefix) 
      {
  	this.logname = logname;
  	this.prefix = prefix;
      }
  
      // -------------------- Log messages. --------------------
      // That all a client needs to know about logging !
      // --------------------
      
      /**
       * Logs the message with level INFORMATION
       **/
      public void log(String msg) 
      {
  	log(msg, null, Logger.INFORMATION);
      }
      
      /**
       * Logs the Throwable with level ERROR (assumes an exception is
       * trouble; if it's not, use log(msg, t, level))
       **/
      public void log(String msg, Throwable t) 
      {
  	log(msg, t, Logger.ERROR);
      }
      
      /**
       * Logs the message with given level
       **/
      public void log(String msg, int level) 
      {
  	log(msg, null, level);
      }
      
      /**
       * Logs the message and Throwable to its logger or, if logger
       * not found, to the default logger, which writes to the
       * default sink, which is usually System.err
       **/
      public void log(String msg, Throwable t, int level)
      {
  	if (prefix != null) {
  	    // tuneme
  	    msg = prefix + ": " + msg;
  	}
  	
  	// 	    // activate proxy if present
  	// 	    if (proxy != null)
  	// 		logger = proxy.getLogger();
  	
  	// activate logname fetch if necessary
  	if (logger == null) {
  	    if (logname != null)
  		logger = Logger.getLogger(logname);
  	}
  	
  	// if all else fails, use default logger (writes to default sink)
  	Logger loggerTemp = logger;
  	if (loggerTemp == null) {
  	    loggerTemp = Logger.defaultLogger;
  	}
  	loggerTemp.log(msg, t, level);
      }
  
  
      // -------------------- Extra configuration stuff --------------------
  
      
      public Logger getLogger() {
  	// 	    if (proxy != null)
  	// 		logger = proxy.getLogger();
  	return logger;
      }
      
      /**
       * Set a logger explicitly.  Also resets the logname property to
       * match that of the given log.
       *
       * <p>(Note that setLogger(null) will not necessarily redirect log
       * output to System.out; if there is a logger named logname it
       * will fall back to using it, or trying to.)
       **/
      public void setLogger(Logger logger) {
  	if (logger != null)
  	    setLogname(logger.getName());
  	this.logger = logger;
      }
      
      /**
       * Set the logger by name.  Will throw away current idea of what
       * its logger is, and next time it's asked, will locate the global
       * Logger object if the given name.
       **/	
      public void setLogname(String logname) {
  	logger = null;	// prepare to locate a new logger
  	this.logname = logname;
      }
      
      /**
       * Set the prefix string to be prepended to each message
       **/
      public void setLogPrefix(String prefix) {
  	this.prefix = prefix;
      }
      
      // 	/**
      // 	 * Set a "proxy" Log -- whatever that one says its
      // 	 * Logger is, use it
      // 	 **/
      // 	public void setProxy(Log helper) {
      // 	    this.proxy = helper;
      // 	}
      
      
      // ???
      // 	public Log getLog() {
      // 	    return this;
      // 	}
      
      }    
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/log/LogAware.java
  
  Index: LogAware.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  package org.apache.tomcat.util.log;
  
  /** Interface used by components that use Log services, and
   *  allow a controller to set the log destination and
   *  childs to log to their log channel.
   *
   *  The asymetry is intentional.
   */
  public interface LogAware {
      /** Set the real logging destination.
       *  Called by a parrent to control where the component
       *  logs.
       */
      public void setLogger(Logger logger);
  
      /**
       *  Returns the log channel used by this component.
       */
      public Log getLog();
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/log/Logger.java
  
  Index: Logger.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.tomcat.util.log;
  
  import java.io.Writer;
  import java.io.PrintWriter;
  import java.io.OutputStreamWriter;
  import java.io.FileWriter;
  import java.io.File;
  import java.io.IOException;
  import java.io.StringWriter;
  import java.lang.reflect.*;
  
  import java.util.*;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  
  // import javax.servlet.ServletException;	// for throwableToString()
  // import org.apache.tomcat.core.TomcatException;	// for throwableToString()
  import org.apache.tomcat.util.FastDateFormat;
  
  /**
   * Interface for a logging object. A logging object provides mechanism
   * for logging errors and messages that are of interest to someone who
   * is trying to monitor the system.
   * 
   * @author Anil Vijendran (akv@eng.sun.com)
   * @author Alex Chaffee (alex@jguru.com)
   * @since  Tomcat 3.1
   */
  public abstract class Logger {
      // ----- static content -----
      
      /**
       * Verbosity level codes.
       */
      public static final int FATAL = Integer.MIN_VALUE;
      public static final int ERROR = 1;
      public static final int WARNING = 2;
      public static final int INFORMATION = 3;
      public static final int DEBUG = 4;
  
      // -------------------- Internal fields --------------------
  
      protected static Writer defaultSink = new OutputStreamWriter(System.err);
  
      // registered loggers 
      protected static Hashtable loggers = new Hashtable(5);
      // default logger
      public static Logger defaultLogger = new DefaultLogger();
      static {
  	defaultLogger.setVerbosityLevel(DEBUG);
      }
        
      // Usefull for subclasses
      private static final String separator = System.getProperty("line.separator", "\n");
      public static final char[] NEWLINE=separator.toCharArray();;
  
  
      /**
       * Prints the log message on a specified logger. 
       *
       * @param	name		the name of the logger. 
       * @param	message		the message to log. 
       * @param	verbosityLevel	what type of message is this? 
       *				(WARNING/DEBUG/INFO etc)
       */
      /*
        public static void log(String logName, String message, 
  			   int verbosityLevel) 
      {
  	Logger logger = getLogger(logName);
  	if (logger != null)
  	    logger.log(message, verbosityLevel);
      }
      */
      
      /**
       * Prints the log message on a specified logger at the "default"
       * log leve: INFORMATION
       *
       * @param	name		the name of the logger. 
       * @param	message		the message to log. 
       */
      /*
        public static void log(String logName, String message)
      {
  	Logger logger = getLogger(logName);
  	if (logger != null)
  	    logger.log(message);
      }
      */
      
      /**
       * Set the default output stream that is used by all logging
       * channels. 
       * 
       * @param	w		the default output stream. 
       */
      public static void setDefaultSink(Writer w) {
  	defaultSink = w;
      }
  
      public static Logger getLogger(String name) {
  	return (Logger) loggers.get(name);
      }
  
      /**
       * Get the logger that prints to the default sink
       * (usu. System.err)
       **/
      public static Logger getDefaultLogger() {
  	return defaultLogger;
      }
  
      public static Enumeration getLoggerNames() {
  	return loggers.keys();
      }
  
      public static void putLogger(Logger logger) {	
  	loggers.put(logger.getName(), logger);	
      }
  
      public static void removeLogger(Logger logger) {
  	loggers.remove(logger.getName());
      }
  
      /**
       * Converts a Throwable to a printable stack trace, including the
       * nested root cause for a ServletException or TomcatException if
       * applicable
       * TODO: JDBCException too
       * 
       * @param t any Throwable, or ServletException, or null
       **/
      public static String throwableToString( Throwable t ) {
  	// we could use a StringManager here to get the
  	// localized translation of "Root cause:" , but
  	// since it's going into a log, no user will see
  	// it, and it's desirable that the log file is
  	// predictable, so just use English
  	return throwableToString( t, "Root cause:" );
      }
  
      public static final int MAX_THROWABLE_DEPTH=3;
      
      /**
       * Converts a Throwable to a printable stack trace, including the
       * nested root cause for a ServletException or TomcatException or
       * SQLException if applicable
       * 
       * @param t any Throwable, or ServletException, or null
       * @param rootcause localized string equivalent of "Root Cause"
       **/
      public static String throwableToString( Throwable t, String rootcause ) {
  	if (rootcause == null)
  	    rootcause = "Root Cause:";
  	StringWriter sw = new StringWriter();
  	PrintWriter w = new PrintWriter(sw);
  	printThrowable(w, t, rootcause, MAX_THROWABLE_DEPTH);
  	w.flush();
  	return sw.toString();
      }
  
      private static Object emptyObjectArray[]=new Object[0];
  
      private static void printThrowable(PrintWriter w, Throwable t, String rootcause, int depth ) {
  	if (t != null) {
  	    // XXX XXX XXX Something seems wrong - DOS, permissions. Need to
  	    // check.
  	    t.printStackTrace(w);
  
  	    // Find chained exception using few general patterns
  	    
  	    Class tC=t.getClass();
  	    Method mA[]= tC.getMethods();
  	    Method nextThrowableMethod=null;
  	    for( int i=0; i< mA.length ; i++  ) {
  		if( "getRootCause".equals( mA[i].getName() )
  		    || "getNextException".equals( mA[i].getName() )
  		    || "getException".equals( mA[i].getName() )) {
  		    // check param types
  		    Class params[]=mA[i].getParameterTypes();
  		    if( params==null || params.length==0 ) {
  			nextThrowableMethod=mA[i];
  			break;
  		    }
  		}
  	    }
  
  	    if( nextThrowableMethod != null ) {
  		try {
  		    Throwable nextT=(Throwable)nextThrowableMethod.invoke( t , emptyObjectArray );
  		    if( nextT != null ) {
  			w.println(rootcause);
  			if( depth > 0 ) {
  			    printThrowable(w, nextT, rootcause, depth-1);
  			}
  		    }
  		} catch( Exception ex ) {
  		    // ignore
  		}
  	    }
  	}
      }
      
      /**
       * General purpose nasty hack to determine if an exception can be
       * safely ignored -- specifically, if it's an IOException or
       * SocketException that is thrown in the normal course of a socket
       * closing halfway through a connection, or if it's a weird
       * unknown type of exception.  This is an intractable problem, and
       * this is a bad solution, but at least it's centralized.
       **/
      public static boolean canIgnore(Throwable t) {
  	String msg = t.getMessage();
  	if (t instanceof java.io.InterruptedIOException) {
  	    return true;
  	}
  	else if (t instanceof java.io.IOException) {
  	    // Streams throw Broken Pipe exceptions if their
  	    // underlying sockets close
  	    if( "Broken pipe".equals(msg))
  		return true;
  	}
  	else if (t instanceof java.net.SocketException) {
  	    // TCP stacks can throw SocketExceptions when the client
  	    // disconnects.  We don't want this to shut down the
  	    // endpoint, so ignore it. Is there a more robust
  	    // solution?  Should we compare the message string to
  	    // "Connection reset by peer"?
  	    return true;
  	}
  	return false;
      }
  
  
      // ----- instance (non-static) content -----
      
      protected boolean custom = true;
      protected Writer sink = defaultSink;
      String path;
      protected String name;
      
      private int level = WARNING;
  
      /**
       * Should we timestamp this log at all?
       **/
      protected boolean timestamp = true;
  
      /**
       * true = The timestamp format is raw msec-since-epoch <br>
       * false = The timestamp format is a custom string to pass to SimpleDateFormat
       **/
      protected boolean timestampRaw = false;
  
      /**
       * The timestamp format string, default is "yyyy-MM-dd hh:mm:ss"
       **/
      protected String timestampFormat = "yyyy-MM-dd hh:mm:ss";
  
      protected DateFormat timestampFormatter
  	= new FastDateFormat(new SimpleDateFormat(timestampFormat));
      
      /**
       * Is this Log usable?
       */
      public boolean isOpen() {
  	return this.sink != null;
      }
  
      /**
       * Prints the log message at the "default" log level: INFORMATION
       * 
       * @param	message		the message to log.
       */
      public final void log(String message) {
  	log(message, Logger.INFORMATION);
      }
          
      /**
       * Prints the log message.
       * 
       * @param	message		the message to log.
       * @param	verbosityLevel	what type of message is this?
       * 				(WARNING/DEBUG/INFO etc)
       */
      public final void log(String message, int verbosityLevel) {
  	log(message, null, level);
      }
  
      /**
       * Prints log message and stack trace, with verbosityLevel ERROR.
       * This makes the assumption that throwables are exceptions which
       * are errors by nature; if you disagree, you can always call
       * log(msg, t, Logger.INFORMATION) or whatever.
       *
       * @param	message		the message to log. 
       * @param t the exception that was thrown.  */
      public final void log(String message, Throwable t)
      {
  	log(message, t, ERROR);
      }
      
      /**
       * Prints log message and stack trace.
       *
       * @param	message		the message to log. 
       * @param	t		the exception that was thrown.
       * @param	verbosityLevel	what type of message is this?
       * 				(WARNING/DEBUG/INFO etc)
       */
      public final void log(String message, Throwable t, 
  			  int verbosityLevel) 
      {
  	if (matchVerbosityLevel(verbosityLevel)) {
  	    if (t == null) {
  		realLog(message);
  	    }
  	    else {
  		realLog(message, t);
  	    }
  	}
      }
  
      public boolean matchVerbosityLevel(int verbosityLevel) {
  	return verbosityLevel <= getVerbosityLevel();
      }
      
      /**
       * Subclasses implement these methods which are called by the
       * log(..) methods internally.
       *
       * @param	message		the message to log.
       */
      protected abstract void realLog(String message);
  
      /** 
       * Subclasses implement these methods which are called by the
       * log(..) methods internally. 
       *
       * @param	message		the message to log. 
       * @param	t		the exception that was thrown.
       */
      protected abstract void realLog(String message, Throwable t);
      
      /**
       * Flush the log. 
       */
      public abstract void flush();
  
  
      /**
       * Close the log. 
       */
      public synchronized void close() {
  	this.sink = null;
  	loggers.remove(getName());
      }
      
      /**
       * Get name of this log channel. 
       */
      public String getName() {
  	return this.name;
      }
  
      /**
       * Set name of this log channel.
       *
       * @param	name		Name of this logger. 
       */
      public void setName(String name) {
  	this.name = name;
  
  	// Once the name of this logger is set, we add it to the list
  	// of loggers... 
  	putLogger(this);
      }
  
      /**
       * Set the path name for the log output file.
       * 
       * @param	path		The path to the log file. 
       */
      public void setPath(String path) {
          if (File.separatorChar == '/')
              this.path = path.replace('\\', '/');
          else if (File.separatorChar == '\\')
              this.path = path.replace('/', '\\');
      }
  
      public String getPath() {
  	return path;
      }
  
      public String toString() {
  	return "Logger(" + getName() + ", " + getPath() + ")";
      }
  
      /** Open the log - will create the log file and all the parent directories.
       *  You must open the logger before use, or it will write to System.err
       */
      public void open() {
  	if (path == null) 
              return;
  	// use default sink == System.err
  	try {
  	    File file = new File(path);
  	    
  	    if (!file.exists())
  		new File(file.getParent()).mkdirs();
  	    
  	    this.sink = new FileWriter(path);
  	} catch (IOException ex) {
  	    System.err.print("Unable to open log file: "+path+"! ");
  	    System.err.println(" Using stderr as the default.");
  	    this.sink = defaultSink;
  	}
      }
  
      
  
      /**
       * Set the verbosity level for this logger. This controls how the
       * logs will be filtered. 
       *
       * @param	level		one of the verbosity level strings. 
       */
      public void setVerbosityLevel(String level) {
  	if ("warning".equalsIgnoreCase(level))
  	    this.level = WARNING;
  	else if ("fatal".equalsIgnoreCase(level))
  	    this.level = FATAL;
  	else if ("error".equalsIgnoreCase(level))
  	    this.level = ERROR;
  	else if ("information".equalsIgnoreCase(level))
  	    this.level = INFORMATION;
  	else if ("debug".equalsIgnoreCase(level))
  	    this.level = DEBUG;
      }
  
      /**
       * Set the verbosity level for this logger. This controls how the
       * logs will be filtered. 
       *
       * @param	level		one of the verbosity level codes. 
       */
      public void setVerbosityLevel(int level) {
  	this.level = level;
      }
      
      /**
       * Get the current verbosity level. 
       */
      public int getVerbosityLevel() {
  	return this.level;
      }
  
      /**
       * Do we need to time stamp this or not?
       *
       * @param	value		"yes/no" or "true/false"
       */
      public void setTimestamp(String value) {
  	if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value))
  	    timestamp = true;
  	else if ("false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value))
  	    timestamp = false;
      }
  
      public  boolean isTimestamp() {
  	return timestamp;
      }
  
      /**
       * If we are timestamping at all, what format do we use to print
       * the timestamp? See java.text.SimpleDateFormat.
       *
       * Default = "yyyy-MM-dd hh:mm:ss". Special case: "msec" => raw
       * number of msec since epoch, very efficient but not
       * user-friendly
       **/
      public void setTimestampFormat(String value)
      {
  	if (value.equalsIgnoreCase("msec"))
  	    timestampRaw = true;
  	else {
  	    timestampRaw = false;
  	    timestampFormat = value;
  	    timestampFormatter =
  		new FastDateFormat(new SimpleDateFormat(timestampFormat));
  	}
      }
      
      public String getTimestampFormat()
      {
  	if (timestampRaw)
  	    return "msec";
  	else
  	    return timestampFormat;
      }
      
  //     public void setCustomOutput( String value ) {
  // 	if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value))
  // 	    custom = true;
  // 	else if ("false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value))
  // 	    custom = false;
  //     }
  
      protected String formatTimestamp(long msec) {
  	StringBuffer buf = new StringBuffer();
  	formatTimestamp(msec, buf);
  	return buf.toString();
      }
  
      // dummy variable to make SimpleDateFormat work right
      private static java.text.FieldPosition position = new java.text.FieldPosition(DateFormat.YEAR_FIELD);
      
      protected void formatTimestamp(long msec, StringBuffer buf) {
  	if (timestamp == false)
  	    return;
  	else if (timestampRaw) {
  	    buf.append(Long.toString(msec));
  	    return;
  	}
  	else {
  	    Date d = new Date(msec);
  	    timestampFormatter.format(d, buf, position);
  	    return;
  	}
      }
      
      // ----- Logger.Helper static inner class -----
      
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/log/QueueLogger.java
  
  Index: QueueLogger.java
  ===================================================================
  /*
   * ====================================================================
   * 
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */ 
  package org.apache.tomcat.util.log;
  
  import java.io.Writer;
  import java.io.StringWriter;
  import java.io.PrintWriter;
  
  import java.util.Date;
  
  import org.apache.tomcat.util.Queue;
  
  /**
   * A real implementation of the Logger abstraction.
   * It uses a log queue, so that the caller will not
   * have to wait.
   *
   * @author Anil V (akv@eng.sun.com)
   * @since  Tomcat 3.1
   */
  public class QueueLogger extends Logger {
      /**
       * Just one daemon and one queue for all Logger instances.. 
       */
      static LogDaemon logDaemon = null;
      static Queue     logQueue  = null;
  
      public QueueLogger() {
  	if (logDaemon == null || logQueue == null) {
  	    logQueue = new Queue();
  	    logDaemon = new LogDaemon(logQueue);
  	    logDaemon.start();
  	}
      }
      
      /**
       * Adds a log message to the queue and returns immediately. The
       * logger daemon thread will pick it up later and actually print
       * it out.
       * 
       * @param	message		the message to log.
       */
      final protected void realLog(String message) {
  	realLog( message, null );
      }
      
      /**
       * Adds a log message and stack trace to the queue and returns
       * immediately. The logger daemon thread will pick it up later and
       * actually print it out. 
       *
       * @param	message		the message to log. 
       * @param	t		the exception that was thrown.
       */
      final protected void realLog(String message, Throwable t) {
  	if( timestamp )
  	    logQueue.put(new LogEntry(this,
  				      System.currentTimeMillis(),
  				      message, t));
  	else
  	    logQueue.put(new LogEntry(this,
  				      message, t));
      }
      
      /**
       * Flush the log. In a separate thread, no wait for the caller.
       */
      public void flush() {
  	logDaemon.flush();
      }
  
      public String toString() {
  	return "QueueLogger(" + getName() + ", " + getPath() + ")";
      }
      
  
      /**
       * This is an entry that is created in response to every
       * Logger.log(...) call.
       */
      public final  class LogEntry {
  	String logName;
  	long date=0;
  	String message;
  	Throwable t;
  	QueueLogger l;
  	
  	LogEntry(QueueLogger l, long date, String message, Throwable t) {
  	    this.date = date;
  	    this.message = message;
  	    this.t = t;
  	    this.l=l;
  	}
  
  	LogEntry( QueueLogger l, String message, Throwable t) {
  	    this.message = message;
  	    this.t = t;
  	    this.l=l;
  	}
  
  	public void print( StringBuffer outSB) {
  	    if (date!=0) {
  		formatTimestamp( date, outSB );
  		outSB.append(" - ");
  	    }
  		    
  	    if (message != null) 
  		outSB.append(message);
  	    
  	    if (t != null) {
  		outSB.append(" - ");
  		outSB.append(throwableToString( t ));
  	    }
  	}
      }
  }
  /**
   * The daemon thread that looks in a queue and if it is not empty
   * writes out everything in the queue to the sink.
   */
  final class LogDaemon extends Thread {
      private Queue logQueue;
      
      LogDaemon(Queue logQueue) {
  	this.logQueue = logQueue;
  	setDaemon(true);
      }
  	
      private static final char[] NEWLINE=Logger.NEWLINE;
      
      // There is only one thread, so we can reuse this
      char outBuffer[]=new char[512]; // resize
      
      // NEVER call toString() on StringBuffer!!!!!
      StringBuffer outSB = new StringBuffer();
      
      
      private void emptyQueue() {
  	do {
  	    QueueLogger.LogEntry logEntry =
  		(QueueLogger.LogEntry) logQueue.pull();
  	    QueueLogger tl=logEntry.l;
  		Writer writer=tl.sink;
  		if (writer != null) {
  		    try {
  			outSB.setLength(0);
  			
  			logEntry.print( outSB );
  			outSB.append( NEWLINE );
  			
  			int len=outSB.length();
  			if( len > outBuffer.length ) {
  			    outBuffer=new char[len];
  			}
  			outSB.getChars(0, len, outBuffer, 0);
  
  			writer.write( outBuffer, 0, len );	    
  			writer.flush();
  		    } catch (Exception ex) { // IOException
  			ex.printStackTrace(); // nowhere else to write it
  		    }
  		}
  	} while (!LogDaemon.this.logQueue.isEmpty());
      }
      
      public void run() {
  	while (true) {
  	    emptyQueue();
  	}
      }
      
      /** Flush the queue - in a separate thread, so that
  	caller doesn't have to wait
      */
      public void flush() {
  	Thread workerThread = new Thread(flusher);
  	workerThread.start();
      }
      
      Runnable flusher = new Runnable() {
  	    public void run() {
  		emptyQueue();
  	    }
  	};
  }