Updates to /graph

- Graph image updates
  - Custom fonts with fallback
  - Left-align legend
  - Thinner restart and graph lines for clarity
  - Separate legend and display period date
  - Text tidyups
- Tweak layout and text of configuration options
- Default width to 400px to avoid info truncation
- 15s and 30s refresh options
This commit is contained in:
str4d
2017-01-07 18:28:55 +00:00
parent 053ebd7079
commit 894186e975
2 changed files with 40 additions and 30 deletions

View File

@ -32,7 +32,7 @@ public class GraphHelper extends FormHandler {
private static final String PROP_REFRESH = "routerconsole.graphRefresh"; private static final String PROP_REFRESH = "routerconsole.graphRefresh";
private static final String PROP_PERIODS = "routerconsole.graphPeriods"; private static final String PROP_PERIODS = "routerconsole.graphPeriods";
private static final String PROP_EVENTS = "routerconsole.graphEvents"; private static final String PROP_EVENTS = "routerconsole.graphEvents";
public static final int DEFAULT_X = 250; public static final int DEFAULT_X = 400;
public static final int DEFAULT_Y = 100; public static final int DEFAULT_Y = 100;
private static final int DEFAULT_REFRESH = 5*60; private static final int DEFAULT_REFRESH = 5*60;
private static final int DEFAULT_PERIODS = 60; private static final int DEFAULT_PERIODS = 60;
@ -344,7 +344,7 @@ public class GraphHelper extends FormHandler {
+ "\">"; + "\">";
} }
private static final int[] times = { 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 }; private static final int[] times = { 15, 30, 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 };
public String getForm() { public String getForm() {
if (StatSummarizer.isDisabled()) if (StatSummarizer.isDisabled())
@ -356,15 +356,16 @@ public class GraphHelper extends FormHandler {
try { try {
_out.write("<br><h3 id=\"graphdisplay\">" + _t("Configure Graph Display") + " <a href=\"configstats\">[" + _t("Select Stats") + "]</a></h3>"); _out.write("<br><h3 id=\"graphdisplay\">" + _t("Configure Graph Display") + " <a href=\"configstats\">[" + _t("Select Stats") + "]</a></h3>");
_out.write("<form action=\"graphs\" method=\"POST\">\n" + _out.write("<form action=\"graphs\" method=\"POST\">\n" +
"<input type=\"hidden\" name=\"action\" value=\"save\">\n" + "<table><tr><td><input type=\"hidden\" name=\"action\" value=\"save\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + nonce + "\" >\n"); "<input type=\"hidden\" name=\"nonce\" value=\"" + nonce + "\" >\n");
_out.write(_t("Periods") + ": <input size=\"5\" style=\"text-align: right;\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\"><br>\n"); _out.write(_t("Display period") + ":</td><td colspan=\"2\"><input size=\"5\" style=\"text-align: right;\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\">" + _t("minutes") + "</td></tr><tr><td>\n");
_out.write(_t("Plot averages") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : HelperBase.CHECKED) + "> "); _out.write(_t("Plot type") + ":</td><td colspan=\"2\">");
_out.write(_t("or")+ " " +_t("plot events") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"true\" "+ (_showEvents ? HelperBase.CHECKED : "") + "><br>\n"); _out.write("<input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : HelperBase.CHECKED) + ">" + _t("Averages") + "&nbsp;&nbsp;&nbsp;");
_out.write(_t("Image sizes") + ": " + _t("width") + ": <input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"width\" value=\"" + _width _out.write ("<input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"true\" "+ (_showEvents ? HelperBase.CHECKED : "") + ">" + _t("Events") + "</td></tr><tr><td>\n");
+ "\"> " + _t("pixels") + ", " + _t("height") + ": <input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"height\" value=\"" + _height _out.write(_t("Graph size") + ":</td><td><input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"width\" value=\"" + _width
+ "\"> " + _t("pixels") + "<br>\n"); + "\">" + _t("pixels wide") + "&nbsp;&nbsp;&nbsp;<input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"height\" value=\"" + _height
_out.write(_t("Refresh delay") + ": <select name=\"refreshDelay\">"); + "\">" + _t("pixels high") + "</td><td class=\"infohelp\">" + _t("Note: Dimensions are for graph only (excludes title, labels and legend).") + "</td></tr><tr><td>\n");
_out.write(_t("Refresh delay") + ":</td><td colspan=\"2\"><select name=\"refreshDelay\">");
for (int i = 0; i < times.length; i++) { for (int i = 0; i < times.length; i++) {
_out.write("<option value=\""); _out.write("<option value=\"");
_out.write(Integer.toString(times[i])); _out.write(Integer.toString(times[i]));
@ -378,13 +379,12 @@ public class GraphHelper extends FormHandler {
_out.write(_t("Never")); _out.write(_t("Never"));
_out.write("</option>\n"); _out.write("</option>\n");
} }
_out.write("</select><br>\n" + _out.write("</select></td></tr><tr><td>\n" + _t("Persistence") +
_t("Store graph data on disk?") + ":</td><td colspan=\"2\"><input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"persistent\"");
" <input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"persistent\"");
boolean persistent = _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT); boolean persistent = _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT);
if (persistent) if (persistent)
_out.write(HelperBase.CHECKED); _out.write(HelperBase.CHECKED);
_out.write(">" + _out.write(">" + _t("Store graph data on disk") + "</td></tr></table>" +
"<hr><div class=\"formaction\" id=\"graphing\"><input type=\"submit\" class=\"accept\" value=\"" + _t("Save settings and redraw graphs") + "\"></div></form>"); "<hr><div class=\"formaction\" id=\"graphing\"><input type=\"submit\" class=\"accept\" value=\"" + _t("Save settings and redraw graphs") + "\"></div></form>");
} catch (IOException ioe) { } catch (IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();

View File

@ -37,7 +37,7 @@ class SummaryRenderer {
private final I2PAppContext _context; private final I2PAppContext _context;
private static final Color AREA_COLOR = new Color(100, 160, 200, 240); private static final Color AREA_COLOR = new Color(100, 160, 200, 240);
private static final Color LINE_COLOR = new Color(0, 30, 110, 255); private static final Color LINE_COLOR = new Color(0, 30, 110, 255);
private static final Color RESTART_BAR_COLOR = new Color(210, 10, 10, 200); private static final Color RESTART_BAR_COLOR = new Color(223, 13, 13, 255);
public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) { public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) {
_log = ctx.logManager().getLog(SummaryRenderer.class); _log = ctx.logManager().getLog(SummaryRenderer.class);
@ -133,8 +133,11 @@ class SummaryRenderer {
small = small.deriveFont(small.getSize2D() + 2.0f); small = small.deriveFont(small.getSize2D() + 2.0f);
large = large.deriveFont(Font.PLAIN, large.getSize2D() + 3.0f); large = large.deriveFont(Font.PLAIN, large.getSize2D() + 3.0f);
} else { } else {
small = small.deriveFont(small.getSize2D() + 1.0f); // small = small.deriveFont(small.getSize2D() + 1.0f);
large = large.deriveFont(large.getSize2D() + 1.0f); // if specified font family is missing, jrobin will use fallback
small = new Font("Droid Sans Mono", Font.PLAIN, 11);
// large = large.deriveFont(large.getSize2D() + 1.0f);
large = new Font("Droid Sans", Font.PLAIN, 13);
} }
def.setSmallFont(small); def.setSmallFont(small);
def.setLargeFont(large); def.setLargeFont(large);
@ -186,14 +189,14 @@ class SummaryRenderer {
def.datasource(plotName, path, plotName, SummaryListener.CF, _listener.getBackendName()); def.datasource(plotName, path, plotName, SummaryListener.CF, _listener.getBackendName());
if (descr.length() > 0) { if (descr.length() > 0) {
def.area(plotName, AREA_COLOR, descr + "\\r"); def.area(plotName, AREA_COLOR, descr + "\\l");
} else { } else {
def.area(plotName, AREA_COLOR); def.area(plotName, AREA_COLOR);
} }
if (!hideLegend) { if (!hideLegend) {
def.gprint(plotName, SummaryListener.CF, _t("avg") + ": %.2f %s"); def.gprint(plotName, SummaryListener.CF, " " + _t("Avg") + ": %.2f%s");
def.gprint(plotName, "MAX", ' ' + _t("max") + ": %.2f %S"); def.gprint(plotName, "MAX", ' ' + _t("Max") + ": %.2f%S");
def.gprint(plotName, "LAST", ' ' + _t("now") + ": %.2f %S\\r"); def.gprint(plotName, "LAST", ' ' + _t("Now") + ": %.2f%S\\l");
} }
String plotName2 = null; String plotName2 = null;
if (lsnr2 != null) { if (lsnr2 != null) {
@ -202,25 +205,32 @@ class SummaryRenderer {
String path2 = lsnr2.getData().getPath(); String path2 = lsnr2.getData().getPath();
String descr2 = _t(lsnr2.getRate().getRateStat().getDescription()); String descr2 = _t(lsnr2.getRate().getRateStat().getDescription());
def.datasource(plotName2, path2, plotName2, SummaryListener.CF, lsnr2.getBackendName()); def.datasource(plotName2, path2, plotName2, SummaryListener.CF, lsnr2.getBackendName());
def.line(plotName2, LINE_COLOR, descr2 + "\\r", 3); def.line(plotName2, LINE_COLOR, descr2 + "\\l", 2);
if (!hideLegend) { if (!hideLegend) {
def.gprint(plotName2, SummaryListener.CF, _t("avg") + ": %.2f %s"); def.gprint(plotName2, SummaryListener.CF, " " + _t("Avg") + ": %.2f%s");
def.gprint(plotName2, "MAX", ' ' + _t("max") + ": %.2f %S"); def.gprint(plotName2, "MAX", ' ' + _t("Max") + ": %.2f%S");
def.gprint(plotName2, "LAST", ' ' + _t("now") + ": %.2f %S\\r"); def.gprint(plotName2, "LAST", ' ' + _t("Now") + ": %.2f%S\\l");
} }
} }
if (!hideLegend) { if (!hideLegend) {
// '07-Jul 21:09 UTC' with month name in the system locale // '07 Jul 21:09' with month name in the system locale
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM HH:mm"); // TODO: Fix Arabic time display
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM HH:mm");
Map<Long, String> events = ((RouterContext)_context).router().eventLog().getEvents(EventLog.STARTED, start); Map<Long, String> events = ((RouterContext)_context).router().eventLog().getEvents(EventLog.STARTED, start);
for (Map.Entry<Long, String> event : events.entrySet()) { for (Map.Entry<Long, String> event : events.entrySet()) {
long started = event.getKey().longValue(); long started = event.getKey().longValue();
if (started > start && started < end) { if (started > start && started < end) {
String legend = _t("Restart") + ' ' + sdf.format(new Date(started)) + " UTC " + event.getValue() + "\\r"; // String legend = _t("Restart") + ' ' + sdf.format(new Date(started)) + " UTC " + event.getValue() + "\\l";
def.vrule(started / 1000, RESTART_BAR_COLOR, legend, 4.0f); if ("ar".equals(lang)) {
String legend = _t("Restart") + ' ' + sdf.format(new Date(started)) + " - " + event.getValue() + "\\l";
def.vrule(started / 1000, RESTART_BAR_COLOR, legend, 2.0f);
} else {
String legend = _t("Restart") + ' ' + sdf.format(new Date(started)) + " [" + event.getValue() + "]\\l";
def.vrule(started / 1000, RESTART_BAR_COLOR, legend, 2.0f);
}
} }
} }
def.comment(sdf.format(new Date(start)) + " -- " + sdf.format(new Date(end)) + " UTC\\r"); def.comment(sdf.format(new Date(start)) + " " + sdf.format(new Date(end)) + " UTC\\r");
} }
if (!showCredit) if (!showCredit)
def.setShowSignature(false); def.setShowSignature(false);