diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java index a59090b414..378ebec15d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/LogsHelper.java @@ -6,12 +6,13 @@ import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.IOException; import java.lang.reflect.Method; -import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.jar.Attributes; import net.i2p.I2PAppContext; import net.i2p.crypto.SigType; +import net.i2p.data.DataHelper; import net.i2p.router.web.ConfigServiceHandler; import net.i2p.router.web.CSSHelper; import net.i2p.router.web.HelperBase; @@ -24,6 +25,10 @@ public class LogsHelper extends HelperBase { private static final String _jstlVersion = jstlVersion(); + private static final int MAX_WRAPPER_LINES = 250; + private static final String PROP_LAST_WRAPPER = "routerconsole.lastWrapperLogEntry"; + + /** @since 0.8.12 */ public String getJettyVersion() { return RouterConsoleRunner.jettyVersion(); @@ -72,7 +77,8 @@ public class LogsHelper extends HelperBase { */ public String getLogs() { String str = formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); - return "

" + _t("File location") + ": " + _context.logManager().currentFile() + "

" + str; + return "

" + _t("File location") + ": " + + DataHelper.escapeHTML(_context.logManager().currentFile()) + "

" + str; } /** @@ -116,31 +122,82 @@ public class LogsHelper extends HelperBase { * @param consoleNonce must match * @since 0.9.46 */ - public void clearThrough(int n, int crit, String consoleNonce) { + public void clearThrough(int n, int crit, long wn, long wts, String wf, String consoleNonce) { if (!CSSHelper.getNonce().equals(consoleNonce)) return; if (n >= 0) _context.logManager().getBuffer().getUIMessages().clearThrough(n); if (crit >= 0) _context.logManager().getBuffer().getCriticalUIMessages().clearThrough(crit); + if (wn >= 0 && wts > 0 && wf != null) { + // timestamp, last line number, filename + String val = wts + "," + wn + "," + wf; + if (!val.equals(_context.getProperty(PROP_LAST_WRAPPER))) + _context.router().saveConfig(PROP_LAST_WRAPPER, val); + } } - public String getServiceLogs() { + /** + * last line number -1 on error + * @param buf out parameter + * @return Long timestamp, Long last line number, String filename (escaped) + */ + public Object[] getServiceLogs(StringBuilder obuf) { File f = ConfigServiceHandler.wrapperLogFile(_context); String str; - if (_context.hasWrapper()) { + long flastMod = f.lastModified(); + long lastMod = 0; + long toSkip = 0; + // timestamp, last line number, filename + String prop = _context.getProperty(PROP_LAST_WRAPPER); + if (prop != null) { + String[] vals = DataHelper.split(prop, ",", 3); + if (vals.length == 3) { + if (vals[2].equals(f.getName())) { + try { lastMod = Long.parseLong(vals[0]); } catch (NumberFormatException nfe) {} + try { toSkip = Long.parseLong(vals[1]); } catch (NumberFormatException nfe) {} + } else { + // file rotated + lastMod = 0; + } + } + } + if (lastMod > 0 && flastMod <= lastMod) { + str = ""; + toSkip = -1; + } else if (_context.hasWrapper()) { // platform encoding - str = readTextFile(f, 250); + StringBuilder buf = new StringBuilder(MAX_WRAPPER_LINES * 80); + toSkip = readTextFile(f, MAX_WRAPPER_LINES, toSkip, buf); + if (toSkip >= 0) + str = buf.toString(); + else + str = null; } else { // UTF-8 - str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false); + // no skipping + str = FileUtil.readTextFile(f.getAbsolutePath(), MAX_WRAPPER_LINES, false); + toSkip = 0; } + String loc = DataHelper.escapeHTML(f.getAbsolutePath()); if (str == null) { - return "

" + _t("File not found") + ": " + f.getAbsolutePath() + "

"; + obuf.append("

").append(_t("File not found")).append(": ").append(loc).append("

"); + toSkip = -1; } else { - str = str.replace("&", "&").replace("<", "<").replace(">", ">"); - return "

" + _t("File location") + ": " + f.getAbsolutePath() + "

\n
" + str + "
"; + obuf.append("

").append(_t("File location")).append(": ") + .append(loc).append("

\n"); + if (str.length() > 0) { + str = str.replace("&", "&").replace("<", "<").replace(">", ">"); + obuf.append("
").append(str).append("
"); + } else { + obuf.append("

").append(_t("No log messages")).append("

"); + } } + Object[] rv = new Object[3]; + rv[0] = Long.valueOf(flastMod); + rv[1] = Long.valueOf(toSkip); + rv[2] = DataHelper.escapeHTML(f.getName()).replace(" ", "%20"); + return rv; } /** @@ -232,28 +289,33 @@ public class LogsHelper extends HelperBase { * Warning - converts \r\n to \n * * @param maxNumLines max number of lines (greater than zero) - * @return string or null; does not throw IOException. + * @param skipLines number of lines to skip, or zero + * @param buf out parameter + * @return -1 on failure, or number of lines in the file. Does not throw IOException. * @since 0.9.11 modded from FileUtil.readTextFile() */ - private static String readTextFile(File f, int maxNumLines) { - if (!f.exists()) return null; + private static long readTextFile(File f, int maxNumLines, long skipLines, StringBuilder buf) { + if (!f.exists()) + return -1; BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(new FileInputStream(f))); - List lines = new ArrayList(maxNumLines); + LinkedList lines = new LinkedList(); + long i = 0; String line = null; while ( (line = in.readLine()) != null) { + if (i++ < skipLines) + continue; lines.add(line); if (lines.size() >= maxNumLines) - lines.remove(0); + lines.removeFirst(); } - StringBuilder buf = new StringBuilder(lines.size() * 80); - for (int i = 0; i < lines.size(); i++) { - buf.append(lines.get(i)).append('\n'); + for (String ln : lines) { + buf.append(ln).append('\n'); } - return buf.toString(); + return i; } catch (IOException ioe) { - return null; + return -1; } finally { if (in != null) try { in.close(); } catch (IOException ioe) {} } diff --git a/apps/routerconsole/jsp/logs.jsp b/apps/routerconsole/jsp/logs.jsp index bf95e98080..c930358364 100644 --- a/apps/routerconsole/jsp/logs.jsp +++ b/apps/routerconsole/jsp/logs.jsp @@ -58,12 +58,18 @@ String consoleNonce = net.i2p.router.web.CSSHelper.getNonce(); String ct1 = request.getParameter("clear"); String ct2 = request.getParameter("crit"); + String ct3 = request.getParameter("svc"); + String ct4 = request.getParameter("svct"); + String ct5 = request.getParameter("svcf"); String ctn = request.getParameter("consoleNonce"); - if ((ct1 != null || ct2 != null) && ctn != null) { + if ((ct1 != null || ct2 != null || (ct3 != null && ct4 != null && ct5 != null)) && ctn != null) { int ict1 = -1, ict2 = -1; + long ict3 = -1, ict4 = -1; try { ict1 = Integer.parseInt(ct1); } catch (NumberFormatException nfe) {} try { ict2 = Integer.parseInt(ct2); } catch (NumberFormatException nfe) {} - logsHelper.clearThrough(ict1, ict2, ctn); + try { ict3 = Long.parseLong(ct3); } catch (NumberFormatException nfe) {} + try { ict4 = Long.parseLong(ct4); } catch (NumberFormatException nfe) {} + logsHelper.clearThrough(ict1, ict2, ict3, ict4, ct5, ctn); } int last = logsHelper.getLastCriticalMessageNumber(); if (last >= 0) { @@ -97,10 +103,22 @@ -

<%=intl._t("Service (Wrapper) Logs")%>

+

<%=intl._t("Service (Wrapper) Logs")%><% + StringBuilder buf = new StringBuilder(24*1024); + // timestamp, last line number, escaped filename + Object[] vals = logsHelper.getServiceLogs(buf); + String lts = vals[0].toString(); + long llast = ((Long) vals[1]).longValue(); + String filename = vals[2].toString(); + if (llast >= 0) { +%> " href="logs?svc=<%=llast%>&svct=<%=lts%>&svcf=<%=filename%>&consoleNonce=<%=consoleNonce%>">[<%=intl._t("Clear logs")%>]<% + } +%>

- +<% + out.append(buf); +%>
diff --git a/installer/resources/themes/console/dark/console.css b/installer/resources/themes/console/dark/console.css index 72967a1ae3..31cb7a1ef3 100644 --- a/installer/resources/themes/console/dark/console.css +++ b/installer/resources/themes/console/dark/console.css @@ -3994,7 +3994,7 @@ h3#servicedebug a, h3#graphinfo a { float: right; } -.main#tunnels h3 a:not(old), #criticallogs + h3.tabletitle a:not(old) { +.main#tunnels h3 a:not(old), #logs h3.tabletitle a.configure:not(old), #logs h3.tabletitle a.delete:not(old) { font-size: 0; } diff --git a/installer/resources/themes/console/light/console.css b/installer/resources/themes/console/light/console.css index d8f93be00e..2d3dd1183a 100644 --- a/installer/resources/themes/console/light/console.css +++ b/installer/resources/themes/console/light/console.css @@ -1800,7 +1800,7 @@ h3#exploratorytunnels { margin-top: 15px; } -#tunnels h3 a:not(old), #criticallogs + h3.tabletitle a:not(old) { +#tunnels h3 a:not(old), #logs h3.tabletitle a.configure:not(old), #logs h3.tabletitle a.delete:not(old) { font-size: 0; }