From b92e1ee9aae45ab092f54371803140ab2251c478 Mon Sep 17 00:00:00 2001
From: str4d <str4d@mail.i2p>
Date: Mon, 2 Mar 2015 10:56:50 +0000
Subject: [PATCH] Split LogWriter to make Android subsititution simpler

---
 core/java/src/net/i2p/util/LogWriter.java     | 186 +++---------------
 core/java/src/net/i2p/util/LogWriterBase.java | 172 ++++++++++++++++
 2 files changed, 201 insertions(+), 157 deletions(-)
 create mode 100644 core/java/src/net/i2p/util/LogWriterBase.java

diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java
index 1d24e34ef4..15c60e566b 100644
--- a/core/java/src/net/i2p/util/LogWriter.java
+++ b/core/java/src/net/i2p/util/LogWriter.java
@@ -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
      *
diff --git a/core/java/src/net/i2p/util/LogWriterBase.java b/core/java/src/net/i2p/util/LogWriterBase.java
new file mode 100644
index 0000000000..7448a1862e
--- /dev/null
+++ b/core/java/src/net/i2p/util/LogWriterBase.java
@@ -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 ? "&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);
+            }
+        }
+    }
+}
-- 
GitLab