package net.i2p.router.web; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import net.i2p.I2PAppContext; import net.i2p.crypto.SigType; import net.i2p.util.FileUtil; import net.i2p.util.VersionComparator; import org.eclipse.jetty.server.Server; import org.tanukisoftware.wrapper.WrapperManager; public class LogsHelper extends HelperBase { private static final String LOCATION_AVAILABLE = "3.3.7"; private static final String _jstlVersion = jstlVersion(); /** @since 0.8.12 */ public String getJettyVersion() { return Server.getVersion(); } /** @since 0.8.13 */ public static String jettyVersion() { return Server.getVersion(); } /** @since 0.9.15 */ public String getUnavailableCrypto() { StringBuilder buf = new StringBuilder(128); for (SigType t : SigType.values()) { if (!t.isAvailable()) { buf.append("<b>Crypto:</b> ").append(t.toString()).append(" unavailable<br>"); } } return buf.toString(); } /** * @return non-null, "n/a" on failure * @since 0.9.26 */ public String getJstlVersion() { return _jstlVersion; } /** * @return non-null, "n/a" on failure * @since 0.9.26 */ private static String jstlVersion() { String rv = "n/a"; try { Class<?> cls = Class.forName("org.apache.taglibs.standard.Version", true, ClassLoader.getSystemClassLoader()); Method getVersion = cls.getMethod("getVersion"); // returns "standard-taglib 1.2.0" Object version = getVersion.invoke(null, (Object[]) null); rv = (String) version; //int sp = rv.indexOf(' '); //if (sp >= 0 && rv.length() > sp + 1) // rv = rv.substring(sp + 1); } catch (Exception e) {} return rv; } /** * Does not call logManager.flush(); call getCriticalLogs() first to flush */ public String getLogs() { String str = formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); return "<p>" + _t("File location") + ": <a href=\"/router.log\">" + _context.logManager().currentFile() + "</a></p>" + str; } /** * Side effect - calls logManager.flush() */ public String getCriticalLogs() { _context.logManager().flush(); return formatMessages(_context.logManager().getBuffer().getMostRecentCriticalMessages()); } /** * Does not necessarily exist. * * @return non-null, doesn't necessarily exist * @since 0.9.1, public since 0.9.27 */ public static File wrapperLogFile(I2PAppContext ctx) { File f = null; if (ctx.hasWrapper()) { String wv = System.getProperty("wrapper.version"); if (wv != null && VersionComparator.comp(wv, LOCATION_AVAILABLE) >= 0) { try { f = WrapperManager.getWrapperLogFile(); } catch (Throwable t) {} } } if (f == null || !f.exists()) { // RouterLaunch puts the location here if no wrapper String path = System.getProperty("wrapper.logfile"); if (path != null) { f = new File(path); } else { // look in new and old places f = new File(System.getProperty("java.io.tmpdir"), "wrapper.log"); if (!f.exists()) f = new File(ctx.getBaseDir(), "wrapper.log"); } } return f; } public String getServiceLogs() { File f = wrapperLogFile(_context); String str; if (_context.hasWrapper()) { // platform encoding str = readTextFile(f, 250); } else { // UTF-8 str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false); } if (str == null) { return "<p>" + _t("File not found") + ": <b><code>" + f.getAbsolutePath() + "</code></b></p>"; } else { str = str.replace("&", "&").replace("<", "<").replace(">", ">"); return "<p>" + _t("File location") + ": <a href=\"/wrapper.log\">" + f.getAbsolutePath() + "</a></p><pre>" + str + "</pre>"; } } /***** unused public String getConnectionLogs() { return formatMessages(_context.commSystem().getMostRecentErrorMessages()); } ******/ private final static String NL = System.getProperty("line.separator"); /** formats in reverse order */ private String formatMessages(List<String> msgs) { if (msgs.isEmpty()) return "<p><i>" + _t("No log messages") + "</i></p>"; boolean colorize = _context.getBooleanPropertyDefaultTrue("routerconsole.logs.color"); StringBuilder buf = new StringBuilder(16*1024); buf.append("<ul>"); for (int i = msgs.size() - 1; i >= 0; i--) { String msg = msgs.get(i); // don't display the dup message if it is last if (i == 0 && msg.contains("↓")) break; msg = msg.replace("&", "&").replace("<", "<").replace(">", ">"); msg = msg.replace("&darr;", "↓"); // hack - undo the damage (LogWriter) // remove last \n that LogRecordFormatter added if (msg.endsWith(NL)) msg = msg.substring(0, msg.length() - NL.length()); // replace \n so that exception stack traces will format correctly and will paste nicely into pastebin msg = msg.replace("\n", "<br>    \n"); buf.append("<li>"); if (colorize) { // TODO this would be a lot easier if LogConsoleBuffer stored LogRecords instead of formatted strings String color; // Homeland Security Advisory System // http://www.dhs.gov/xinfoshare/programs/Copy_of_press_release_0046.shtm // but pink instead of yellow for WARN if (msg.contains(_t("CRIT"))) color = "#cc0000"; else if (msg.contains(_t("ERROR"))) color = "#ff3300"; else if (msg.contains(_t("WARN"))) color = "#ff00cc"; else if (msg.contains(_t("INFO"))) color = "#000099"; else color = "#006600"; buf.append("<font color=\"").append(color).append("\">"); buf.append(msg); buf.append("</font>"); } else { buf.append(msg); } buf.append("</li>\n"); } buf.append("</ul>\n"); return buf.toString(); } /** * Read in the last few lines of a (newline delimited) textfile, or null if * the file doesn't exist. * * Same as FileUtil.readTextFile but uses platform encoding, * not UTF-8, since the wrapper log cannot be configured: * http://stackoverflow.com/questions/14887690/how-do-i-get-the-tanuki-wrapper-log-files-to-be-utf-8-encoded * * Warning - this inefficiently allocates a StringBuilder of size maxNumLines*80, * so don't make it too big. * Warning - converts \r\n to \n * * @param maxNumLines max number of lines (greater than zero) * @return string or null; 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; FileInputStream fis = null; BufferedReader in = null; try { fis = new FileInputStream(f); in = new BufferedReader(new InputStreamReader(fis)); List<String> lines = new ArrayList<String>(maxNumLines); String line = null; while ( (line = in.readLine()) != null) { lines.add(line); if (lines.size() >= maxNumLines) lines.remove(0); } StringBuilder buf = new StringBuilder(lines.size() * 80); for (int i = 0; i < lines.size(); i++) { buf.append(lines.get(i)).append('\n'); } return buf.toString(); } catch (IOException ioe) { return null; } finally { if (in != null) try { in.close(); } catch (IOException ioe) {} } } }