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 [EMAIL PROTECTED]
   *
   * 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;
        }
      }
      
  }
  
  
  
  

Reply via email to