forked from I2P_Developers/i2p.i2p
Split LogWriter to make Android subsititution simpler
This commit is contained in:
@@ -14,139 +14,24 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Log writer thread that pulls log records from the LogManager, writes them to
|
||||
* the current logfile, and rotates the logs as necessary. This also periodically
|
||||
* instructs the LogManager to reread its config file.
|
||||
* File-based log writer thread that pulls log records from the LogManager,
|
||||
* writes them to the current logfile, and rotates the logs as necessary.
|
||||
*
|
||||
*/
|
||||
class LogWriter implements Runnable {
|
||||
/** every 10 seconds? why? Just have the gui force a reread after a change?? */
|
||||
private final static long CONFIG_READ_INTERVAL = 50 * 1000;
|
||||
final static long FLUSH_INTERVAL = 29 * 1000;
|
||||
private final static long MIN_FLUSH_INTERVAL = 2*1000;
|
||||
private final static long MAX_FLUSH_INTERVAL = 5*60*1000;
|
||||
private long _lastReadConfig;
|
||||
private long _numBytesInCurrentFile;
|
||||
class LogWriter extends LogWriterBase {
|
||||
// volatile as it changes on log file rotation
|
||||
private volatile Writer _currentOut;
|
||||
private int _rotationNum = -1;
|
||||
private File _currentFile;
|
||||
private final LogManager _manager;
|
||||
private long _numBytesInCurrentFile;
|
||||
|
||||
private volatile boolean _write;
|
||||
private static final int MAX_DISKFULL_MESSAGES = 8;
|
||||
private int _diskFullMessageCount;
|
||||
private LogRecord _last;
|
||||
// ms
|
||||
private volatile long _flushInterval = FLUSH_INTERVAL;
|
||||
|
||||
public LogWriter(LogManager manager) {
|
||||
_manager = manager;
|
||||
_lastReadConfig = Clock.getInstance().now();
|
||||
}
|
||||
|
||||
public void stopWriting() {
|
||||
_write = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ms
|
||||
* @since 0.9.18
|
||||
*/
|
||||
public void setFlushInterval(long interval) {
|
||||
_flushInterval = Math.min(MAX_FLUSH_INTERVAL, Math.max(MIN_FLUSH_INTERVAL, interval));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_write = true;
|
||||
try {
|
||||
// Don't rotate and open until needed
|
||||
//rotateFile();
|
||||
while (_write) {
|
||||
flushRecords();
|
||||
if (_write)
|
||||
rereadConfig();
|
||||
}
|
||||
//System.err.println("Done writing");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error writing the log: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeFile();
|
||||
}
|
||||
|
||||
public void flushRecords() { flushRecords(true); }
|
||||
|
||||
public void flushRecords(boolean shouldWait) {
|
||||
try {
|
||||
// zero copy, drain the manager queue directly
|
||||
Queue<LogRecord> records = _manager.getQueue();
|
||||
if (records == null) return;
|
||||
if (!records.isEmpty()) {
|
||||
if (_last != null && _last.getDate() < _manager.getContext().clock().now() - 30*60*1000)
|
||||
_last = null;
|
||||
LogRecord rec;
|
||||
int dupCount = 0;
|
||||
while ((rec = records.poll()) != null) {
|
||||
if (_manager.shouldDropDuplicates() && rec.equals(_last)) {
|
||||
dupCount++;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
writeRecord(dupMessage(dupCount, _last, false));
|
||||
_manager.getBuffer().add(dupMessage(dupCount, _last, true));
|
||||
dupCount = 0;
|
||||
}
|
||||
writeRecord(rec);
|
||||
}
|
||||
_last = rec;
|
||||
}
|
||||
if (dupCount > 0) {
|
||||
writeRecord(dupMessage(dupCount, _last, false));
|
||||
_manager.getBuffer().add(dupMessage(dupCount, _last, true));
|
||||
}
|
||||
try {
|
||||
if (_currentOut != null)
|
||||
_currentOut.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_write && ++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing the router log - disk full? " + ioe);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
if (shouldWait) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.wait(_flushInterval);
|
||||
}
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a msg with the date stamp of the last duplicate
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) {
|
||||
String arrows = reverse ? "↓↓↓" : "^^^";
|
||||
return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' +
|
||||
_(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows + '\n';
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
/**
|
||||
* gettext
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String _(int a, String b, String c) {
|
||||
return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME);
|
||||
super(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,34 +46,11 @@ class LogWriter implements Runnable {
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void rereadConfig() {
|
||||
long now = Clock.getInstance().now();
|
||||
if (now - _lastReadConfig > CONFIG_READ_INTERVAL) {
|
||||
_manager.rereadConfig();
|
||||
_lastReadConfig = now;
|
||||
}
|
||||
protected void writeRecord(LogRecord rec, String formatted) {
|
||||
writeRecord(formatted);
|
||||
}
|
||||
|
||||
private void writeRecord(LogRecord rec) {
|
||||
String val = LogRecordFormatter.formatRecord(_manager, rec, true);
|
||||
writeRecord(val);
|
||||
|
||||
// we always add to the console buffer, but only sometimes write to stdout
|
||||
_manager.getBuffer().add(val);
|
||||
if (rec.getPriority() >= Log.CRIT)
|
||||
_manager.getBuffer().addCritical(val);
|
||||
if (_manager.getDisplayOnScreenLevel() <= rec.getPriority()) {
|
||||
if (_manager.displayOnScreen()) {
|
||||
// wrapper log already does time stamps, so reformat without the date
|
||||
if (_manager.getContext().hasWrapper())
|
||||
System.out.print(LogRecordFormatter.formatRecord(_manager, rec, false));
|
||||
else
|
||||
System.out.print(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void writeRecord(String val) {
|
||||
protected synchronized void writeRecord(String val) {
|
||||
if (val == null) return;
|
||||
if (_currentOut == null) {
|
||||
rotateFile();
|
||||
@@ -212,6 +74,25 @@ class LogWriter implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
protected void flushWriter() {
|
||||
try {
|
||||
if (_currentOut != null)
|
||||
_currentOut.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_write && ++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing the router log - disk full? " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected void closeWriter() {
|
||||
Writer out = _currentOut;
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate to the next file (or the first file if this is the first call)
|
||||
*
|
||||
@@ -236,7 +117,7 @@ class LogWriter implements Runnable {
|
||||
//System.exit(0);
|
||||
}
|
||||
}
|
||||
closeFile();
|
||||
closeWriter();
|
||||
try {
|
||||
_currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
|
||||
} catch (IOException ioe) {
|
||||
@@ -245,15 +126,6 @@ class LogWriter implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void closeFile() {
|
||||
Writer out = _currentOut;
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next file in the rotation
|
||||
*
|
||||
|
172
core/java/src/net/i2p/util/LogWriterBase.java
Normal file
172
core/java/src/net/i2p/util/LogWriterBase.java
Normal file
@@ -0,0 +1,172 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Log writer thread that pulls log records from the LogManager and writes them to
|
||||
* the log. This also periodically instructs the LogManager to reread its config
|
||||
* file.
|
||||
*
|
||||
*/
|
||||
abstract class LogWriterBase implements Runnable {
|
||||
/** every 10 seconds? why? Just have the gui force a reread after a change?? */
|
||||
private final static long CONFIG_READ_INTERVAL = 50 * 1000;
|
||||
final static long FLUSH_INTERVAL = 29 * 1000;
|
||||
private final static long MIN_FLUSH_INTERVAL = 2*1000;
|
||||
private final static long MAX_FLUSH_INTERVAL = 5*60*1000;
|
||||
private long _lastReadConfig;
|
||||
protected final LogManager _manager;
|
||||
|
||||
protected volatile boolean _write;
|
||||
private LogRecord _last;
|
||||
// ms
|
||||
private volatile long _flushInterval = FLUSH_INTERVAL;
|
||||
|
||||
public LogWriterBase(LogManager manager) {
|
||||
_manager = manager;
|
||||
_lastReadConfig = Clock.getInstance().now();
|
||||
}
|
||||
|
||||
public abstract String currentFile();
|
||||
/**
|
||||
* Write the provided LogRecord to the writer.
|
||||
* @param rec the LogRecord to write.
|
||||
* @param formatted a String pre-formatted from rec, may be ignored.
|
||||
*/
|
||||
protected abstract void writeRecord(LogRecord rec, String formatted);
|
||||
/**
|
||||
* Write a single String verbatim to the writer.
|
||||
* @param line the String to write.
|
||||
*/
|
||||
protected abstract void writeRecord(String line);
|
||||
protected abstract void flushWriter();
|
||||
protected abstract void closeWriter();
|
||||
|
||||
public void stopWriting() {
|
||||
_write = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ms
|
||||
* @since 0.9.18
|
||||
*/
|
||||
public void setFlushInterval(long interval) {
|
||||
_flushInterval = Math.min(MAX_FLUSH_INTERVAL, Math.max(MIN_FLUSH_INTERVAL, interval));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_write = true;
|
||||
try {
|
||||
while (_write) {
|
||||
flushRecords();
|
||||
if (_write)
|
||||
rereadConfig();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error writing the log: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeWriter();
|
||||
}
|
||||
|
||||
public void flushRecords() { flushRecords(true); }
|
||||
|
||||
public void flushRecords(boolean shouldWait) {
|
||||
try {
|
||||
// zero copy, drain the manager queue directly
|
||||
Queue<LogRecord> records = _manager.getQueue();
|
||||
if (records == null) return;
|
||||
if (!records.isEmpty()) {
|
||||
if (_last != null && _last.getDate() < _manager.getContext().clock().now() - 30*60*1000)
|
||||
_last = null;
|
||||
LogRecord rec;
|
||||
int dupCount = 0;
|
||||
while ((rec = records.poll()) != null) {
|
||||
if (_manager.shouldDropDuplicates() && rec.equals(_last)) {
|
||||
dupCount++;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
writeRecord(dupMessage(dupCount, _last, false));
|
||||
_manager.getBuffer().add(dupMessage(dupCount, _last, true));
|
||||
dupCount = 0;
|
||||
}
|
||||
writeRecord(rec);
|
||||
}
|
||||
_last = rec;
|
||||
}
|
||||
if (dupCount > 0) {
|
||||
writeRecord(dupMessage(dupCount, _last, false));
|
||||
_manager.getBuffer().add(dupMessage(dupCount, _last, true));
|
||||
}
|
||||
flushWriter();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
if (shouldWait) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.wait(_flushInterval);
|
||||
}
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a msg with the date stamp of the last duplicate
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) {
|
||||
String arrows = reverse ? "↓↓↓" : "^^^";
|
||||
return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' +
|
||||
_(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows + '\n';
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
/**
|
||||
* gettext
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String _(int a, String b, String c) {
|
||||
return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
private void rereadConfig() {
|
||||
long now = Clock.getInstance().now();
|
||||
if (now - _lastReadConfig > CONFIG_READ_INTERVAL) {
|
||||
_manager.rereadConfig();
|
||||
_lastReadConfig = now;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRecord(LogRecord rec) {
|
||||
String val = LogRecordFormatter.formatRecord(_manager, rec, true);
|
||||
writeRecord(rec, val);
|
||||
|
||||
// we always add to the console buffer, but only sometimes write to stdout
|
||||
_manager.getBuffer().add(val);
|
||||
if (rec.getPriority() >= Log.CRIT)
|
||||
_manager.getBuffer().addCritical(val);
|
||||
if (_manager.getDisplayOnScreenLevel() <= rec.getPriority()) {
|
||||
if (_manager.displayOnScreen()) {
|
||||
// wrapper and android logs already do time stamps, so reformat without the date
|
||||
if (_manager.getContext().hasWrapper() || SystemVersion.isAndroid())
|
||||
System.out.print(LogRecordFormatter.formatRecord(_manager, rec, false));
|
||||
else
|
||||
System.out.print(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user