Util: Add option to gzip router logs

Primarily for devs. No UI.
remove shutdown hook ID
This commit is contained in:
zzz
2022-08-28 15:07:30 -04:00
parent b25c207e9a
commit 3a4bfc9c07
2 changed files with 114 additions and 10 deletions

View File

@@ -9,11 +9,19 @@ package net.i2p.util;
*
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.zip.GZIPOutputStream;
import net.i2p.data.DataHelper;
/**
* File-based log writer thread that pulls log records from the LogManager,
@@ -93,12 +101,31 @@ class FileLogWriter extends LogWriter {
* @since 0.9.19 renamed from closeFile()
*/
protected void closeWriter() {
closeWriter(_currentFile, false);
}
/**
* Gzip the closed file
*
* @param threadGzipper if true, spin off a thread
* @since 0.9.55
*/
private void closeWriter(File currentFile, boolean threadGzipper) {
Writer out = _currentOut;
if (out != null) {
try {
out.close();
} catch (IOException ioe) {}
}
if (_manager.shouldGzip() && currentFile != null && currentFile.length() >= _manager.getMinGzipSize()) {
Thread gzipper = new Gzipper(currentFile);
if (threadGzipper) {
gzipper.setPriority(Thread.MIN_PRIORITY);
gzipper.start(); // rotate
} else {
gzipper.run(); // shutdown
}
}
}
/**
@@ -107,6 +134,7 @@ class FileLogWriter extends LogWriter {
* Caller must synch
*/
private void rotateFile() {
File old = _currentFile;
File f = getNextFile();
_currentFile = f;
_numBytesInCurrentFile = 0;
@@ -125,7 +153,9 @@ class FileLogWriter extends LogWriter {
//System.exit(0);
}
}
closeWriter();
closeWriter(old, true);
if (_manager.shouldGzip())
(new File(f.getPath() + ".gz")).delete();
try {
_currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
} catch (IOException ioe) {
@@ -180,7 +210,8 @@ class FileLogWriter extends LogWriter {
f = new File(base, replace(pattern, i));
else
f = new File(replace(pattern, i));
if (!f.exists()) {
// check for file or file.gz
if (!f.exists() && !(_manager.shouldGzip() && (new File(f.getPath() + ".gz").exists()))) {
_rotationNum = i;
return f;
}
@@ -197,7 +228,18 @@ class FileLogWriter extends LogWriter {
if (oldest == null) {
oldest = f;
} else {
if (f.lastModified() < oldest.lastModified()) {
// set file or file.gz for last mod check
File ff, oo;
if (!_manager.shouldGzip() || f.exists())
ff = f;
else
ff = new File(f.getPath() + ".gz");
if (!_manager.shouldGzip() || oldest.exists())
oo = oldest;
else
oo = new File(oldest.getPath() + ".gz");
if (ff.lastModified() < oo.lastModified()) {
_rotationNum = i;
oldest = f;
}
@@ -218,4 +260,34 @@ class FileLogWriter extends LogWriter {
}
return buf.toString();
}
/**
* @since 0.9.55
*/
private static class Gzipper extends I2PAppThread {
private final File _f;
public Gzipper(File f) {
super("Log file compressor");
_f = f;
}
public void run() {
File to = new File(_f.getPath() + ".gz");
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream(_f));
out = new BufferedOutputStream(new GZIPOutputStream(new SecureFileOutputStream(to)));
DataHelper.copy(in, out);
} catch (IOException ioe) {
System.out.println("Error compressing log file " + _f);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
to.setLastModified(_f.lastModified());
_f.delete();
}
}
}
}

View File

@@ -38,6 +38,8 @@ import net.i2p.data.DataHelper;
* This also fires off a LogWriter thread that pulls pending records off and
* writes them where appropriate.
*
* As of 0.9.41, this class may be overridden via I2PAppContext.setLogManager()
*
*/
public class LogManager implements Flushable {
public final static String CONFIG_LOCATION_PROP = "loggerConfigLocation";
@@ -65,6 +67,10 @@ public class LogManager implements Flushable {
private static final String PROP_DUP = "logger.dropDuplicates";
/** @since 0.9.18 */
private static final String PROP_FLUSH = "logger.flushInterval";
/** @since 0.9.56 */
private static final String PROP_GZIP = "logger.gzip";
/** @since 0.9.56 */
private static final String PROP_MIN_GZIP_SIZE = "logger.minGzipSize";
public final static String PROP_RECORD_PREFIX = "logger.record.";
public final static String DEFAULT_FORMAT = DATE + " " + PRIORITY + " [" + THREAD + "] " + CLASS + ": " + MESSAGE;
@@ -79,6 +85,9 @@ public class LogManager implements Flushable {
public final static String DEFAULT_DEFAULTLEVEL = Log.STR_ERROR;
public final static String DEFAULT_ONSCREENLEVEL = Log.STR_CRIT;
private static final int MIN_FILESIZE_LIMIT = 16*1024;
private final static boolean DEFAULT_GZIP = false;
private static final int DEFAULT_MIN_GZIP_SIZE = 64*1024;
private final I2PAppContext _context;
private final Log _log;
@@ -133,6 +142,8 @@ public class LogManager implements Flushable {
private final AtomicLong _droppedRecords = new AtomicLong();
// in seconds
private int _flushInterval = (int) (LogWriter.FLUSH_INTERVAL / 1000);
private boolean _gzip;
private long _minGzipSize;
private boolean _alreadyNoticedMissingConfig;
@@ -452,6 +463,17 @@ public class LogManager implements Flushable {
String str = config.getProperty(PROP_DUP);
_dropDuplicates = str == null || Boolean.parseBoolean(str);
str = config.getProperty(PROP_GZIP);
_gzip = str != null ? Boolean.parseBoolean(str) : DEFAULT_GZIP;
if (_gzip) {
_minGzipSize = DEFAULT_MIN_GZIP_SIZE;
try {
str = config.getProperty(PROP_MIN_GZIP_SIZE);
if (str != null)
_minGzipSize = Long.parseLong(str);
} catch (NumberFormatException nfe) {}
}
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Log set to use the base log file as " + _baseLogfilename);
@@ -673,6 +695,20 @@ public class LogManager implements Flushable {
return _rotationLimit;
}
/**
* @since 0.9.56
*/
boolean shouldGzip() {
return _gzip;
}
/**
* @since 0.9.56
*/
long getMinGzipSize() {
return _gzip ? _minGzipSize : Long.MAX_VALUE;
}
/** @return success */
public synchronized boolean saveConfig() {
Properties props = createConfig();
@@ -712,6 +748,8 @@ public class LogManager implements Flushable {
rv.setProperty(PROP_DISPLAYONSCREENLEVEL, Log.toLevelString(_onScreenLimit));
rv.setProperty(PROP_CONSOLEBUFFERSIZE, Integer.toString(_consoleBufferSize));
rv.setProperty(PROP_FLUSH, Integer.toString(_flushInterval));
rv.setProperty(PROP_GZIP, Boolean.toString(_gzip));
rv.setProperty(PROP_MIN_GZIP_SIZE, Long.toString(_minGzipSize));
for (LogLimit lim : _limits) {
rv.setProperty(PROP_RECORD_PREFIX + lim.getRootName(), Log.toLevelString(lim.getLimit()));
@@ -812,16 +850,10 @@ public class LogManager implements Flushable {
_consoleBuffer.clear();
}
private static final AtomicInteger __id = new AtomicInteger();
private class ShutdownHook extends I2PAppThread {
private final int _id;
public ShutdownHook() {
_id = __id.incrementAndGet();
}
@Override
public void run() {
setName("Log " + _id + " shutdown ");
setName("Log shutdown");
shutdown();
}
}