Logger now uses only one Thread for all instances.
Instead of a Vector, a Linked List is used as log buffer, which is much better for removing items at the top of the list.
This commit is contained in:
		
							parent
							
								
									a3074265e4
								
							
						
					
					
						commit
						0b35d05b2b
					
				
					 1 changed files with 129 additions and 37 deletions
				
			
		| 
						 | 
					@ -11,11 +11,14 @@ import java.text.*;
 | 
				
			||||||
 * Utility class for asynchronous logging.
 | 
					 * Utility class for asynchronous logging.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
public class Logger implements Runnable {
 | 
					public final class Logger {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Thread logger;
 | 
					    // we use one static thread for all Loggers
 | 
				
			||||||
 | 
					    static Runner runner;
 | 
				
			||||||
 | 
					    // the list of active loggers
 | 
				
			||||||
 | 
					    static ArrayList loggers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Vector entries;
 | 
					    private LinkedList entries;
 | 
				
			||||||
    private String filename;
 | 
					    private String filename;
 | 
				
			||||||
    private String dirname;
 | 
					    private String dirname;
 | 
				
			||||||
    private File dir;
 | 
					    private File dir;
 | 
				
			||||||
| 
						 | 
					@ -27,23 +30,31 @@ public class Logger implements Runnable {
 | 
				
			||||||
    private long dateLastRendered;
 | 
					    private long dateLastRendered;
 | 
				
			||||||
    private String dateCache;
 | 
					    private String dateCache;
 | 
				
			||||||
    private PrintStream out = null;
 | 
					    private PrintStream out = null;
 | 
				
			||||||
 | 
					    boolean closed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a logger for a PrintStream, such as System.out.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public Logger (PrintStream out) {
 | 
					    public Logger (PrintStream out) {
 | 
				
			||||||
	dformat = DateFormat.getInstance ();
 | 
						dformat = new SimpleDateFormat ("[yyyy/MM/dd HH:mm] ");
 | 
				
			||||||
	this.out = out;
 | 
						this.out = out;
 | 
				
			||||||
	entries = new Vector ();
 | 
						entries = new LinkedList ();
 | 
				
			||||||
	logger = new Thread (this);
 | 
						
 | 
				
			||||||
	// logger.setPriority (Thread.MIN_PRIORITY+2);
 | 
						// register this instance with static logger list
 | 
				
			||||||
	logger.start ();
 | 
						start (this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a file logger. The actual file names do have numbers appended and are
 | 
				
			||||||
 | 
					     * rotated every x bytes.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public Logger (String dirname, String filename) throws IOException {
 | 
					    public Logger (String dirname, String filename) throws IOException {
 | 
				
			||||||
	if (filename == null || dirname == null)
 | 
						if (filename == null || dirname == null)
 | 
				
			||||||
	    throw new IOException ("Logger can't use null as file or directory name");
 | 
						    throw new IOException ("Logger can't use null as file or directory name");
 | 
				
			||||||
	this.filename = filename;
 | 
						this.filename = filename;
 | 
				
			||||||
	this.dirname = dirname;
 | 
						this.dirname = dirname;
 | 
				
			||||||
	nformat = new DecimalFormat ("00000");
 | 
						nformat = new DecimalFormat ("00000");
 | 
				
			||||||
	dformat = DateFormat.getInstance ();
 | 
						dformat = new SimpleDateFormat ("[yyyy/MM/dd HH:mm] ");
 | 
				
			||||||
	dir = new File (dirname);
 | 
						dir = new File (dirname);
 | 
				
			||||||
	if (!dir.exists())
 | 
						if (!dir.exists())
 | 
				
			||||||
	    dir.mkdirs ();
 | 
						    dir.mkdirs ();
 | 
				
			||||||
| 
						 | 
					@ -51,17 +62,20 @@ public class Logger implements Runnable {
 | 
				
			||||||
	while (currentFile.exists())
 | 
						while (currentFile.exists())
 | 
				
			||||||
	    currentFile =  new File (dir, filename+nformat.format(++fileindex)+".log");
 | 
						    currentFile =  new File (dir, filename+nformat.format(++fileindex)+".log");
 | 
				
			||||||
	currentWriter = new PrintWriter (new FileWriter (currentFile), false);
 | 
						currentWriter = new PrintWriter (new FileWriter (currentFile), false);
 | 
				
			||||||
	entries = new Vector ();
 | 
						entries = new LinkedList ();
 | 
				
			||||||
	logger = new Thread (this);
 | 
						
 | 
				
			||||||
	// logger.setPriority (Thread.MIN_PRIORITY+2);
 | 
						// register this instance with static logger list
 | 
				
			||||||
	logger.start ();
 | 
						start (this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Append a message with to the log.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public void log (String msg) {
 | 
					    public void log (String msg) {
 | 
				
			||||||
	// it's enough to render the date every 15 seconds
 | 
						// it's enough to render the date every 15 seconds
 | 
				
			||||||
	if (System.currentTimeMillis () - 15000 > dateLastRendered)
 | 
						if (System.currentTimeMillis () - 15000 > dateLastRendered)
 | 
				
			||||||
	    renderDate ();
 | 
						    renderDate ();
 | 
				
			||||||
	entries.addElement (dateCache + " " + msg);
 | 
						entries.add (dateCache + msg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private synchronized void renderDate () {
 | 
					    private synchronized void renderDate () {
 | 
				
			||||||
| 
						 | 
					@ -69,34 +83,42 @@ public class Logger implements Runnable {
 | 
				
			||||||
	dateCache = dformat.format (new Date());
 | 
						dateCache = dformat.format (new Date());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This is called by the runner thread to perform actual IO.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public void run () {
 | 
					    public void run () {
 | 
				
			||||||
	while (Thread.currentThread () == logger) {
 | 
						if (entries.isEmpty ())
 | 
				
			||||||
	    try {
 | 
						    return;
 | 
				
			||||||
	        if (currentFile != null && currentFile.length() > 10000000) {
 | 
						try {
 | 
				
			||||||
	            // rotate log files each 10 megs
 | 
						    if (currentFile != null && currentFile.length() > 10000000) {
 | 
				
			||||||
	            swapFile ();
 | 
						        // rotate log files each 10 megs
 | 
				
			||||||
	        }
 | 
						        swapFile ();
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	        int l = entries.size();
 | 
					 | 
				
			||||||
	        for (int i=0; i<l; i++) {
 | 
					 | 
				
			||||||
	            Object entry = entries.elementAt (0);
 | 
					 | 
				
			||||||
	            entries.removeElementAt (0);
 | 
					 | 
				
			||||||
	            if (out != null)
 | 
					 | 
				
			||||||
	                out.println (entry.toString());
 | 
					 | 
				
			||||||
	            else
 | 
					 | 
				
			||||||
	                currentWriter.println (entry.toString());
 | 
					 | 
				
			||||||
	        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                     if (currentWriter != null)
 | 
					 | 
				
			||||||
	            currentWriter.flush ();
 | 
					 | 
				
			||||||
	        logger.sleep (1000l);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    } catch (InterruptedException ir) {
 | 
					 | 
				
			||||||
	        Thread.currentThread().interrupt ();
 | 
					 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						    int l = entries.size();
 | 
				
			||||||
 | 
						    if (out != null) {
 | 
				
			||||||
 | 
						        for (int i=0; i<l; i++) {
 | 
				
			||||||
 | 
						            String entry = (String) entries.get (0);
 | 
				
			||||||
 | 
						            entries.remove (0);
 | 
				
			||||||
 | 
						            out.println (entry);
 | 
				
			||||||
 | 
						        }
 | 
				
			||||||
 | 
						    } else {
 | 
				
			||||||
 | 
						        for (int i=0; i<l; i++) {
 | 
				
			||||||
 | 
						            String entry = (String) entries.get (0);
 | 
				
			||||||
 | 
						            entries.remove (0);
 | 
				
			||||||
 | 
						            currentWriter.println (entry);
 | 
				
			||||||
 | 
						        }
 | 
				
			||||||
 | 
						        currentWriter.flush ();
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						} catch (Exception x) {
 | 
				
			||||||
 | 
						    //
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *  Rotata log files, closing the old file and starting a new one.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private void swapFile () {
 | 
					    private void swapFile () {
 | 
				
			||||||
    	try {
 | 
					    	try {
 | 
				
			||||||
    	    currentWriter.close();
 | 
					    	    currentWriter.close();
 | 
				
			||||||
| 
						 | 
					@ -107,4 +129,74 @@ public class Logger implements Runnable {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The static start class adds a log to the list of logs and starts the
 | 
				
			||||||
 | 
					     *  runner thread if necessary.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static synchronized void start (Logger log) {
 | 
				
			||||||
 | 
						if (loggers == null) {
 | 
				
			||||||
 | 
						    loggers = new ArrayList ();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						loggers.add (log);
 | 
				
			||||||
 | 
						if (runner == null || !runner.isAlive ()) {
 | 
				
			||||||
 | 
						    runner = new Runner ();
 | 
				
			||||||
 | 
						    runner.setPriority (Thread.NORM_PRIORITY-1);
 | 
				
			||||||
 | 
						    runner.start ();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tells a log to close down
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void close () {
 | 
				
			||||||
 | 
						this.closed = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes the file writer of a log
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void closeFiles () {
 | 
				
			||||||
 | 
						if (currentWriter != null) try {
 | 
				
			||||||
 | 
						    currentWriter.close ();
 | 
				
			||||||
 | 
						} catch (Exception ignore) {}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *  The static runner class that loops through all loggers.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static class Runner extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void run () {
 | 
				
			||||||
 | 
						    while (!isInterrupted ()) {
 | 
				
			||||||
 | 
						        int l = loggers.size();
 | 
				
			||||||
 | 
						        for (int i=l-1; i>=0; i--) {
 | 
				
			||||||
 | 
						            Logger log = (Logger) loggers.get (i);
 | 
				
			||||||
 | 
						            log.run ();
 | 
				
			||||||
 | 
						            if (log.closed) {
 | 
				
			||||||
 | 
						                loggers.remove (log);
 | 
				
			||||||
 | 
						                log.closeFiles ();
 | 
				
			||||||
 | 
						            }
 | 
				
			||||||
 | 
						        }
 | 
				
			||||||
 | 
						        try {
 | 
				
			||||||
 | 
						            sleep (700);
 | 
				
			||||||
 | 
						        } catch (InterruptedException ix) {}	
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *  test main method
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void main (String[] args) throws IOException {
 | 
				
			||||||
 | 
						Logger log = new Logger (".", "testlog");
 | 
				
			||||||
 | 
						long start = System.currentTimeMillis ();
 | 
				
			||||||
 | 
						for (int i=0; i<50000; i++)
 | 
				
			||||||
 | 
						    log.log ("test log entry aasdfasdfasdfasdf");
 | 
				
			||||||
 | 
						log.log ("done: "+(System.currentTimeMillis () - start));
 | 
				
			||||||
 | 
						System.err.println (System.currentTimeMillis () - start);
 | 
				
			||||||
 | 
						System.exit (0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue