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:
hns 2001-08-31 15:44:50 +00:00
parent a3074265e4
commit 0b35d05b2b

View file

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