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.
|
||||
*/
|
||||
|
||||
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 dirname;
|
||||
private File dir;
|
||||
|
@ -27,23 +30,31 @@ public class Logger implements Runnable {
|
|||
private long dateLastRendered;
|
||||
private String dateCache;
|
||||
private PrintStream out = null;
|
||||
boolean closed = false;
|
||||
|
||||
/**
|
||||
* Create a logger for a PrintStream, such as System.out.
|
||||
*/
|
||||
public Logger (PrintStream out) {
|
||||
dformat = DateFormat.getInstance ();
|
||||
dformat = new SimpleDateFormat ("[yyyy/MM/dd HH:mm] ");
|
||||
this.out = out;
|
||||
entries = new Vector ();
|
||||
logger = new Thread (this);
|
||||
// logger.setPriority (Thread.MIN_PRIORITY+2);
|
||||
logger.start ();
|
||||
entries = new LinkedList ();
|
||||
|
||||
// register this instance with static logger list
|
||||
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 {
|
||||
if (filename == null || dirname == null)
|
||||
throw new IOException ("Logger can't use null as file or directory name");
|
||||
this.filename = filename;
|
||||
this.dirname = dirname;
|
||||
nformat = new DecimalFormat ("00000");
|
||||
dformat = DateFormat.getInstance ();
|
||||
dformat = new SimpleDateFormat ("[yyyy/MM/dd HH:mm] ");
|
||||
dir = new File (dirname);
|
||||
if (!dir.exists())
|
||||
dir.mkdirs ();
|
||||
|
@ -51,17 +62,20 @@ public class Logger implements Runnable {
|
|||
while (currentFile.exists())
|
||||
currentFile = new File (dir, filename+nformat.format(++fileindex)+".log");
|
||||
currentWriter = new PrintWriter (new FileWriter (currentFile), false);
|
||||
entries = new Vector ();
|
||||
logger = new Thread (this);
|
||||
// logger.setPriority (Thread.MIN_PRIORITY+2);
|
||||
logger.start ();
|
||||
entries = new LinkedList ();
|
||||
|
||||
// register this instance with static logger list
|
||||
start (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a message with to the log.
|
||||
*/
|
||||
public void log (String msg) {
|
||||
// it's enough to render the date every 15 seconds
|
||||
if (System.currentTimeMillis () - 15000 > dateLastRendered)
|
||||
renderDate ();
|
||||
entries.addElement (dateCache + " " + msg);
|
||||
entries.add (dateCache + msg);
|
||||
}
|
||||
|
||||
private synchronized void renderDate () {
|
||||
|
@ -69,34 +83,42 @@ public class Logger implements Runnable {
|
|||
dateCache = dformat.format (new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the runner thread to perform actual IO.
|
||||
*/
|
||||
public void run () {
|
||||
while (Thread.currentThread () == logger) {
|
||||
try {
|
||||
if (currentFile != null && currentFile.length() > 10000000) {
|
||||
// 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 ();
|
||||
if (entries.isEmpty ())
|
||||
return;
|
||||
try {
|
||||
if (currentFile != null && currentFile.length() > 10000000) {
|
||||
// rotate log files each 10 megs
|
||||
swapFile ();
|
||||
}
|
||||
|
||||
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 () {
|
||||
try {
|
||||
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
Reference in a new issue