forked from I2P_Developers/i2p.i2p
* Console: Add event log viewer (ticket #1117)
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
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.TimeZone;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.util.EventLog;
|
||||
|
||||
/**
|
||||
* /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.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.OOM, _x("Out of memory error"),
|
||||
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], _(_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>" + _("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(_("Events since") + ": <select name=\"from\">");
|
||||
for (int i = 0; i < _times.length; i++) {
|
||||
writeOption(_times[i]);
|
||||
}
|
||||
_out.write("</select><br>");
|
||||
_out.write(_("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(_("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=\"" + _("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(_("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;
|
||||
if (events.isEmpty()) {
|
||||
if (isAll) {
|
||||
if (_age == 0)
|
||||
return _("No events found");
|
||||
return _("No events found in previous {0}", DataHelper.formatDuration2(_age));
|
||||
}
|
||||
if (_age == 0)
|
||||
return _("No \"{0}\" events found", xev);
|
||||
return _("No \"{0}\" events found in previous {1}", xev, DataHelper.formatDuration2(_age));
|
||||
}
|
||||
StringBuilder buf = new StringBuilder(2048);
|
||||
buf.append("<table><tr><th>");
|
||||
buf.append(_("Time"));
|
||||
buf.append("</th><th>");
|
||||
if (isAll) {
|
||||
buf.append(_("Event"));
|
||||
buf.append("</th><th>");
|
||||
buf.append(_("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
|
||||
String systemTimeZone = _context.getProperty("i2p.systemTimeZone");
|
||||
if (systemTimeZone != null)
|
||||
fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone));
|
||||
|
||||
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 = event.split(" ", 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();
|
||||
}
|
||||
}
|
||||
@@ -267,4 +267,14 @@ public abstract class FormHandler {
|
||||
public String _(String s, Object o, Object o2) {
|
||||
return Messages.getString(s, o, o2, _context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a string for extraction by xgettext and translation.
|
||||
* Use this only in static initializers.
|
||||
* It does not translate!
|
||||
* @return s
|
||||
*/
|
||||
public static String _x(String s) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
28
apps/routerconsole/jsp/events.jsp
Normal file
28
apps/routerconsole/jsp/events.jsp
Normal file
@@ -0,0 +1,28 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page trimDirectiveWhitespaces="true"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<%@include file="css.jsi" %>
|
||||
<%=intl.title("events")%>
|
||||
<jsp:useBean class="net.i2p.router.web.EventLogHelper" id="eventHelper" scope="request" />
|
||||
<jsp:setProperty name="eventHelper" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" />
|
||||
<% /* GraphHelper sets the defaults in setContextId, so setting the properties must be after the context */ %>
|
||||
<jsp:setProperty name="eventHelper" property="*" />
|
||||
<%
|
||||
eventHelper.storeWriter(out);
|
||||
eventHelper.storeMethod(request.getMethod());
|
||||
%>
|
||||
<script src="/js/ajax.js" type="text/javascript"></script>
|
||||
<%@include file="summaryajax.jsi" %>
|
||||
</head><body onload="initAjax()">
|
||||
<%@include file="summary.jsi" %>
|
||||
<h1><%=intl._("I2P Event Log")%></h1>
|
||||
<div class="main" id="main">
|
||||
<div class="eventspanel">
|
||||
<div class="widepanel">
|
||||
<jsp:getProperty name="eventHelper" property="allMessages" />
|
||||
<jsp:getProperty name="eventHelper" property="form" />
|
||||
<jsp:getProperty name="eventHelper" property="events" />
|
||||
</div></div></div></body></html>
|
||||
@@ -37,10 +37,10 @@
|
||||
<p><%=intl._("Note that system information, log timestamps, and log messages may provide clues to your location; please review everything you include in a bug report.")%></p>
|
||||
<h3><%=intl._("Critical Logs")%></h3><a name="criticallogs"> </a>
|
||||
<jsp:getProperty name="logsHelper" property="criticalLogs" />
|
||||
<hr>
|
||||
<h3><%=intl._("Router Logs")%> (<a href="configlogging"><%=intl._("configure")%></a>)</h3>
|
||||
<jsp:getProperty name="logsHelper" property="logs" />
|
||||
<hr>
|
||||
<h3><%=intl._("Event Logs")%></h3>
|
||||
<a href="events"><%=intl._("View event logs")%></a>
|
||||
<h3><%=intl._("Service (Wrapper) Logs")%></h3><a name="servicelogs"> </a>
|
||||
<jsp:getProperty name="logsHelper" property="serviceLogs" />
|
||||
</div><hr></div></body></html>
|
||||
</div></div></body></html>
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
2014-07-03 zzz
|
||||
* Base64:
|
||||
- Catch numerous decoding errors that were previously misdecoded (ticket #1318)
|
||||
- Improve decoding efficiency, reduce copies
|
||||
- encode(String) now uses UTF-8 encoding
|
||||
- decode() now accepts short strings without trailing '='
|
||||
- whitespace in decode will now cause an error, was previously ignored
|
||||
* Console: Add event log viewer (ticket #1117)
|
||||
|
||||
2014-07-02 kytv
|
||||
* Update Java Service Wrapper to v3.5.25
|
||||
- Windows: x86 and x64 versions self-compiled with VS2010 in
|
||||
|
||||
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 12;
|
||||
public final static long BUILD = 13;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
||||
@@ -144,4 +144,43 @@ public class EventLog {
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* All events since a given time.
|
||||
* Does not cache. Fails silently.
|
||||
* Values in the returned map have the format "event[ info]".
|
||||
* Events do not contain spaces.
|
||||
*
|
||||
* @param since since this time, 0 for all
|
||||
* @return non-null, Map of times to info strings, sorted, earliest first, unmodifiable
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public synchronized SortedMap<Long, String> getEvents(long since) {
|
||||
SortedMap<Long, String> rv = new TreeMap<Long, String>();
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(_file), "UTF-8"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
try {
|
||||
String[] s = line.split(" ", 2);
|
||||
if (s.length < 2)
|
||||
continue;
|
||||
long time = Long.parseLong(s[0]);
|
||||
if (time <= since)
|
||||
continue;
|
||||
Long ltime = Long.valueOf(time);
|
||||
rv.put(ltime, s[1]);
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
} catch (NumberFormatException nfe) {
|
||||
}
|
||||
}
|
||||
rv = Collections.unmodifiableSortedMap(rv);
|
||||
} catch (IOException ioe) {
|
||||
} finally {
|
||||
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user