Changeset 03e188b5 for core


Ignore:
Timestamp:
May 28, 2016 10:51:44 PM (4 years ago)
Author:
str4d <str4d@…>
Branches:
master
Children:
e969213
Parents:
8807787
Message:

Rename LogWriter?FileLogWriter?, LogWriterBase?LogWriter?

Location:
core/java/src/net/i2p/util
Files:
1 added
1 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/util/LogManager.java

    r8807787 r03e188b5  
    164164        if (_writer != null)
    165165            return;
    166         _writer = new LogWriter(this);
     166        _writer = new FileLogWriter(this);
    167167        _writer.setFlushInterval(_flushInterval * 1000);
    168168        // if you enable logging in I2PThread again, you MUST change this back to Thread
  • core/java/src/net/i2p/util/LogWriter.java

    r8807787 r03e188b5  
    1010 */
    1111
    12 import java.io.BufferedWriter;
    13 import java.io.File;
    14 import java.io.IOException;
    15 import java.io.OutputStreamWriter;
    16 import java.io.Writer;
     12import java.util.Queue;
    1713
    1814/**
    19  * File-based log writer thread that pulls log records from the LogManager,
    20  * writes them to the current logfile, and rotates the logs as necessary.
     15 * Log writer thread that pulls log records from the LogManager and writes them to
     16 * the log.  This also periodically instructs the LogManager to reread its config
     17 * file.
    2118 *
     19 * @since 0.9.19 pulled from FileLogWriter so Android may extend
    2220 */
    23 class LogWriter extends LogWriterBase {
    24     // volatile as it changes on log file rotation
    25     private volatile Writer _currentOut;
    26     private int _rotationNum = -1;
    27     private File _currentFile;
    28     private long _numBytesInCurrentFile;
     21abstract class LogWriter implements Runnable {
     22    /** every 10 seconds? why? Just have the gui force a reread after a change?? */
     23    private final static long CONFIG_READ_INTERVAL = 50 * 1000;
     24    final static long FLUSH_INTERVAL = 29 * 1000;
     25    private final static long MIN_FLUSH_INTERVAL = 2*1000;
     26    private final static long MAX_FLUSH_INTERVAL = 5*60*1000;
     27    private long _lastReadConfig;
     28    protected final LogManager _manager;
    2929
    30     private static final int MAX_DISKFULL_MESSAGES = 8;
    31     private int _diskFullMessageCount;
     30    protected volatile boolean _write;
     31    private LogRecord _last;
     32    // ms
     33    private volatile long _flushInterval = FLUSH_INTERVAL;
    3234
    3335    public LogWriter(LogManager manager) {
    34         super(manager);
     36        _manager = manager;
     37        _lastReadConfig = Clock.getInstance().now();
     38    }
     39
     40    public abstract String currentFile();
     41    /**
     42     * Write the provided LogRecord to the writer.
     43     * @param rec the LogRecord to write.
     44     * @param formatted a String pre-formatted from rec, may be ignored.
     45     */
     46    protected abstract void writeRecord(LogRecord rec, String formatted);
     47    /**
     48     * Write a single String verbatim to the writer.
     49     * @param priority the level to log the line at.
     50     * @param line the String to write.
     51     */
     52    protected abstract void writeRecord(int priority, String line);
     53    protected abstract void flushWriter();
     54    protected abstract void closeWriter();
     55
     56    public void stopWriting() {
     57        _write = false;
    3558    }
    3659
    3760    /**
    38      *  File may not exist or have old logs in it if not opened yet
     61     *  @param interval ms
     62     *  @since 0.9.18
    3963     */
    40     public synchronized String currentFile() {
    41         if (_currentFile != null)
    42             return _currentFile.getAbsolutePath();
    43         String rv = getNextFile().getAbsolutePath();
    44         // so it doesn't increment every time we call this
    45         _rotationNum = -1;
    46         return rv;
     64    public void setFlushInterval(long interval) {
     65        _flushInterval = Math.min(MAX_FLUSH_INTERVAL, Math.max(MIN_FLUSH_INTERVAL, interval));
    4766    }
    4867
    49     protected void writeRecord(LogRecord rec, String formatted) {
    50         writeRecord(rec.getPriority(), formatted);
     68    public void run() {
     69        _write = true;
     70        // don't bother on Android
     71        final boolean shouldReadConfig = !SystemVersion.isAndroid();
     72        try {
     73            while (_write) {
     74                flushRecords();
     75                if (_write && shouldReadConfig)
     76                    rereadConfig();
     77            }
     78        } catch (RuntimeException e) {
     79            System.err.println("Error writing the log: " + e);
     80            e.printStackTrace();
     81        }
     82        closeWriter();
    5183    }
    5284
    53     protected synchronized void writeRecord(int priority, String val) {
    54         if (val == null) return;
    55         if (_currentOut == null) {
    56             rotateFile();
    57             if (_currentOut == null)
    58                 return; // hosed
    59         }
     85    public void flushRecords() { flushRecords(true); }
    6086
     87    public void flushRecords(boolean shouldWait) {
    6188        try {
    62             _currentOut.write(val);
    63             // may be a little off if a lot of multi-byte chars, but unlikely
    64             _numBytesInCurrentFile += val.length();
     89            // zero copy, drain the manager queue directly
     90            Queue<LogRecord> records = _manager.getQueue();
     91            if (records == null) return;
     92            if (!records.isEmpty()) {
     93                if (_last != null && _last.getDate() < _manager.getContext().clock().now() - 30*60*1000)
     94                    _last = null;
     95                LogRecord rec;
     96                int dupCount = 0;
     97                while ((rec = records.poll()) != null) {
     98                    if (_manager.shouldDropDuplicates() && rec.equals(_last)) {
     99                        dupCount++;
     100                    } else {
     101                        if (dupCount > 0) {
     102                            writeDupMessage(dupCount, _last);
     103                            dupCount = 0;
     104                        }
     105                        writeRecord(rec);
     106                    }
     107                    _last = rec;
     108                }
     109                if (dupCount > 0) {
     110                    writeDupMessage(dupCount, _last);
     111                }
     112                flushWriter();
     113            }
    65114        } catch (Throwable t) {
    66             if (!_write)
    67                 return;
    68             if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
    69                 System.err.println("Error writing log, disk full? " + t);
    70             //t.printStackTrace();
    71         }
    72         if (_numBytesInCurrentFile >= _manager.getFileSize()) {
    73             rotateFile();
     115            t.printStackTrace();
     116        } finally {
     117            if (shouldWait) {
     118                try {
     119                    synchronized (this) {
     120                        this.wait(_flushInterval);
     121                    }
     122                } catch (InterruptedException ie) { // nop
     123                }
     124            }
    74125        }
    75126    }
    76127
    77128    /**
    78      *  @since 0.9.19
     129     *  Write a msg with the date stamp of the last duplicate
     130     *  @since 0.9.21
    79131     */
    80     protected void flushWriter() {
    81         try {
    82             if (_currentOut != null)
    83                 _currentOut.flush();
    84         } catch (IOException ioe) {
    85             if (_write && ++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
    86                 System.err.println("Error writing the router log - disk full? " + ioe);
     132    private void writeDupMessage(int dupCount, LogRecord lastRecord) {
     133        String dmsg = dupMessage(dupCount, lastRecord, false);
     134        writeRecord(lastRecord.getPriority(), dmsg);
     135        if (_manager.getDisplayOnScreenLevel() <= lastRecord.getPriority() && _manager.displayOnScreen())
     136            System.out.print(dmsg);
     137        dmsg = dupMessage(dupCount, lastRecord, true);
     138        _manager.getBuffer().add(dmsg);
     139        if (lastRecord.getPriority() >= Log.CRIT)
     140            _manager.getBuffer().addCritical(dmsg);
     141    }
     142
     143    /**
     144     *  Return a msg with the date stamp of the last duplicate
     145     *  @since 0.9.3
     146     */
     147    private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) {
     148        String arrows = reverse ? (SystemVersion.isAndroid() ? "vvv" : "&darr;&darr;&darr;") : "^^^";
     149        return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' +
     150               _t(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows +
     151               LogRecordFormatter.NL;
     152    }
     153   
     154    private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
     155
     156    /**
     157     *  gettext
     158     *  @since 0.9.3
     159     */
     160    private String _t(int a, String b, String c) {
     161        return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME);
     162    }
     163
     164    private void rereadConfig() {
     165        long now = Clock.getInstance().now();
     166        if (now - _lastReadConfig > CONFIG_READ_INTERVAL) {
     167            _manager.rereadConfig();
     168            _lastReadConfig = now;
    87169        }
    88170    }
    89171
    90     /**
    91      *  @since 0.9.19 renamed from closeFile()
    92      */
    93     protected void closeWriter() {
    94         Writer out = _currentOut;
    95         if (out != null) {
    96             try {
    97                 out.close();
    98             } catch (IOException ioe) {}
     172    private void writeRecord(LogRecord rec) {
     173        String val = LogRecordFormatter.formatRecord(_manager, rec, true);
     174        writeRecord(rec, val);
     175
     176        // we always add to the console buffer, but only sometimes write to stdout
     177        _manager.getBuffer().add(val);
     178        if (rec.getPriority() >= Log.CRIT)
     179            _manager.getBuffer().addCritical(val);
     180        if (_manager.getDisplayOnScreenLevel() <= rec.getPriority()) {
     181            if (_manager.displayOnScreen()) {
     182                // wrapper and android logs already do time stamps, so reformat without the date
     183                if (_manager.getContext().hasWrapper() || SystemVersion.isAndroid())
     184                    System.out.print(LogRecordFormatter.formatRecord(_manager, rec, false));
     185                else
     186                    System.out.print(val);
     187            }
    99188        }
    100189    }
    101 
    102     /**
    103      * Rotate to the next file (or the first file if this is the first call)
    104      *
    105      * Caller must synch
    106      */
    107     private void rotateFile() {
    108         File f = getNextFile();
    109         _currentFile = f;
    110         _numBytesInCurrentFile = 0;
    111         File parent = f.getParentFile();
    112         if (parent != null) {
    113             if (!parent.exists()) {
    114                 File sd = new SecureDirectory(parent.getAbsolutePath());
    115                 boolean ok = sd.mkdirs();
    116                 if (!ok) {
    117                     System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath());
    118                     //System.exit(0);
    119                 }
    120             }
    121             if (!parent.isDirectory()) {
    122                 System.err.println("Cannot put the logs in a subdirectory of a plain file: " + f.getAbsolutePath());
    123                 //System.exit(0);
    124             }
    125         }
    126         closeWriter();
    127         try {
    128             _currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
    129         } catch (IOException ioe) {
    130             if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
    131                 System.err.println("Error creating log file [" + f.getAbsolutePath() + "]" + ioe);
    132         }
    133     }
    134 
    135     /**
    136      * Get the next file in the rotation
    137      *
    138      * Caller must synch
    139      */
    140     private File getNextFile() {
    141         String pattern = _manager.getBaseLogfilename();
    142         File f = new File(pattern);
    143         File base = null;
    144         if (!f.isAbsolute())
    145             base = _manager.getContext().getLogDir();
    146 
    147         if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) {
    148             if (base != null)
    149                 return new File(base, pattern);
    150             else
    151                 return f;
    152         }
    153        
    154         int max = _manager.getRotationLimit();
    155         if (_rotationNum == -1) {
    156             return getFirstFile(base, pattern, max);
    157         }
    158              
    159         // we're in rotation, just go to the next 
    160         _rotationNum++;
    161         if (_rotationNum > max) _rotationNum = 0;
    162 
    163         String newf = replace(pattern, _rotationNum);
    164         if (base != null)
    165             return new File(base, newf);
    166         return new File(newf);
    167     }
    168 
    169     /**
    170      * Retrieve the first file, updating the rotation number accordingly
    171      *
    172      * Caller must synch
    173      */
    174     private File getFirstFile(File base, String pattern, int max) {
    175         for (int i = 0; i < max; i++) {
    176             File f;
    177             if (base != null)
    178                 f = new File(base, replace(pattern, i));
    179             else
    180                 f = new File(replace(pattern, i));
    181             if (!f.exists()) {
    182                 _rotationNum = i;
    183                 return f;
    184             }
    185         }
    186 
    187         // all exist, pick the oldest to replace
    188         File oldest = null;
    189         for (int i = 0; i < max; i++) {
    190             File f;
    191             if (base != null)
    192                 f = new File(base, replace(pattern, i));
    193             else
    194                 f = new File(replace(pattern, i));
    195             if (oldest == null) {
    196                 oldest = f;
    197             } else {
    198                 if (f.lastModified() < oldest.lastModified()) {
    199                     _rotationNum = i;
    200                     oldest = f;
    201                 }
    202             }
    203         }
    204         return oldest;
    205     }
    206 
    207     private static final String replace(String pattern, int num) {
    208         char c[] = pattern.toCharArray();
    209         StringBuilder buf = new StringBuilder();
    210         for (int i = 0; i < c.length; i++) {
    211             if ( (c[i] != '#') && (c[i] != '@') )
    212                 buf.append(c[i]);
    213             else
    214                 buf.append(num);
    215         }
    216         return buf.toString();
    217     }
    218190}
Note: See TracChangeset for help on using the changeset viewer.