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