From 03e188b57ad44dce48de4242d29e1a1b6705d7e1 Mon Sep 17 00:00:00 2001 From: str4d <str4d@mail.i2p> Date: Sat, 28 May 2016 22:51:44 +0000 Subject: [PATCH] Rename LogWriter -> FileLogWriter, LogWriterBase -> LogWriter --- core/java/src/net/i2p/util/FileLogWriter.java | 218 +++++++++++++ core/java/src/net/i2p/util/LogManager.java | 2 +- core/java/src/net/i2p/util/LogWriter.java | 306 ++++++++---------- core/java/src/net/i2p/util/LogWriterBase.java | 190 ----------- 4 files changed, 358 insertions(+), 358 deletions(-) create mode 100644 core/java/src/net/i2p/util/FileLogWriter.java delete mode 100644 core/java/src/net/i2p/util/LogWriterBase.java diff --git a/core/java/src/net/i2p/util/FileLogWriter.java b/core/java/src/net/i2p/util/FileLogWriter.java new file mode 100644 index 0000000000..92babcf862 --- /dev/null +++ b/core/java/src/net/i2p/util/FileLogWriter.java @@ -0,0 +1,218 @@ +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.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +/** + * 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 FileLogWriter extends LogWriter { + // volatile as it changes on log file rotation + private volatile Writer _currentOut; + private int _rotationNum = -1; + private File _currentFile; + private long _numBytesInCurrentFile; + + private static final int MAX_DISKFULL_MESSAGES = 8; + private int _diskFullMessageCount; + + public FileLogWriter(LogManager manager) { + super(manager); + } + + /** + * File may not exist or have old logs in it if not opened yet + */ + public synchronized String currentFile() { + if (_currentFile != null) + return _currentFile.getAbsolutePath(); + String rv = getNextFile().getAbsolutePath(); + // so it doesn't increment every time we call this + _rotationNum = -1; + return rv; + } + + protected void writeRecord(LogRecord rec, String formatted) { + writeRecord(rec.getPriority(), formatted); + } + + protected synchronized void writeRecord(int priority, String val) { + if (val == null) return; + if (_currentOut == null) { + rotateFile(); + if (_currentOut == null) + return; // hosed + } + + try { + _currentOut.write(val); + // may be a little off if a lot of multi-byte chars, but unlikely + _numBytesInCurrentFile += val.length(); + } catch (Throwable t) { + if (!_write) + return; + if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES) + System.err.println("Error writing log, disk full? " + t); + //t.printStackTrace(); + } + if (_numBytesInCurrentFile >= _manager.getFileSize()) { + rotateFile(); + } + } + + /** + * @since 0.9.19 + */ + 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); + } + } + + /** + * @since 0.9.19 renamed from closeFile() + */ + 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) + * + * Caller must synch + */ + private void rotateFile() { + File f = getNextFile(); + _currentFile = f; + _numBytesInCurrentFile = 0; + File parent = f.getParentFile(); + if (parent != null) { + if (!parent.exists()) { + File sd = new SecureDirectory(parent.getAbsolutePath()); + boolean ok = sd.mkdirs(); + if (!ok) { + System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath()); + //System.exit(0); + } + } + if (!parent.isDirectory()) { + System.err.println("Cannot put the logs in a subdirectory of a plain file: " + f.getAbsolutePath()); + //System.exit(0); + } + } + closeWriter(); + try { + _currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8")); + } catch (IOException ioe) { + if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES) + System.err.println("Error creating log file [" + f.getAbsolutePath() + "]" + ioe); + } + } + + /** + * Get the next file in the rotation + * + * Caller must synch + */ + private File getNextFile() { + String pattern = _manager.getBaseLogfilename(); + File f = new File(pattern); + File base = null; + if (!f.isAbsolute()) + base = _manager.getContext().getLogDir(); + + if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) { + if (base != null) + return new File(base, pattern); + else + return f; + } + + int max = _manager.getRotationLimit(); + if (_rotationNum == -1) { + return getFirstFile(base, pattern, max); + } + + // we're in rotation, just go to the next + _rotationNum++; + if (_rotationNum > max) _rotationNum = 0; + + String newf = replace(pattern, _rotationNum); + if (base != null) + return new File(base, newf); + return new File(newf); + } + + /** + * Retrieve the first file, updating the rotation number accordingly + * + * Caller must synch + */ + private File getFirstFile(File base, String pattern, int max) { + for (int i = 0; i < max; i++) { + File f; + if (base != null) + f = new File(base, replace(pattern, i)); + else + f = new File(replace(pattern, i)); + if (!f.exists()) { + _rotationNum = i; + return f; + } + } + + // all exist, pick the oldest to replace + File oldest = null; + for (int i = 0; i < max; i++) { + File f; + if (base != null) + f = new File(base, replace(pattern, i)); + else + f = new File(replace(pattern, i)); + if (oldest == null) { + oldest = f; + } else { + if (f.lastModified() < oldest.lastModified()) { + _rotationNum = i; + oldest = f; + } + } + } + return oldest; + } + + private static final String replace(String pattern, int num) { + char c[] = pattern.toCharArray(); + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < c.length; i++) { + if ( (c[i] != '#') && (c[i] != '@') ) + buf.append(c[i]); + else + buf.append(num); + } + return buf.toString(); + } +} diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 86012c0251..d608438065 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -163,7 +163,7 @@ public class LogManager implements Flushable { // yeah, this doesn't always work, _writer should be volatile if (_writer != null) return; - _writer = new LogWriter(this); + _writer = new FileLogWriter(this); _writer.setFlushInterval(_flushInterval * 1000); // if you enable logging in I2PThread again, you MUST change this back to Thread Thread t = new I2PThread(_writer, "LogWriter"); diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java index d44bcb1b05..12571288dd 100644 --- a/core/java/src/net/i2p/util/LogWriter.java +++ b/core/java/src/net/i2p/util/LogWriter.java @@ -9,210 +9,182 @@ package net.i2p.util; * */ -import java.io.BufferedWriter; -import java.io.File; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; +import java.util.Queue; /** - * File-based log writer thread that pulls log records from the LogManager, - * writes them to the current logfile, and rotates the logs as necessary. + * 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. * + * @since 0.9.19 pulled from FileLogWriter so Android may extend */ -class LogWriter extends LogWriterBase { - // volatile as it changes on log file rotation - private volatile Writer _currentOut; - private int _rotationNum = -1; - private File _currentFile; - private long _numBytesInCurrentFile; - - private static final int MAX_DISKFULL_MESSAGES = 8; - private int _diskFullMessageCount; +abstract 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; + protected final LogManager _manager; + + protected volatile boolean _write; + private LogRecord _last; + // ms + private volatile long _flushInterval = FLUSH_INTERVAL; public LogWriter(LogManager manager) { - super(manager); + _manager = manager; + _lastReadConfig = Clock.getInstance().now(); } + public abstract String currentFile(); /** - * File may not exist or have old logs in it if not opened yet + * Write the provided LogRecord to the writer. + * @param rec the LogRecord to write. + * @param formatted a String pre-formatted from rec, may be ignored. */ - public synchronized String currentFile() { - if (_currentFile != null) - return _currentFile.getAbsolutePath(); - String rv = getNextFile().getAbsolutePath(); - // so it doesn't increment every time we call this - _rotationNum = -1; - return rv; - } + protected abstract void writeRecord(LogRecord rec, String formatted); + /** + * Write a single String verbatim to the writer. + * @param priority the level to log the line at. + * @param line the String to write. + */ + protected abstract void writeRecord(int priority, String line); + protected abstract void flushWriter(); + protected abstract void closeWriter(); - protected void writeRecord(LogRecord rec, String formatted) { - writeRecord(rec.getPriority(), formatted); + public void stopWriting() { + _write = false; } - protected synchronized void writeRecord(int priority, String val) { - if (val == null) return; - if (_currentOut == null) { - rotateFile(); - if (_currentOut == null) - return; // hosed - } + /** + * @param interval 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; + // don't bother on Android + final boolean shouldReadConfig = !SystemVersion.isAndroid(); try { - _currentOut.write(val); - // may be a little off if a lot of multi-byte chars, but unlikely - _numBytesInCurrentFile += val.length(); - } catch (Throwable t) { - if (!_write) - return; - if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES) - System.err.println("Error writing log, disk full? " + t); - //t.printStackTrace(); - } - if (_numBytesInCurrentFile >= _manager.getFileSize()) { - rotateFile(); + while (_write) { + flushRecords(); + if (_write && shouldReadConfig) + rereadConfig(); + } + } catch (RuntimeException e) { + System.err.println("Error writing the log: " + e); + e.printStackTrace(); } + closeWriter(); } - /** - * @since 0.9.19 - */ - protected void flushWriter() { + public void flushRecords() { flushRecords(true); } + + public void flushRecords(boolean shouldWait) { 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); + // 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) { + writeDupMessage(dupCount, _last); + dupCount = 0; + } + writeRecord(rec); + } + _last = rec; + } + if (dupCount > 0) { + writeDupMessage(dupCount, _last); + } + flushWriter(); + } + } catch (Throwable t) { + t.printStackTrace(); + } finally { + if (shouldWait) { + try { + synchronized (this) { + this.wait(_flushInterval); + } + } catch (InterruptedException ie) { // nop + } + } } } /** - * @since 0.9.19 renamed from closeFile() + * Write a msg with the date stamp of the last duplicate + * @since 0.9.21 */ - protected void closeWriter() { - Writer out = _currentOut; - if (out != null) { - try { - out.close(); - } catch (IOException ioe) {} - } + private void writeDupMessage(int dupCount, LogRecord lastRecord) { + String dmsg = dupMessage(dupCount, lastRecord, false); + writeRecord(lastRecord.getPriority(), dmsg); + if (_manager.getDisplayOnScreenLevel() <= lastRecord.getPriority() && _manager.displayOnScreen()) + System.out.print(dmsg); + dmsg = dupMessage(dupCount, lastRecord, true); + _manager.getBuffer().add(dmsg); + if (lastRecord.getPriority() >= Log.CRIT) + _manager.getBuffer().addCritical(dmsg); } /** - * Rotate to the next file (or the first file if this is the first call) - * - * Caller must synch + * Return a msg with the date stamp of the last duplicate + * @since 0.9.3 */ - private void rotateFile() { - File f = getNextFile(); - _currentFile = f; - _numBytesInCurrentFile = 0; - File parent = f.getParentFile(); - if (parent != null) { - if (!parent.exists()) { - File sd = new SecureDirectory(parent.getAbsolutePath()); - boolean ok = sd.mkdirs(); - if (!ok) { - System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath()); - //System.exit(0); - } - } - if (!parent.isDirectory()) { - System.err.println("Cannot put the logs in a subdirectory of a plain file: " + f.getAbsolutePath()); - //System.exit(0); - } - } - closeWriter(); - try { - _currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8")); - } catch (IOException ioe) { - if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES) - System.err.println("Error creating log file [" + f.getAbsolutePath() + "]" + ioe); - } + private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) { + String arrows = reverse ? (SystemVersion.isAndroid() ? "vvv" : "↓↓↓") : "^^^"; + return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' + + _t(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows + + LogRecordFormatter.NL; } + + private static final String BUNDLE_NAME = "net.i2p.router.web.messages"; /** - * Get the next file in the rotation - * - * Caller must synch + * gettext + * @since 0.9.3 */ - private File getNextFile() { - String pattern = _manager.getBaseLogfilename(); - File f = new File(pattern); - File base = null; - if (!f.isAbsolute()) - base = _manager.getContext().getLogDir(); - - if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) { - if (base != null) - return new File(base, pattern); - else - return f; - } - - int max = _manager.getRotationLimit(); - if (_rotationNum == -1) { - return getFirstFile(base, pattern, max); - } - - // we're in rotation, just go to the next - _rotationNum++; - if (_rotationNum > max) _rotationNum = 0; - - String newf = replace(pattern, _rotationNum); - if (base != null) - return new File(base, newf); - return new File(newf); + private String _t(int a, String b, String c) { + return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME); } - /** - * Retrieve the first file, updating the rotation number accordingly - * - * Caller must synch - */ - private File getFirstFile(File base, String pattern, int max) { - for (int i = 0; i < max; i++) { - File f; - if (base != null) - f = new File(base, replace(pattern, i)); - else - f = new File(replace(pattern, i)); - if (!f.exists()) { - _rotationNum = i; - return f; - } + private void rereadConfig() { + long now = Clock.getInstance().now(); + if (now - _lastReadConfig > CONFIG_READ_INTERVAL) { + _manager.rereadConfig(); + _lastReadConfig = now; } - - // all exist, pick the oldest to replace - File oldest = null; - for (int i = 0; i < max; i++) { - File f; - if (base != null) - f = new File(base, replace(pattern, i)); - else - f = new File(replace(pattern, i)); - if (oldest == null) { - oldest = f; - } else { - if (f.lastModified() < oldest.lastModified()) { - _rotationNum = i; - oldest = f; - } - } - } - return oldest; } - private static final String replace(String pattern, int num) { - char c[] = pattern.toCharArray(); - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < c.length; i++) { - if ( (c[i] != '#') && (c[i] != '@') ) - buf.append(c[i]); - else - buf.append(num); + 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); + } } - return buf.toString(); } } diff --git a/core/java/src/net/i2p/util/LogWriterBase.java b/core/java/src/net/i2p/util/LogWriterBase.java deleted file mode 100644 index 4eb6b89423..0000000000 --- a/core/java/src/net/i2p/util/LogWriterBase.java +++ /dev/null @@ -1,190 +0,0 @@ -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. - * - * @since 0.9.19 pulled from LogWriter so Android may extend - */ -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 priority the level to log the line at. - * @param line the String to write. - */ - protected abstract void writeRecord(int priority, String line); - protected abstract void flushWriter(); - protected abstract void closeWriter(); - - public void stopWriting() { - _write = false; - } - - /** - * @param interval 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; - // don't bother on Android - final boolean shouldReadConfig = !SystemVersion.isAndroid(); - try { - while (_write) { - flushRecords(); - if (_write && shouldReadConfig) - rereadConfig(); - } - } catch (RuntimeException 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) { - writeDupMessage(dupCount, _last); - dupCount = 0; - } - writeRecord(rec); - } - _last = rec; - } - if (dupCount > 0) { - writeDupMessage(dupCount, _last); - } - flushWriter(); - } - } catch (Throwable t) { - t.printStackTrace(); - } finally { - if (shouldWait) { - try { - synchronized (this) { - this.wait(_flushInterval); - } - } catch (InterruptedException ie) { // nop - } - } - } - } - - /** - * Write a msg with the date stamp of the last duplicate - * @since 0.9.21 - */ - private void writeDupMessage(int dupCount, LogRecord lastRecord) { - String dmsg = dupMessage(dupCount, lastRecord, false); - writeRecord(lastRecord.getPriority(), dmsg); - if (_manager.getDisplayOnScreenLevel() <= lastRecord.getPriority() && _manager.displayOnScreen()) - System.out.print(dmsg); - dmsg = dupMessage(dupCount, lastRecord, true); - _manager.getBuffer().add(dmsg); - if (lastRecord.getPriority() >= Log.CRIT) - _manager.getBuffer().addCritical(dmsg); - } - - /** - * 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 ? (SystemVersion.isAndroid() ? "vvv" : "↓↓↓") : "^^^"; - return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' + - _t(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows + - LogRecordFormatter.NL; - } - - private static final String BUNDLE_NAME = "net.i2p.router.web.messages"; - - /** - * gettext - * @since 0.9.3 - */ - private String _t(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); - } - } - } -} -- GitLab