forked from I2P_Developers/i2p.i2p
Replace ident log with new, general-purpose event log.
Use for stops, starts, and updates, and others. Mark all restarts on graphs using the event log.
This commit is contained in:
@ -7,6 +7,7 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
@ -15,6 +16,7 @@ import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.util.EventLog;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
@ -137,9 +139,11 @@ class SummaryRenderer {
|
||||
// Strings.java
|
||||
descr = _(_listener.getRate().getRateStat().getDescription());
|
||||
}
|
||||
long started = ((RouterContext)_context).router().getWhenStarted();
|
||||
if (started > start && started < end)
|
||||
def.vrule(started / 1000, RESTART_BAR_COLOR, _("Restart"), 4.0f);
|
||||
|
||||
//long started = ((RouterContext)_context).router().getWhenStarted();
|
||||
//if (started > start && started < end)
|
||||
// def.vrule(started / 1000, RESTART_BAR_COLOR, _("Restart"), 4.0f);
|
||||
|
||||
def.datasource(plotName, path, plotName, SummaryListener.CF, _listener.getBackendName());
|
||||
if (descr.length() > 0)
|
||||
def.area(plotName, Color.BLUE, descr + "\\r");
|
||||
@ -151,6 +155,14 @@ class SummaryRenderer {
|
||||
def.gprint(plotName, "LAST", ' ' + _("now") + ": %.2f %S\\r");
|
||||
// '07-Jul 21:09 UTC' with month name in the system locale
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM HH:mm");
|
||||
Map<Long, String> events = ((RouterContext)_context).router().eventLog().getEvents(EventLog.STARTED, start);
|
||||
for (Map.Entry<Long, String> event : events.entrySet()) {
|
||||
long started = event.getKey().longValue();
|
||||
if (started > start && started < end) {
|
||||
String legend = _("Restart") + ' ' + sdf.format(new Date(started)) + " UTC " + event.getValue() + "\\r";
|
||||
def.vrule(started / 1000, RESTART_BAR_COLOR, legend, 4.0f);
|
||||
}
|
||||
}
|
||||
def.comment(sdf.format(new Date(start)) + " -- " + sdf.format(new Date(end)) + " UTC\\r");
|
||||
}
|
||||
if (!showCredit)
|
||||
|
@ -40,6 +40,7 @@ import net.i2p.router.startup.WorkingDir;
|
||||
import net.i2p.router.tasks.*;
|
||||
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
||||
import net.i2p.router.transport.udp.UDPTransport;
|
||||
import net.i2p.router.util.EventLog;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.stat.StatManager;
|
||||
import net.i2p.util.ByteCache;
|
||||
@ -77,6 +78,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
private I2PThread _gracefulShutdownDetector;
|
||||
private RouterWatchdog _watchdog;
|
||||
private Thread _watchdogThread;
|
||||
private final EventLog _eventLog;
|
||||
|
||||
public final static String PROP_CONFIG_FILE = "router.configLocation";
|
||||
|
||||
@ -100,6 +102,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys";
|
||||
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
|
||||
public final static String DNS_CACHE_TIME = "" + (5*60);
|
||||
private static final String EVENTLOG = "eventlog.txt";
|
||||
|
||||
private static final String originalTimeZoneID;
|
||||
static {
|
||||
@ -219,12 +222,14 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
// i2p.dir.pid defaults to i2p.dir.router
|
||||
// i2p.dir.base defaults to user.dir == $CWD
|
||||
_context = new RouterContext(this, envProps);
|
||||
_eventLog = new EventLog(_context, new File(_context.getRouterDir(), EVENTLOG));
|
||||
|
||||
// This is here so that we can get the directory location from the context
|
||||
// for the ping file
|
||||
// Check for other router but do not start a thread yet so the update doesn't cause
|
||||
// a NCDFE
|
||||
if (!isOnlyRouterRunning()) {
|
||||
_eventLog.addEvent(EventLog.ABORTED, "Another router running");
|
||||
System.err.println("ERROR: There appears to be another router already running!");
|
||||
System.err.println(" Please make sure to shut down old instances before starting up");
|
||||
System.err.println(" a new one. If you are positive that no other instance is running,");
|
||||
@ -410,6 +415,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
public void runRouter() {
|
||||
if (_isAlive)
|
||||
throw new IllegalStateException();
|
||||
_eventLog.addEvent(EventLog.STARTED, RouterVersion.FULL_VERSION);
|
||||
startupStuff();
|
||||
_isAlive = true;
|
||||
_started = _context.clock().now();
|
||||
@ -631,6 +637,13 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
return Certificate.NULL_CERT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public EventLog eventLog() {
|
||||
return _eventLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ugly list of files that we need to kill if we are building a new identity
|
||||
*
|
||||
@ -646,7 +659,6 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
"sessionKeys.dat" // no longer used
|
||||
};
|
||||
|
||||
static final String IDENTLOG = "identlog.txt";
|
||||
public void killKeys() {
|
||||
//new Exception("Clearing identity files").printStackTrace();
|
||||
int remCount = 0;
|
||||
@ -671,18 +683,10 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
}
|
||||
|
||||
if (remCount > 0) {
|
||||
FileOutputStream log = null;
|
||||
try {
|
||||
log = new FileOutputStream(new File(_context.getRouterDir(), IDENTLOG), true);
|
||||
log.write((new Date() + ": Old router identity keys cleared\n").getBytes());
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
} finally {
|
||||
if (log != null)
|
||||
try { log.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
_eventLog.addEvent(EventLog.REKEYED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild a new identity the hard way - delete all of our old identity
|
||||
* files, then reboot the router.
|
||||
@ -872,6 +876,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
}
|
||||
}
|
||||
_context.getFinalShutdownTasks().clear();
|
||||
_eventLog.addEvent(EventLog.STOPPED, Integer.toString(exitCode));
|
||||
|
||||
if (_killVMOnEnd) {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
|
||||
@ -1140,6 +1145,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
// Set the last version to the current version, since 0.8.13
|
||||
_config.put("router.previousVersion", RouterVersion.VERSION);
|
||||
saveConfig();
|
||||
_eventLog.addEvent(EventLog.UPDATED);
|
||||
ok = FileUtil.extractZip(updateFile, _context.getBaseDir());
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.util.EventLog;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.ShellCommand;
|
||||
@ -107,6 +108,7 @@ public class RouterWatchdog implements Runnable {
|
||||
_log.error("Memory: " + DataHelper.formatSize(used) + '/' + DataHelper.formatSize(max));
|
||||
if (_consecutiveErrors == 1) {
|
||||
_log.log(Log.CRIT, "Router appears hung, or there is severe network congestion. Watchdog starts barking!");
|
||||
_context.router().eventLog().addEvent(EventLog.WATCHDOG);
|
||||
// This works on linux...
|
||||
// It won't on windows, and we can't call i2prouter.bat either, it does something
|
||||
// completely different...
|
||||
|
@ -10,6 +10,8 @@ package net.i2p.router.tasks;
|
||||
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.RouterVersion;
|
||||
import net.i2p.router.util.EventLog;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -35,6 +37,7 @@ public class ShutdownHook extends Thread {
|
||||
// Needed to make the wrapper happy, otherwise it gets confused
|
||||
// and thinks we haven't shut down, possibly because it
|
||||
// prevents other shutdown hooks from running
|
||||
_context.router().eventLog().addEvent(EventLog.CRASHED, RouterVersion.FULL_VERSION);
|
||||
_context.router().setKillVMOnEnd(false);
|
||||
_context.router().shutdown2(Router.EXIT_HARD);
|
||||
}
|
||||
|
145
router/java/src/net/i2p/router/util/EventLog.java
Normal file
145
router/java/src/net/i2p/router/util/EventLog.java
Normal file
@ -0,0 +1,145 @@
|
||||
package net.i2p.router.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Simple event logger for occasional events,
|
||||
* with caching for reads.
|
||||
* Does not keep the file open.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public class EventLog {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final File _file;
|
||||
/** event to cached map */
|
||||
private final Map<String, SortedMap<Long, String>> _cache;
|
||||
/** event to starting time of cached map */
|
||||
private final Map<String, Long> _cacheTime;
|
||||
|
||||
/** for convenience, not required */
|
||||
public static final String ABORTED = "aborted";
|
||||
public static final String CHANGE_IP = "changeIP";
|
||||
public static final String CHANGE_PORT = "changePort";
|
||||
public static final String CLOCK_SHIFT = "clockShift";
|
||||
public static final String CRASHED = "crashed";
|
||||
public static final String INSTALLED = "installed";
|
||||
public static final String INSTALL_FAILED = "intallFailed";
|
||||
public static final String NEW_IDENT = "newIdent";
|
||||
public static final String REKEYED = "rekeyed";
|
||||
public static final String SOFT_RESTART = "softRestart";
|
||||
public static final String STARTED = "started";
|
||||
public static final String STOPPED = "stopped";
|
||||
public static final String UPDATED = "updated";
|
||||
public static final String WATCHDOG = "watchdog";
|
||||
|
||||
/**
|
||||
* @param file must be absolute
|
||||
* @throws IllegalArgumentException if not absolute
|
||||
*/
|
||||
public EventLog(I2PAppContext ctx, File file) {
|
||||
if (!file.isAbsolute())
|
||||
throw new IllegalArgumentException();
|
||||
_context = ctx;
|
||||
_file = file;
|
||||
_cache = new HashMap(4);
|
||||
_cacheTime = new HashMap(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an event. Fails silently.
|
||||
* @param event no spaces, e.g. "started"
|
||||
* @throws IllegalArgumentException if event contains a space or newline
|
||||
*/
|
||||
public void addEvent(String event) {
|
||||
addEvent(event, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an event. Fails silently.
|
||||
* @param event no spaces or newlines, e.g. "started"
|
||||
* @param info no newlines, may be blank or null
|
||||
* @throws IllegalArgumentException if event contains a space or either contains a newline
|
||||
*/
|
||||
public synchronized void addEvent(String event, String info) {
|
||||
if (event.contains(" ") || event.contains("\n") ||
|
||||
(info != null && info.contains("\n")))
|
||||
throw new IllegalArgumentException();
|
||||
_cache.remove(event);
|
||||
_cacheTime.remove(event);
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new SecureFileOutputStream(_file, true);
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append(_context.clock().now()).append(' ').append(event);
|
||||
if (info != null && info.length() > 0)
|
||||
buf.append(' ').append(info);
|
||||
buf.append('\n');
|
||||
out.write(buf.toString().getBytes("UTF-8"));
|
||||
} catch (IOException ioe) {
|
||||
} finally {
|
||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches.
|
||||
* Fails silently.
|
||||
* @param event matching this event only, case sensitive
|
||||
* @param since since this time, 0 for all
|
||||
* @return non-null, Map of times to (possibly empty) info strings, sorted, earliest first, unmodifiable
|
||||
*/
|
||||
public synchronized SortedMap<Long, String> getEvents(String event, long since) {
|
||||
SortedMap<Long, String> rv = _cache.get(event);
|
||||
if (rv != null) {
|
||||
Long cacheTime = _cacheTime.get(event);
|
||||
if (cacheTime != null) {
|
||||
if (since >= cacheTime.longValue())
|
||||
return rv.tailMap(Long.valueOf(since));
|
||||
}
|
||||
}
|
||||
rv = new TreeMap();
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(_file);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
try {
|
||||
String[] s = line.split(" ", 3);
|
||||
if (!s[1].equals(event))
|
||||
continue;
|
||||
long time = Long.parseLong(s[0]);
|
||||
if (time <= since)
|
||||
continue;
|
||||
Long ltime = Long.valueOf(time);
|
||||
String info = s.length > 2 ? s[2] : "";
|
||||
rv.put(time, info);
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
} catch (NumberFormatException nfe) {
|
||||
}
|
||||
}
|
||||
rv = Collections.unmodifiableSortedMap(rv);
|
||||
_cache.put(event, rv);
|
||||
_cacheTime.put(event, Long.valueOf(since));
|
||||
} catch (IOException ioe) {
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user