package net.i2p.router.web; import java.io.IOException; import java.io.Writer; import java.text.Collator; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import net.i2p.data.DataHelper; import net.i2p.router.util.EventLog; import net.i2p.util.SystemVersion; /** * /events.jsp */ public class EventLogHelper extends FormHandler { protected Writer _out; private long _from, _age; //private long _to = Long.MAX_VALUE; private String _event = ALL; // EventLog name to translated display string private final Map<String, String> _xevents; private static final String ALL = "all"; private static final String[] _events = new String[] { EventLog.ABORTED, _x("Aborted startup"), EventLog.BECAME_FLOODFILL, _x("Enabled floodfill"), EventLog.CHANGE_IP, _x("Changed IP"), EventLog.CHANGE_PORT, _x("Changed port"), EventLog.CLOCK_SHIFT, _x("Clock shifted"), EventLog.CRASHED, _x("Crashed"), EventLog.CRITICAL, _x("Critical error"), EventLog.INSTALLED, _x("Installed new version"), EventLog.INSTALL_FAILED, _x("Install failed"), EventLog.NETWORK, _x("Network error"), EventLog.NEW_IDENT, _x("New router identity"), EventLog.NOT_FLOODFILL, _x("Disabled floodfill"), EventLog.OOM, _x("Out of memory error"), EventLog.REACHABILITY, _x("Reachability change"), EventLog.REKEYED, _x("New router identity"), EventLog.RESEED, _x("Reseeded router"), EventLog.SOFT_RESTART, _x("Soft restart"), EventLog.STARTED, _x("Started router"), EventLog.STOPPED, _x("Stopped router"), EventLog.UPDATED, _x("Updated router"), EventLog.WATCHDOG, _x("Watchdog warning") }; private static final long DAY = 24*60*60*1000L; private static final long[] _times = { 0, DAY, 7*DAY, 30*DAY, 365*DAY }; public EventLogHelper() { super(); _xevents = new HashMap<String, String>(1 + (_events.length / 2)); } /** set the defaults after we have a context */ @Override public void setContextId(String contextId) { super.setContextId(contextId); for (int i = 0; i < _events.length; i += 2) { _xevents.put(_events[i], _t(_events[i + 1])); } } public void storeWriter(Writer out) { _out = out; } public void setFrom(String s) { try { _age = Long.parseLong(s); if (_age > 0) _from = _context.clock().now() - _age; else _from = 0; } catch (NumberFormatException nfe) { _age = 0; _from = 0; } } //public void setTo(String s) { // _to = s; //} public void setType(String s) { _event = s; } public String getForm() { // too hard to use the standard formhandler.jsi / FormHandler.java session nonces // since graphs.jsp needs the refresh value in its <head>. // So just use the "shared/console nonce". String nonce = CSSHelper.getNonce(); try { _out.write("<br><h3>" + _t("Display Events") + "</h3>"); _out.write("<form action=\"events\" method=\"POST\">\n" + "<input type=\"hidden\" name=\"action\" value=\"save\">\n" + "<input type=\"hidden\" name=\"nonce\" value=\"" + nonce + "\" >\n"); _out.write(_t("Events since") + ": <select name=\"from\">"); for (int i = 0; i < _times.length; i++) { writeOption(_times[i]); } _out.write("</select><br>"); _out.write(_t("Event type") + ": <select name=\"type\">"); // sorted by translated display string Map<String, String> events = new TreeMap<String, String>(Collator.getInstance()); for (int i = 0; i < _events.length; i += 2) { events.put(_xevents.get(_events[i]), _events[i]); } writeOption(_t("All events"), ALL); for (Map.Entry<String, String> e : events.entrySet()) { writeOption(e.getKey(), e.getValue()); } _out.write("</select>" + "<hr><div class=\"formaction\"><input type=\"submit\" class=\"accept\" value=\"" + _t("Filter events") + "\"></div></form>"); } catch (IOException ioe) { ioe.printStackTrace(); } return ""; } private void writeOption(String key, String val) throws IOException { _out.write("<option value=\""); _out.write(val); _out.write("\""); if (val.equals(_event)) _out.write(" selected=\"selected\""); _out.write(">"); _out.write(key); _out.write("</option>\n"); } private void writeOption(long age) throws IOException { _out.write("<option value=\""); _out.write(Long.toString(age)); _out.write("\""); if (age == _age) _out.write(" selected=\"selected\""); _out.write(">"); if (age == 0) _out.write(_t("All events")); else _out.write(DataHelper.formatDuration2(age)); _out.write("</option>\n"); } public String getEvents() { EventLog ev = _context.router().eventLog(); // oldest first Map<Long, String> events; boolean isAll = ALL.equals(_event); if (isAll) events = ev.getEvents(_from); else events = ev.getEvents(_event, _from); String xev = _xevents.get(_event); if (xev == null) xev = _event; xev = DataHelper.escapeHTML(xev); if (events.isEmpty()) { if (isAll) { if (_age == 0) return _t("No events found"); return _t("No events found in previous {0}", DataHelper.formatDuration2(_age)); } if (_age == 0) return _t("No \"{0}\" events found", xev); return _t("No \"{0}\" events found in previous {1}", xev, DataHelper.formatDuration2(_age)); } StringBuilder buf = new StringBuilder(2048); buf.append("<table><tr><th>"); buf.append(_t("Time")); buf.append("</th><th>"); if (isAll) { buf.append(_t("Event")); buf.append("</th><th>"); buf.append(_t("Details")); } else { buf.append(xev); } buf.append("</th></tr>"); SimpleDateFormat fmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); // the router sets the JVM time zone to UTC but saves the original here so we can get it fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context)); List<Map.Entry<Long, String>> entries = new ArrayList<Map.Entry<Long, String>>(events.entrySet()); Collections.reverse(entries); for (Map.Entry<Long, String> e : entries) { long time = e.getKey().longValue(); String event = e.getValue(); buf.append("<tr><td>"); buf.append(fmt.format(new Date(time))); buf.append("</td><td>"); if (isAll) { String[] s = DataHelper.split(event, " ", 2); String xs = _xevents.get(s[0]); if (xs == null) xs = s[0]; buf.append(xs); buf.append("</td><td>"); if (s.length > 1) buf.append(s[1]); } else { buf.append(event); } buf.append("</td></tr>"); } buf.append("</table>"); return buf.toString(); } }