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...@apache.org on 2001/04/21 20:23:55 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util/qlog LogDaemon.java LogEntry.java Logger.java QueueLogger.java

costin      01/04/21 11:23:55

  Modified:    src/share/org/apache/tomcat/modules/config LogSetter.java
               src/share/org/apache/tomcat/util/qlog LogEntry.java
                        Logger.java QueueLogger.java
  Added:       src/share/org/apache/tomcat/util/qlog LogDaemon.java
  Log:
  Work on 1418, plus some code improvements.
  
  - the background thread that writes to log files is a separate class, directly
  controled by the log module, with a clear lifecycle and managing it's own
  resources.
  
  - The LogEntry is now recyclable
  - A pool is used for LogEntries
  
  - small fixes ( the super.sink is used instead of redefining it with a different type)
  - if the log daemon is stopped, fall back to sync logging ( without losing any log)
  - less "static", explicit object lifecycle ( static fields are a good hack, but
  take away flexibility )
  
  - less garbage and string waste
  
  Revision  Changes    Path
  1.11      +66 -29    jakarta-tomcat/src/share/org/apache/tomcat/modules/config/LogSetter.java
  
  Index: LogSetter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/config/LogSetter.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- LogSetter.java	2001/03/09 21:39:32	1.10
  +++ LogSetter.java	2001/04/21 18:23:54	1.11
  @@ -201,6 +201,13 @@
   	    cm.setNote("tc.LogManager", logManager);
   	    Log.setLogManager( logManager );
   	}
  +
  +	LogDaemon logDaemon=(LogDaemon)cm.getNote("tc.LogDaemon");
  +	if( logDaemon==null ) {
  +	    logDaemon=new LogDaemon();
  +	    cm.setNote( "tc.LogDaemon", logDaemon );
  +	    logDaemon.start();
  +	}
   	
   	if( name==null ) {
   	    if( servletLogger )
  @@ -225,11 +232,69 @@
   	    name=name +  "/"  + ctx.getId();
   	}
   
  +	createLogger(logManager, logDaemon );
  +	
  +    }
  +
  +    public void engineInit( ContextManager cm )
  +	throws TomcatException
  +    {
  +	// make sure it's started
  +	LogDaemon logDaemon=(LogDaemon)cm.getNote("tc.LogDaemon");
  +	logDaemon.start();
  +    }
  +
  +    public void engineShutdown(ContextManager cm)
  +	throws TomcatException
  +    {
  +	if( getContext() != null )
  +	    return;
  +	
  +	cm.getLog().flush();
  +	// engineShutdown shouldn't be called on local modules anyway !
  +
  +	LogDaemon logDaemon=(LogDaemon)cm.getNote("tc.LogDaemon");
  +	if( logDaemon!=null ) {
  +	    try{ 
  +		logDaemon.stop();
  +	    } catch( Exception ex ) {
  +		ex.printStackTrace();
  +	    }
  +	    //	    cm.setNote( "tc.LogDaemon", null );
  +	}
  +
  +    }
  +
  +
  +
  +    
  +    /** Set default ServletLog for Context if necessary
  +     */
  +
  +    public void addContext( ContextManager cm, Context ctx )
  +	throws TomcatException
  +    {
  +	if( "org/apache/tomcat/facade".equals( name ) &&
  +		    ctx.getServletLog() == null ) {
  +	    ctx.setServletLog( Log.getLog( name, ctx.getId() ) );
  +	}
  +    }
  +
  +    /** Adapter and registry for QueueLoggers
  +     */
  +    static class TomcatLogManager extends LogManager {
  +
  +
  +    }
  +
  +    
  +    private void createLogger(LogManager logManager, LogDaemon logDaemon) {
  +	
   	if( debug>0) 
   	    log( "Constructing logger " + name + " " + path + " " + ctx );
   	
  -	// construct a queue logger
   	QueueLogger ql=new QueueLogger();
  +	ql.setLogDaemon( logDaemon );
   	if( ! timestamps )
   	    ql.setTimestamp( "false" );
   	if( tsFormat!=null )
  @@ -258,33 +323,5 @@
   		ctx.setLog( Log.getLog( name, ctx.getId() ) );
   	    }
   	}  
  -
       }
  -
  -    /** Set default ServletLog for Context if necessary
  -     */
  -
  -    public void addContext( ContextManager cm, Context ctx )
  -	throws TomcatException
  -    {
  -	if( "org/apache/tomcat/facade".equals( name ) &&
  -		    ctx.getServletLog() == null ) {
  -	    ctx.setServletLog( Log.getLog( name, ctx.getId() ) );
  -	}
  -    }
  -
  -    /** Adapter and registry for QueueLoggers
  -     */
  -    static class TomcatLogManager extends LogManager {
  -
  -	void addChannel( String name, Log log ) {
  -	    
  -	}
  -
  -    }
  -    
  -
  -    
  -    // XXX Flush the buffers on shutdown !!!!!!
  -
   }
  
  
  
  1.2       +22 -11    jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/LogEntry.java
  
  Index: LogEntry.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/LogEntry.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- LogEntry.java	2001/03/02 04:11:51	1.1
  +++ LogEntry.java	2001/04/21 18:23:54	1.2
  @@ -72,30 +72,44 @@
   public final  class LogEntry {
       String logName;
       long date=0;
  +    String prefix;
       String message;
       Throwable t;
       QueueLogger l;
  +
  +    LogEntry(QueueLogger l) {
  +	this.l=l;
  +    }
  +    
  +    QueueLogger getLogger() {
  +	return l;
  +    }
       
  -    LogEntry(QueueLogger l, long date, String message, Throwable t) {
  +
  +    void setDate(long date ) {
   	this.date = date;
  -	this.message = message;
  -	this.t = t;
  -	this.l=l;
  +    }
  +    void setPrefix( String prefix ) {
  +	this.prefix=prefix;
       }
       
  -    LogEntry( QueueLogger l, String message, Throwable t) {
  +    void setMessage( String message ) {
   	this.message = message;
  +    }
  +    void setThrowable( Throwable t) {
   	this.t = t;
  -	this.l=l;
       }
  -
  +    
       // XXX should move to LogFormat !!!
       public void print( StringBuffer outSB) {
   	if (date!=0) {
   	    l.formatTimestamp( date, outSB );
   	    outSB.append(" - ");
   	}
  -	
  +	if (prefix != null) {
  +	    outSB.append(prefix).append( ": ");
  +	}
  +
   	if (message != null) 
   	    outSB.append(message);
   	
  @@ -104,7 +118,4 @@
   	    outSB.append(l.throwableToString( t ));
   	}
       }
  -    
  -    
  -
   }
  
  
  
  1.2       +8 -44     jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/Logger.java
  
  Index: Logger.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/Logger.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Logger.java	2001/03/02 04:11:51	1.1
  +++ Logger.java	2001/04/21 18:23:54	1.2
  @@ -83,7 +83,8 @@
   public abstract class Logger extends LogHandler {
       // -------------------- Internal fields --------------------
   
  -    protected static Writer defaultSink = new OutputStreamWriter(System.err);
  +    protected static PrintWriter defaultSink =
  +	new PrintWriter( new OutputStreamWriter(System.err));
   
       protected long day;
       
  @@ -99,14 +100,15 @@
        * @param	w		the default output stream.
        */
       public static void setDefaultSink(Writer w) {
  -	defaultSink = w;
  +	if( w!=null )
  +	    defaultSink = new PrintWriter(w);
       }
   
   
       // ----- instance (non-static) content -----
       
       protected boolean custom = true;
  -    protected Writer sink = defaultSink;
  +    //    protected Writer sink = defaultSink;
       protected String path;
       
       /**
  @@ -129,44 +131,6 @@
       protected DateFormat timestampFormatter
   	= new FastDateFormat(new SimpleDateFormat(timestampFormat));
   
  -    /**
  -     * 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 prefix, String message, Throwable t,
  -			  int verbosityLevel)
  -    {
  -	if (prefix != null) {
  -	    message = prefix + ": " + message;
  -	}
  -
  -	if (verbosityLevel <= getVerbosityLevel()) {
  -            // check wheter we are logging to a file
  -            if (path!= null){
  -                // If the date has changed, switch log files
  -                if (day!=getDay(System.currentTimeMillis())) {
  -                    synchronized (this) {
  -                        close();
  -                        open();
  -                    }
  -                }
  -            }
  -	    realLog(message,t);
  -	}
  -    }
  -
  -    /** 
  -     * 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);
   
       /**
        * Set the path name for the log output file.
  @@ -200,7 +164,7 @@
               file=new File(logName);
   	    if (!file.exists())
   		new File(file.getParent()).mkdirs();
  -	    this.sink = new FileWriter(logName);
  +	    this.sink = new PrintWriter( new FileWriter(logName));
   	} catch (IOException ex) {
   	    System.err.print("Unable to open log file: "+path+"! ");
   	    System.err.println(" Using stderr as the default.");
  @@ -327,7 +291,7 @@
       static final String START_FORMAT="${";
       static final String END_FORMAT="}";
   
  -    private String getDatePrefix(long millis,String format) {
  +    protected String getDatePrefix(long millis,String format) {
           try{
               int pos=format.indexOf(Logger.START_FORMAT);
               int lpos=format.lastIndexOf(Logger.END_FORMAT);
  @@ -343,7 +307,7 @@
           return format;
       }
   
  -    private long getDay(long millis){
  +    protected long getDay(long millis){
           return (millis+TimeZone.getDefault().getRawOffset()) /
   	    ( 24*60*60*1000 );
       }
  
  
  
  1.2       +95 -86    jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/QueueLogger.java
  
  Index: QueueLogger.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/QueueLogger.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- QueueLogger.java	2001/03/02 04:11:51	1.1
  +++ QueueLogger.java	2001/04/21 18:23:55	1.2
  @@ -62,6 +62,7 @@
   
   import java.util.Date;
   
  +import org.apache.tomcat.util.collections.SimplePool;
   import org.apache.tomcat.util.collections.Queue;
   
   /**
  @@ -73,20 +74,18 @@
    * @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;
  +    // will be shared by all loggers
  +    private LogDaemon logDaemon = null;
  +    // one pool per QueueLogger
  +    private SimplePool pool=new SimplePool();
   
       public QueueLogger() {
  -	if (logDaemon == null || logQueue == null) {
  -	    logQueue = new Queue();
  -	    logDaemon = new LogDaemon(logQueue);
  -	    logDaemon.start();
  -	}
       }
  -    
  +
  +    public void setLogDaemon(LogDaemon ld ) {
  +	logDaemon=ld;
  +    }
  +
       /**
        * Adds a log message and stack trace to the queue and returns
        * immediately. The logger daemon thread will pick it up later and
  @@ -94,93 +93,103 @@
        *
        * @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)
        */
  -    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));
  +    public void log(String prefix, String message, Throwable t,
  +		    int verbosityLevel)
  +    {
  +	//	System.out.println("XXXZ " + logDaemon +  " " + message  );
  +	if( ! logDaemon.isStarted() ) {
  +	    System.out.println("SUPER " + logDaemon +  " " + message  );
  +	    super.log( prefix, message, t , verbosityLevel );
  +	    return;
  +	}
  +	
  +	if (verbosityLevel <= getVerbosityLevel()) {
  +            // check wheter we are logging to a file
  +            if (path!= null){
  +                // If the date has changed, switch log files
  +                if (day!=getDay(System.currentTimeMillis())) {
  +                    synchronized (this) {
  +                        close();
  +                        open();
  +                    }
  +                }
  +            }
  +
  +	    LogEntry entry=(LogEntry)pool.get();
  +	    if( entry == null ) {
  +		entry=new LogEntry(this);
  +	    }
  +	    
  +	    if( timestamp ) {
  +		entry.setDate(System.currentTimeMillis());
  +	    } else {
  +		entry.setDate( 0 );
  +	    }
  +	    entry.setPrefix( prefix );
  +	    entry.setMessage( message );
  +	    entry.setThrowable( t );
  +	    logDaemon.add(entry);
  +	}
       }
       
  -    /**
  -     * Flush the log. In a separate thread, no wait for the caller.
  -     */
  +    /** Flush the queue - in a separate thread, so that
  +	caller doesn't have to wait
  +    */
       public void flush() {
  -	logDaemon.flush();
  +	// we need to wait for the log thread to finish, there is
  +	// nothing special we can do ( writing will interfere with the
  +	// log thread, which logs as soon as it gets an entry )
  +	//emptyQueue();
       }
  -
  -}
  -
  -/**
  - * 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;
  +    // 	Thread workerThread = new Thread(flusher);
  +    // 	workerThread.start();
  +    //     Runnable flusher = new Runnable() {
  +    // 	    public void run() {
  +    // 		QueueLogger.emptyQueue();
  +    // 	    }};
       
  +    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 {
  -	    LogEntry logEntry =
  -		(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();
  +    /** Produce output for a log entry, and then recycle it.
  +	This method is called from a single thread ( the log daemon )
  +     */
  +    void log(LogEntry logEntry) {
  +	if( logEntry==null ) {
  +	    System.out.println("Null log entry ");
  +	    return;
   	}
  -    }
  -    
  -    /** 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();
  +	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);
  +	    if (sink != null) {
  +		sink.write( outBuffer, 0, len );	    
  +		sink.flush();
  +		//System.out.print(sink + " "  + new String(outBuffer,0,len) );
  +	    } else {
  +		System.out.println("No writer ");
  +		System.out.print(new String(outBuffer,0,len) );
  +	    }
  +	} catch (Exception ex) { // IOException
  +	    ex.printStackTrace(); // nowhere else to write it
  +	}
  +	pool.put( logEntry );
  +    }
  +
  +
   }
   
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/qlog/LogDaemon.java
  
  Index: LogDaemon.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.qlog;
  
  import java.io.Writer;
  import java.io.StringWriter;
  import java.io.PrintWriter;
  
  import java.util.Date;
  
  import org.apache.tomcat.util.collections.SimplePool;
  import org.apache.tomcat.util.collections.Queue;
  
  /**
   * The daemon thread that looks in a queue and if it is not empty
   * writes out everything in the queue to the sink.
   */
  public final class LogDaemon implements Runnable {
      private boolean shouldStop=false;
      private Thread  logDaemonThread = null;
      private Queue   logQueue  = null;
      
      public LogDaemon() {
      }
  
      public void start() {
  	logQueue = new Queue();
  	logDaemonThread=new Thread(this);
  	logDaemonThread.setName("QueueLogDaemon");
  	// Don't set it as daemon - we don't want tomcat to exit 
  	//	    logDaemonThread.setDaemon(true);
  	shouldStop=false;
  	logDaemonThread.start();
      }
  
  
      
      public void stop() {
  	if( shouldStop ) return;
  	shouldStop=true;
  	// wait for it to finish
  	logQueue.stop(); // unblock
  	logDaemonThread=null;
  	logQueue=null;
      }
  
      public boolean isStarted() {
  	return logDaemonThread!=null;
      }
      
      public void add(LogEntry logE ) {
  	if( logQueue!=null ) {
  	    logQueue.put( logE );
  	} else {
  	    // We're not started, do it synchronously
  	    logE.getLogger().log( logE );
  	}
      }
      
      public void run() {
  	while (true) {
  	    // Will block !
  	    LogEntry logEntry =
  		(LogEntry)logQueue.pull();
  	    if( shouldStop ) return;
  	    logEntry.getLogger().log( logEntry );
  	    if( shouldStop ) return;
  	}
      }
      
  }