I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit b92e1ee9 authored by str4d's avatar str4d
Browse files

Split LogWriter to make Android subsititution simpler

parent 04ac54cd
No related branches found
No related tags found
No related merge requests found
......@@ -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 ? "&darr;&darr;&darr;" : "^^^";
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);
public LogWriter(LogManager manager) {
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
*
......
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 ? "&darr;&darr;&darr;" : "^^^";
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);
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment