diff --git a/core/java/src/net/i2p/util/Log.java b/core/java/src/net/i2p/util/Log.java index 922df7885..725b389e0 100644 --- a/core/java/src/net/i2p/util/Log.java +++ b/core/java/src/net/i2p/util/Log.java @@ -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) { log(DEBUG, msg); } diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 54a3b9e13..49eaf291f 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -23,6 +23,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Queue; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; @@ -78,6 +79,9 @@ public class LogManager { /** the config file */ 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 */ private final LinkedBlockingQueue _records; /** List of explicit overrides of log levels (LogLimit objects) */ @@ -119,7 +123,7 @@ public class LogManager { public LogManager(I2PAppContext context) { _displayOnScreen = true; _alreadyNoticedMissingConfig = false; - _records = new LinkedBlockingQueue(); + _records = new LinkedBlockingQueue(MAX_BUFFER); _limits = new ConcurrentHashSet(); _logs = new ConcurrentHashMap(128); _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) { if ((!_context.isRouterContext()) && _writer == null) startLogWriter(); - _records.offer(record); - /**** don't burden the logging thread with counting - int numRecords = _records.size(); - - if (numRecords > 100) { + boolean success = _records.offer(record); + if (!success) { // the writer waits 10 seconds *or* until we tell them to wake up // before rereading the config and writing out any log messages synchronized (_writer) { _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(); } - List _removeAll() { - List vals = new LinkedList(); - _records.drainTo(vals); - return vals; + /** + * Zero-copy. + * For the LogWriter + * @since 0.8.2 + */ + Queue getQueue() { + return _records; } public char[] getFormat() { diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java index b4d6e4395..d1970b745 100644 --- a/core/java/src/net/i2p/util/LogWriter.java +++ b/core/java/src/net/i2p/util/LogWriter.java @@ -15,7 +15,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; -import java.util.List; +import java.util.Queue; import net.i2p.I2PAppContext; @@ -38,6 +38,8 @@ class LogWriter implements Runnable { private LogManager _manager; private boolean _write; + private static final int MAX_DISKFULL_MESSAGES = 8; + private int _diskFullMessageCount; private LogWriter() { // nop } @@ -61,7 +63,7 @@ class LogWriter implements Runnable { } //System.err.println("Done writing"); } catch (Exception e) { - System.err.println("Error writing the logs: " + e.getMessage()); + System.err.println("Error writing the log: " + e); e.printStackTrace(); } closeFile(); @@ -70,16 +72,19 @@ class LogWriter implements Runnable { public void flushRecords() { flushRecords(true); } public void flushRecords(boolean shouldWait) { try { - List records = _manager._removeAll(); + // zero copy, drain the manager queue directly + Queue records = _manager.getQueue(); if (records == null) return; if (!records.isEmpty()) { - for (LogRecord rec : records) { + LogRecord rec; + while ((rec = records.poll()) != null) { writeRecord(rec); } try { _currentOut.flush(); } 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) { @@ -138,7 +143,8 @@ class LogWriter implements Runnable { } catch (Throwable t) { if (!_write) 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(); } if (_numBytesInCurrentFile >= _manager.getFileSize()) {