* Logging:

- Limit buffer size; block and wakeup writer when full
      - Limit errors written to system log
      - Add method to force a log below the current level
This commit is contained in:
zzz
2010-11-30 15:07:45 +00:00
parent 9147fddb8e
commit 45b8d8b6b5
3 changed files with 44 additions and 19 deletions

View File

@ -117,6 +117,17 @@ public class Log {
} }
} }
/**
* Always log this messge with the given priority, ignoring current minimum priority level.
* This allows an INFO message about changing port numbers, for example, to always be logged.
* @since 0.8.2
*/
public void logAlways(int priority, String msg) {
_manager.addRecord(new LogRecord(_class, _name,
Thread.currentThread().getName(), priority,
msg, null));
}
public void debug(String msg) { public void debug(String msg) {
log(DEBUG, msg); log(DEBUG, msg);
} }

View File

@ -23,6 +23,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.TreeMap; import java.util.TreeMap;
@ -78,6 +79,9 @@ public class LogManager {
/** the config file */ /** the config file */
private File _locationFile; private File _locationFile;
/** max to LogRecords to buffer in memory before we start blocking */
private static final int MAX_BUFFER = 1024;
/** Ordered list of LogRecord elements that have not been written out yet */ /** Ordered list of LogRecord elements that have not been written out yet */
private final LinkedBlockingQueue<LogRecord> _records; private final LinkedBlockingQueue<LogRecord> _records;
/** List of explicit overrides of log levels (LogLimit objects) */ /** List of explicit overrides of log levels (LogLimit objects) */
@ -119,7 +123,7 @@ public class LogManager {
public LogManager(I2PAppContext context) { public LogManager(I2PAppContext context) {
_displayOnScreen = true; _displayOnScreen = true;
_alreadyNoticedMissingConfig = false; _alreadyNoticedMissingConfig = false;
_records = new LinkedBlockingQueue(); _records = new LinkedBlockingQueue(MAX_BUFFER);
_limits = new ConcurrentHashSet(); _limits = new ConcurrentHashSet();
_logs = new ConcurrentHashMap(128); _logs = new ConcurrentHashMap(128);
_defaultLimit = Log.ERROR; _defaultLimit = Log.ERROR;
@ -221,25 +225,26 @@ public class LogManager {
} }
/** /**
* Used by Log to add records to the queue * Used by Log to add records to the queue.
* * This is generally nonblocking and unsyncrhonized but may block when under
* massive logging load as a way of throttling logging threads.
*/ */
void addRecord(LogRecord record) { void addRecord(LogRecord record) {
if ((!_context.isRouterContext()) && _writer == null) if ((!_context.isRouterContext()) && _writer == null)
startLogWriter(); startLogWriter();
_records.offer(record); boolean success = _records.offer(record);
/**** don't burden the logging thread with counting if (!success) {
int numRecords = _records.size();
if (numRecords > 100) {
// the writer waits 10 seconds *or* until we tell them to wake up // the writer waits 10 seconds *or* until we tell them to wake up
// before rereading the config and writing out any log messages // before rereading the config and writing out any log messages
synchronized (_writer) { synchronized (_writer) {
_writer.notifyAll(); _writer.notifyAll();
} }
// block as a way of slowing down out-of-control loggers (a little)
try {
_records.put(record);
} catch (InterruptedException ie) {}
} }
****/
} }
/** /**
@ -605,10 +610,13 @@ public class LogManager {
return buf.toString(); return buf.toString();
} }
List<LogRecord> _removeAll() { /**
List<LogRecord> vals = new LinkedList(); * Zero-copy.
_records.drainTo(vals); * For the LogWriter
return vals; * @since 0.8.2
*/
Queue<LogRecord> getQueue() {
return _records;
} }
public char[] getFormat() { public char[] getFormat() {

View File

@ -15,7 +15,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.util.List; import java.util.Queue;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
@ -38,6 +38,8 @@ class LogWriter implements Runnable {
private LogManager _manager; private LogManager _manager;
private boolean _write; private boolean _write;
private static final int MAX_DISKFULL_MESSAGES = 8;
private int _diskFullMessageCount;
private LogWriter() { // nop private LogWriter() { // nop
} }
@ -61,7 +63,7 @@ class LogWriter implements Runnable {
} }
//System.err.println("Done writing"); //System.err.println("Done writing");
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error writing the logs: " + e.getMessage()); System.err.println("Error writing the log: " + e);
e.printStackTrace(); e.printStackTrace();
} }
closeFile(); closeFile();
@ -70,16 +72,19 @@ class LogWriter implements Runnable {
public void flushRecords() { flushRecords(true); } public void flushRecords() { flushRecords(true); }
public void flushRecords(boolean shouldWait) { public void flushRecords(boolean shouldWait) {
try { try {
List<LogRecord> records = _manager._removeAll(); // zero copy, drain the manager queue directly
Queue<LogRecord> records = _manager.getQueue();
if (records == null) return; if (records == null) return;
if (!records.isEmpty()) { if (!records.isEmpty()) {
for (LogRecord rec : records) { LogRecord rec;
while ((rec = records.poll()) != null) {
writeRecord(rec); writeRecord(rec);
} }
try { try {
_currentOut.flush(); _currentOut.flush();
} catch (IOException ioe) { } catch (IOException ioe) {
System.err.println("Error writing the router log"); if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
System.err.println("Error writing the router log - disk full? " + ioe);
} }
} }
} catch (Throwable t) { } catch (Throwable t) {
@ -138,7 +143,8 @@ class LogWriter implements Runnable {
} catch (Throwable t) { } catch (Throwable t) {
if (!_write) if (!_write)
return; return;
System.err.println("Error writing log, disk full? " + t); if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
System.err.println("Error writing log, disk full? " + t);
//t.printStackTrace(); //t.printStackTrace();
} }
if (_numBytesInCurrentFile >= _manager.getFileSize()) { if (_numBytesInCurrentFile >= _manager.getFileSize()) {