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
Reference in a new issue