* Stats: Improve Frequency, enable coalescing; cleanup and javadocs
* stats.jsp: Cleanup, more tagging, hide obscure stuff unless ?f=1
This commit is contained in:
@ -6,8 +6,14 @@ import java.io.OutputStreamWriter;
|
|||||||
|
|
||||||
|
|
||||||
public class OldConsoleHelper extends HelperBase {
|
public class OldConsoleHelper extends HelperBase {
|
||||||
|
private boolean _full;
|
||||||
|
|
||||||
public OldConsoleHelper() {}
|
public OldConsoleHelper() {}
|
||||||
|
|
||||||
|
public void setFull(String f) {
|
||||||
|
_full = f != null && f.length() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public String getConsole() {
|
public String getConsole() {
|
||||||
try {
|
try {
|
||||||
if (_out != null) {
|
if (_out != null) {
|
||||||
@ -27,11 +33,11 @@ public class OldConsoleHelper extends HelperBase {
|
|||||||
StatsGenerator gen = new StatsGenerator(_context);
|
StatsGenerator gen = new StatsGenerator(_context);
|
||||||
try {
|
try {
|
||||||
if (_out != null) {
|
if (_out != null) {
|
||||||
gen.generateStatsPage(_out);
|
gen.generateStatsPage(_out, _full);
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
|
||||||
gen.generateStatsPage(new OutputStreamWriter(baos));
|
gen.generateStatsPage(new OutputStreamWriter(baos), _full);
|
||||||
return baos.toString();
|
return baos.toString();
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
@ -22,12 +22,13 @@ import net.i2p.util.Log;
|
|||||||
public class StatsGenerator {
|
public class StatsGenerator {
|
||||||
private Log _log;
|
private Log _log;
|
||||||
private RouterContext _context;
|
private RouterContext _context;
|
||||||
|
|
||||||
public StatsGenerator(RouterContext context) {
|
public StatsGenerator(RouterContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
_log = context.logManager().getLog(StatsGenerator.class);
|
_log = context.logManager().getLog(StatsGenerator.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateStatsPage(Writer out) throws IOException {
|
public void generateStatsPage(Writer out, boolean showAll) throws IOException {
|
||||||
StringBuilder buf = new StringBuilder(16*1024);
|
StringBuilder buf = new StringBuilder(16*1024);
|
||||||
buf.append("<div class=\"joblog\"><form action=\"/stats.jsp\">");
|
buf.append("<div class=\"joblog\"><form action=\"/stats.jsp\">");
|
||||||
buf.append("<select name=\"go\" onChange='location.href=this.value'>");
|
buf.append("<select name=\"go\" onChange='location.href=this.value'>");
|
||||||
@ -58,8 +59,9 @@ public class StatsGenerator {
|
|||||||
|
|
||||||
buf.append(_("Statistics gathered during this router's uptime")).append(" (");
|
buf.append(_("Statistics gathered during this router's uptime")).append(" (");
|
||||||
long uptime = _context.router().getUptime();
|
long uptime = _context.router().getUptime();
|
||||||
buf.append(DataHelper.formatDuration(uptime));
|
buf.append(DataHelper.formatDuration2(uptime));
|
||||||
buf.append("). ").append( _("The data gathered is quantized over a 1 minute period, so should just be used as an estimate."));
|
buf.append("). ").append( _("The data gathered is quantized over a 1 minute period, so should just be used as an estimate."));
|
||||||
|
buf.append(' ').append( _("These statistics are primarily used for development and debugging."));
|
||||||
|
|
||||||
out.write(buf.toString());
|
out.write(buf.toString());
|
||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
@ -85,7 +87,7 @@ public class StatsGenerator {
|
|||||||
if (_context.statManager().isFrequency(stat))
|
if (_context.statManager().isFrequency(stat))
|
||||||
renderFrequency(stat, buf);
|
renderFrequency(stat, buf);
|
||||||
else
|
else
|
||||||
renderRate(stat, buf);
|
renderRate(stat, buf, showAll);
|
||||||
out.write(buf.toString());
|
out.write(buf.toString());
|
||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
}
|
}
|
||||||
@ -99,38 +101,50 @@ public class StatsGenerator {
|
|||||||
buf.append("<i>");
|
buf.append("<i>");
|
||||||
buf.append(freq.getDescription());
|
buf.append(freq.getDescription());
|
||||||
buf.append("</i><br>");
|
buf.append("</i><br>");
|
||||||
|
if (freq.getEventCount() <= 0) {
|
||||||
|
buf.append(_("No lifetime events")).append("<br>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
long uptime = _context.router().getUptime();
|
long uptime = _context.router().getUptime();
|
||||||
long periods[] = freq.getPeriods();
|
long periods[] = freq.getPeriods();
|
||||||
Arrays.sort(periods);
|
Arrays.sort(periods);
|
||||||
|
buf.append("<ul>");
|
||||||
for (int i = 0; i < periods.length; i++) {
|
for (int i = 0; i < periods.length; i++) {
|
||||||
if (periods[i] > uptime)
|
if (periods[i] > uptime)
|
||||||
break;
|
break;
|
||||||
|
buf.append("<li>");
|
||||||
renderPeriod(buf, periods[i], _("frequency"));
|
renderPeriod(buf, periods[i], _("frequency"));
|
||||||
Frequency curFreq = freq.getFrequency(periods[i]);
|
Frequency curFreq = freq.getFrequency(periods[i]);
|
||||||
buf.append(" <i>avg per period:</i> (");
|
buf.append(DataHelper.formatDuration2(Math.round(curFreq.getAverageInterval())));
|
||||||
|
buf.append("; ");
|
||||||
|
buf.append(_("Rolling average events per period"));
|
||||||
|
buf.append(": ");
|
||||||
buf.append(num(curFreq.getAverageEventsPerPeriod()));
|
buf.append(num(curFreq.getAverageEventsPerPeriod()));
|
||||||
buf.append(", max ");
|
buf.append("; ");
|
||||||
|
buf.append(_("Highest events per period"));
|
||||||
|
buf.append(": ");
|
||||||
buf.append(num(curFreq.getMaxAverageEventsPerPeriod()));
|
buf.append(num(curFreq.getMaxAverageEventsPerPeriod()));
|
||||||
if ( (curFreq.getMaxAverageEventsPerPeriod() > 0) && (curFreq.getAverageEventsPerPeriod() > 0) ) {
|
buf.append("; ");
|
||||||
buf.append(", current is ");
|
//if (showAll && (curFreq.getMaxAverageEventsPerPeriod() > 0) && (curFreq.getAverageEventsPerPeriod() > 0) ) {
|
||||||
buf.append(pct(curFreq.getAverageEventsPerPeriod()/curFreq.getMaxAverageEventsPerPeriod()));
|
// buf.append("(current is ");
|
||||||
buf.append(" of max");
|
// buf.append(pct(curFreq.getAverageEventsPerPeriod()/curFreq.getMaxAverageEventsPerPeriod()));
|
||||||
}
|
// buf.append(" of max)");
|
||||||
buf.append(")");
|
//}
|
||||||
//buf.append(" <i>avg interval between updates:</i> (").append(num(curFreq.getAverageInterval())).append("ms, min ");
|
//buf.append(" <i>avg interval between updates:</i> (").append(num(curFreq.getAverageInterval())).append("ms, min ");
|
||||||
//buf.append(num(curFreq.getMinAverageInterval())).append("ms)");
|
//buf.append(num(curFreq.getMinAverageInterval())).append("ms)");
|
||||||
buf.append(" <i>strict average per period:</i> ");
|
buf.append(_("Lifetime average events per period")).append(": ");
|
||||||
buf.append(num(curFreq.getStrictAverageEventsPerPeriod()));
|
buf.append(num(curFreq.getStrictAverageEventsPerPeriod()));
|
||||||
buf.append(" events (averaged ");
|
buf.append("</li>\n");
|
||||||
buf.append(" using the lifetime of ");
|
|
||||||
buf.append(curFreq.getEventCount());
|
|
||||||
buf.append(" events)");
|
|
||||||
buf.append("<br>\n");
|
|
||||||
}
|
}
|
||||||
buf.append("<br>\n");
|
// Display the strict average
|
||||||
|
buf.append("<li><b>").append(_("Lifetime average frequency")).append(":</b> ");
|
||||||
|
buf.append(DataHelper.formatDuration2(freq.getFrequency()));
|
||||||
|
buf.append(" (");
|
||||||
|
buf.append(ngettext((int) freq.getEventCount(), "1 event", "{0} events"));
|
||||||
|
buf.append(")</li></ul><br>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRate(String name, StringBuilder buf) {
|
private void renderRate(String name, StringBuilder buf, boolean showAll) {
|
||||||
RateStat rate = _context.statManager().getRate(name);
|
RateStat rate = _context.statManager().getRate(name);
|
||||||
String d = rate.getDescription();
|
String d = rate.getDescription();
|
||||||
if (! "".equals(d)) {
|
if (! "".equals(d)) {
|
||||||
@ -153,40 +167,39 @@ public class StatsGenerator {
|
|||||||
buf.append("<li>");
|
buf.append("<li>");
|
||||||
renderPeriod(buf, periods[i], _("rate"));
|
renderPeriod(buf, periods[i], _("rate"));
|
||||||
if (curRate.getLastEventCount() > 0) {
|
if (curRate.getLastEventCount() > 0) {
|
||||||
buf.append( "<i>").append(_("avg value")).append(":</i> (");
|
buf.append(_("Average")).append(":</i> ");
|
||||||
buf.append(num(curRate.getAverageValue()));
|
buf.append(num(curRate.getAverageValue()));
|
||||||
buf.append(" peak ");
|
buf.append("; ");
|
||||||
|
buf.append(_("Highest average"));
|
||||||
|
buf.append(": ");
|
||||||
buf.append(num(curRate.getExtremeAverageValue()));
|
buf.append(num(curRate.getExtremeAverageValue()));
|
||||||
buf.append(", [");
|
buf.append("; ");
|
||||||
buf.append(pct(curRate.getPercentageOfExtremeValue()));
|
|
||||||
buf.append(" of max");
|
// This is rarely interesting
|
||||||
buf.append(", and ");
|
// Don't bother to translate
|
||||||
buf.append(pct(curRate.getPercentageOfLifetimeValue()));
|
if (showAll) {
|
||||||
buf.append(" of lifetime average]");
|
buf.append("Highest total in a period: ");
|
||||||
|
buf.append(num(curRate.getExtremeTotalValue()));
|
||||||
buf.append(")");
|
buf.append("; ");
|
||||||
buf.append(" <i>highest total period value:</i> (");
|
|
||||||
buf.append(num(curRate.getExtremeTotalValue()));
|
|
||||||
buf.append(")");
|
|
||||||
if (curRate.getLifetimeTotalEventTime() > 0) {
|
|
||||||
buf.append(" <i>saturation:</i> (");
|
|
||||||
buf.append(pct(curRate.getLastEventSaturation()));
|
|
||||||
buf.append(")");
|
|
||||||
buf.append(" <i>saturated limit:</i> (");
|
|
||||||
buf.append(num(curRate.getLastSaturationLimit()));
|
|
||||||
buf.append(")");
|
|
||||||
buf.append(" <i>peak saturation:</i> (");
|
|
||||||
buf.append(pct(curRate.getExtremeEventSaturation()));
|
|
||||||
buf.append(")");
|
|
||||||
buf.append(" <i>peak saturated limit:</i> (");
|
|
||||||
buf.append(num(curRate.getExtremeSaturationLimit()));
|
|
||||||
buf.append(")");
|
|
||||||
}
|
}
|
||||||
buf.append(" <i>").append(_("events")).append(":</i> ");
|
|
||||||
buf.append(curRate.getLastEventCount());
|
// Saturation stats, which nobody understands, even when it isn't meaningless
|
||||||
buf.append(" <i>in this period which ended:</i> ");
|
// Don't bother to translate
|
||||||
buf.append(DataHelper.formatDuration(now - curRate.getLastCoalesceDate()));
|
if (showAll && curRate.getLifetimeTotalEventTime() > 0) {
|
||||||
buf.append(" ago ");
|
buf.append("Saturation: ");
|
||||||
|
buf.append(pct(curRate.getLastEventSaturation()));
|
||||||
|
buf.append("; Saturated limit: ");
|
||||||
|
buf.append(num(curRate.getLastSaturationLimit()));
|
||||||
|
buf.append("; Peak saturation: ");
|
||||||
|
buf.append(pct(curRate.getExtremeEventSaturation()));
|
||||||
|
buf.append("; Peak saturated limit: ");
|
||||||
|
buf.append(num(curRate.getExtremeSaturationLimit()));
|
||||||
|
buf.append("; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append(ngettext((int) curRate.getLastEventCount(), "There was 1 event", "There were {0} events"));
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(_("in this period which ended {0} ago.", DataHelper.formatDuration2(now - curRate.getLastCoalesceDate())));
|
||||||
} else {
|
} else {
|
||||||
buf.append(" <i>").append(_("No events")).append("</i> ");
|
buf.append(" <i>").append(_("No events")).append("</i> ");
|
||||||
}
|
}
|
||||||
@ -194,38 +207,38 @@ public class StatsGenerator {
|
|||||||
if (numPeriods > 0) {
|
if (numPeriods > 0) {
|
||||||
double avgFrequency = curRate.getLifetimeEventCount() / (double)numPeriods;
|
double avgFrequency = curRate.getLifetimeEventCount() / (double)numPeriods;
|
||||||
double peakFrequency = curRate.getExtremeEventCount();
|
double peakFrequency = curRate.getExtremeEventCount();
|
||||||
buf.append(" (").append(_("lifetime average")).append(": ");
|
buf.append(" (").append(_("Average event count")).append(": ");
|
||||||
buf.append(num(avgFrequency));
|
buf.append(num(avgFrequency));
|
||||||
buf.append(", ").append(_("peak average")).append(": ");
|
buf.append("; ").append(_("Events in peak period")).append(": ");
|
||||||
|
// This isn't really the highest event count, but the event count during the period with the highest total value.
|
||||||
buf.append(curRate.getExtremeEventCount());
|
buf.append(curRate.getExtremeEventCount());
|
||||||
buf.append(")");
|
buf.append(")");
|
||||||
}
|
}
|
||||||
if (curRate.getSummaryListener() != null) {
|
if (curRate.getSummaryListener() != null) {
|
||||||
buf.append(" <a href=\"viewstat.jsp?stat=").append(name);
|
buf.append(" <a href=\"viewstat.jsp?stat=").append(name);
|
||||||
buf.append("&period=").append(periods[i]);
|
buf.append("&period=").append(periods[i]);
|
||||||
buf.append("\" title=\"Render summarized data\">render</a>");
|
buf.append("\">").append(_("Graph Data")).append("</a> - ");
|
||||||
buf.append(" <a href=\"viewstat.jsp?stat=").append(name);
|
buf.append(" <a href=\"viewstat.jsp?stat=").append(name);
|
||||||
buf.append("&period=").append(periods[i]).append("&showEvents=true\" title=\"Render summarized event counts\">events</a>");
|
buf.append("&period=").append(periods[i]).append("&showEvents=true\">").append(_("Graph Event Count")).append("</a> - ");
|
||||||
buf.append(" (as <a href=\"viewstat.jsp?stat=").append(name);
|
buf.append("<a href=\"viewstat.jsp?stat=").append(name);
|
||||||
buf.append("&period=").append(periods[i]);
|
buf.append("&period=").append(periods[i]);
|
||||||
buf.append("&format=xml\" title=\"Dump stat history as XML\">XML</a>");
|
buf.append("&format=xml\">").append(_("Export Data as XML")).append("</a>");
|
||||||
buf.append(" in a format <a href=\"http://people.ee.ethz.ch/~oetiker/webtools/rrdtool\">RRDTool</a> understands)");
|
|
||||||
}
|
}
|
||||||
buf.append("</li>\n");
|
buf.append("</li>\n");
|
||||||
}
|
}
|
||||||
// Display the strict average
|
// Display the strict average
|
||||||
buf.append("<li><b>").append(_("lifetime average value")).append(":</b> ");
|
buf.append("<li><b>").append(_("Lifetime average value")).append(":</b> ");
|
||||||
buf.append(num(rate.getLifetimeAverageValue()));
|
buf.append(num(rate.getLifetimeAverageValue()));
|
||||||
buf.append(" over ");
|
buf.append(" (");
|
||||||
buf.append(rate.getLifetimeEventCount());
|
buf.append(ngettext((int) rate.getLifetimeEventCount(), "1 event", "{0} events"));
|
||||||
buf.append(" events<br></li>");
|
buf.append(")<br></li>" +
|
||||||
buf.append("</ul>");
|
"</ul>" +
|
||||||
buf.append("<br>\n");
|
"<br>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renderPeriod(StringBuilder buf, long period, String name) {
|
private static void renderPeriod(StringBuilder buf, long period, String name) {
|
||||||
buf.append("<b>");
|
buf.append("<b>");
|
||||||
buf.append(DataHelper.formatDuration(period));
|
buf.append(DataHelper.formatDuration2(period));
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
buf.append(name);
|
buf.append(name);
|
||||||
buf.append(":</b> ");
|
buf.append(":</b> ");
|
||||||
@ -246,4 +259,9 @@ public class StatsGenerator {
|
|||||||
private String _(String s, Object o) {
|
private String _(String s, Object o) {
|
||||||
return Messages.getString(s, o, _context);
|
return Messages.getString(s, o, _context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** translate a string */
|
||||||
|
private String ngettext(int n, String s, String p) {
|
||||||
|
return Messages.getString(n, s, p, _context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" />
|
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" />
|
||||||
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||||
<jsp:setProperty name="oldhelper" property="writer" value="<%=out%>" />
|
<jsp:setProperty name="oldhelper" property="writer" value="<%=out%>" />
|
||||||
|
<jsp:setProperty name="oldhelper" property="full" value="<%=request.getParameter("f")%>" />
|
||||||
<h1><%=intl._("I2P Router Statistics")%></h1>
|
<h1><%=intl._("I2P Router Statistics")%></h1>
|
||||||
<div class="main" id="main">
|
<div class="main" id="main">
|
||||||
<jsp:getProperty name="oldhelper" property="stats" />
|
<jsp:getProperty name="oldhelper" property="stats" />
|
||||||
|
@ -1,30 +1,41 @@
|
|||||||
package net.i2p.stat;
|
package net.i2p.stat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage the calculation of a moving event frequency over a certain period.
|
* Manage the calculation of a moving average event frequency over a certain period.
|
||||||
*
|
*
|
||||||
|
* This provides lifetime, and rolling average, frequency counts.
|
||||||
|
* Unlike Rate, it does not support "bucketed" averages.
|
||||||
|
* There is no tracking of the event frequency in the current or last bucket.
|
||||||
|
* There are no buckets at all.
|
||||||
|
*
|
||||||
|
* Depending on what you want, a rolling average might be better than buckets.
|
||||||
|
* Or not.
|
||||||
*/
|
*/
|
||||||
public class Frequency {
|
public class Frequency {
|
||||||
private double _avgInterval;
|
private double _avgInterval;
|
||||||
private double _minAverageInterval;
|
private double _minAverageInterval;
|
||||||
private long _period;
|
private final long _period;
|
||||||
private long _lastEvent;
|
private long _lastEvent;
|
||||||
private long _start = now();
|
private final long _start = now();
|
||||||
private long _count = 0;
|
private long _count;
|
||||||
private final Object _lock = this; // new Object(); // in case we want to do fancy sync later
|
private final Object _lock = this; // new Object(); // in case we want to do fancy sync later
|
||||||
|
|
||||||
|
/** @param period ms */
|
||||||
public Frequency(long period) {
|
public Frequency(long period) {
|
||||||
setPeriod(period);
|
_period = period;
|
||||||
|
_avgInterval = period + 1;
|
||||||
|
_minAverageInterval = _avgInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** how long is this frequency averaged over? */
|
/** how long is this frequency averaged over? (ms) */
|
||||||
public long getPeriod() {
|
public long getPeriod() {
|
||||||
synchronized (_lock) {
|
|
||||||
return _period;
|
return _period;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** when did the last event occur? */
|
/**
|
||||||
|
* when did the last event occur?
|
||||||
|
* @deprecated unused
|
||||||
|
*/
|
||||||
public long getLastEvent() {
|
public long getLastEvent() {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
return _lastEvent;
|
return _lastEvent;
|
||||||
@ -34,7 +45,7 @@ public class Frequency {
|
|||||||
/**
|
/**
|
||||||
* on average over the last $period, after how many milliseconds are events coming in,
|
* on average over the last $period, after how many milliseconds are events coming in,
|
||||||
* as calculated during the last event occurrence?
|
* as calculated during the last event occurrence?
|
||||||
*
|
* @return milliseconds; returns period + 1 if no events in previous period
|
||||||
*/
|
*/
|
||||||
public double getAverageInterval() {
|
public double getAverageInterval() {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
@ -42,14 +53,21 @@ public class Frequency {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** what is the lowest average interval (aka most frequent) we have seen? */
|
/**
|
||||||
|
* what is the lowest average interval (aka most frequent) we have seen? (ms)
|
||||||
|
* @return milliseconds; returns period + 1 if no events in previous period
|
||||||
|
* @deprecated unused
|
||||||
|
*/
|
||||||
public double getMinAverageInterval() {
|
public double getMinAverageInterval() {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
return _minAverageInterval;
|
return _minAverageInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** calculate how many events would occur in a period given the current average */
|
/**
|
||||||
|
* Calculate how many events would occur in a period given the current (rolling) average.
|
||||||
|
* Use getStrictAverageInterval() for the real lifetime average.
|
||||||
|
*/
|
||||||
public double getAverageEventsPerPeriod() {
|
public double getAverageEventsPerPeriod() {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
if (_avgInterval > 0) return _period / _avgInterval;
|
if (_avgInterval > 0) return _period / _avgInterval;
|
||||||
@ -58,20 +76,26 @@ public class Frequency {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** calculate how many events would occur in a period given the maximum average */
|
/**
|
||||||
|
* Calculate how many events would occur in a period given the maximum rolling average.
|
||||||
|
* Use getStrictAverageEventsPerPeriod() for the real lifetime average.
|
||||||
|
*/
|
||||||
public double getMaxAverageEventsPerPeriod() {
|
public double getMaxAverageEventsPerPeriod() {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
if (_minAverageInterval > 0) return _period / _minAverageInterval;
|
if (_minAverageInterval > 0 && _minAverageInterval <= _period) return _period / _minAverageInterval;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** over the lifetime of this stat, without any decay or weighting, what was the average interval between events? */
|
/**
|
||||||
|
* Over the lifetime of this stat, without any decay or weighting, what was the average interval between events? (ms)
|
||||||
|
* @return milliseconds; returns Double.MAX_VALUE if no events ever
|
||||||
|
*/
|
||||||
public double getStrictAverageInterval() {
|
public double getStrictAverageInterval() {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
long duration = now() - _start;
|
long duration = now() - _start;
|
||||||
if ((duration <= 0) || (_count <= 0)) return 0;
|
if ((duration <= 0) || (_count <= 0)) return Double.MAX_VALUE;
|
||||||
|
|
||||||
return duration / (double) _count;
|
return duration / (double) _count;
|
||||||
}
|
}
|
||||||
@ -80,11 +104,8 @@ public class Frequency {
|
|||||||
/** using the strict average interval, how many events occur within an average period? */
|
/** using the strict average interval, how many events occur within an average period? */
|
||||||
public double getStrictAverageEventsPerPeriod() {
|
public double getStrictAverageEventsPerPeriod() {
|
||||||
double avgInterval = getStrictAverageInterval();
|
double avgInterval = getStrictAverageInterval();
|
||||||
synchronized (_lock) {
|
if (avgInterval > 0) return _period / avgInterval;
|
||||||
if (avgInterval > 0) return _period / avgInterval;
|
return 0;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** how many events have occurred within the lifetime of this stat? */
|
/** how many events have occurred within the lifetime of this stat? */
|
||||||
@ -115,18 +136,23 @@ public class Frequency {
|
|||||||
*/
|
*/
|
||||||
private void recalculate(boolean eventOccurred) {
|
private void recalculate(boolean eventOccurred) {
|
||||||
synchronized (_lock) {
|
synchronized (_lock) {
|
||||||
|
// This calculates something of a rolling average interval.
|
||||||
long now = now();
|
long now = now();
|
||||||
long interval = now - _lastEvent;
|
long interval = now - _lastEvent;
|
||||||
if (interval >= _period)
|
if (interval > _period)
|
||||||
interval = _period - 1;
|
interval = _period;
|
||||||
else if (interval <= 0) interval = 1;
|
else if (interval <= 0) interval = 1;
|
||||||
|
|
||||||
double oldWeight = 1 - (interval / (float) _period);
|
if (interval >= _period && !eventOccurred) {
|
||||||
double newWeight = (interval / (float) _period);
|
// ensure getAverageEventsPerPeriod() will return 0
|
||||||
|
_avgInterval = _period + 1;
|
||||||
double oldInterval = _avgInterval * oldWeight;
|
} else {
|
||||||
double newInterval = interval * newWeight;
|
double oldWeight = 1 - (interval / (float) _period);
|
||||||
_avgInterval = oldInterval + newInterval;
|
double newWeight = (interval / (float) _period);
|
||||||
|
double oldInterval = _avgInterval * oldWeight;
|
||||||
|
double newInterval = interval * newWeight;
|
||||||
|
_avgInterval = oldInterval + newInterval;
|
||||||
|
}
|
||||||
|
|
||||||
if ((_avgInterval < _minAverageInterval) || (_minAverageInterval <= 0)) _minAverageInterval = _avgInterval;
|
if ((_avgInterval < _minAverageInterval) || (_minAverageInterval <= 0)) _minAverageInterval = _avgInterval;
|
||||||
|
|
||||||
@ -137,30 +163,6 @@ public class Frequency {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPeriod(long milliseconds) {
|
|
||||||
synchronized (_lock) {
|
|
||||||
_period = milliseconds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLastEvent(long when) {
|
|
||||||
synchronized (_lock) {
|
|
||||||
_lastEvent = when;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAverageInterval(double msInterval) {
|
|
||||||
synchronized (_lock) {
|
|
||||||
_avgInterval = msInterval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMinAverageInterval(double minAverageInterval) {
|
|
||||||
synchronized (_lock) {
|
|
||||||
_minAverageInterval = minAverageInterval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static long now() {
|
private final static long now() {
|
||||||
return System.currentTimeMillis();
|
return System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ package net.i2p.stat;
|
|||||||
/** coordinate an event frequency over various periods */
|
/** coordinate an event frequency over various periods */
|
||||||
public class FrequencyStat {
|
public class FrequencyStat {
|
||||||
/** unique name of the statistic */
|
/** unique name of the statistic */
|
||||||
private String _statName;
|
private final String _statName;
|
||||||
/** grouping under which the stat is kept */
|
/** grouping under which the stat is kept */
|
||||||
private String _groupName;
|
private final String _groupName;
|
||||||
/** describe the stat */
|
/** describe the stat */
|
||||||
private String _description;
|
private final String _description;
|
||||||
/** actual frequency objects for this statistic */
|
/** actual frequency objects for this statistic */
|
||||||
private Frequency _frequencies[];
|
private final Frequency _frequencies[];
|
||||||
|
|
||||||
public FrequencyStat(String name, String description, String group, long periods[]) {
|
public FrequencyStat(String name, String description, String group, long periods[]) {
|
||||||
_statName = name;
|
_statName = name;
|
||||||
@ -26,10 +26,12 @@ public class FrequencyStat {
|
|||||||
_frequencies[i].eventOccurred();
|
_frequencies[i].eventOccurred();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** coalesce all the stats */
|
/**
|
||||||
|
* coalesce all the stats
|
||||||
|
*/
|
||||||
public void coalesceStats() {
|
public void coalesceStats() {
|
||||||
//for (int i = 0; i < _frequencies.length; i++)
|
for (int i = 0; i < _frequencies.length; i++)
|
||||||
// _frequencies[i].coalesceStats();
|
_frequencies[i].recalculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -58,9 +60,37 @@ public class FrequencyStat {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME missing equals() method FIXME */
|
/**
|
||||||
|
* @return lifetime event count
|
||||||
|
* @since 0.8.2
|
||||||
|
*/
|
||||||
|
public long getEventCount() {
|
||||||
|
if ( (_frequencies == null) || (_frequencies.length <= 0) ) return 0;
|
||||||
|
return _frequencies[0].getEventCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return lifetime average frequency in millisedonds, i.e. the average time between events, or Long.MAX_VALUE if no events ever
|
||||||
|
* @since 0.8.2
|
||||||
|
*/
|
||||||
|
public long getFrequency() {
|
||||||
|
if ( (_frequencies == null) || (_frequencies.length <= 0) ) return Long.MAX_VALUE;
|
||||||
|
double d = _frequencies[0].getStrictAverageInterval();
|
||||||
|
if (d > _frequencies[0].getPeriod())
|
||||||
|
return Long.MAX_VALUE;
|
||||||
|
return Math.round(d);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return _statName.hashCode();
|
return _statName.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.2 */
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if ((obj == null) || (obj.getClass() != FrequencyStat.class)) return false;
|
||||||
|
return _statName.equals(((FrequencyStat)obj)._statName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import net.i2p.util.Log;
|
|||||||
* average value over a period, the number of events in that period, the maximum number
|
* average value over a period, the number of events in that period, the maximum number
|
||||||
* of events (using the interval between events), and lifetime data.
|
* of events (using the interval between events), and lifetime data.
|
||||||
*
|
*
|
||||||
|
* If value is always a constant, you should be using Frequency instead.
|
||||||
*/
|
*/
|
||||||
public class Rate {
|
public class Rate {
|
||||||
private final static Log _log = new Log(Rate.class);
|
private final static Log _log = new Log(Rate.class);
|
||||||
@ -70,7 +71,10 @@ public class Rate {
|
|||||||
return _extremeTotalValue;
|
return _extremeTotalValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** when the max(totalValue) was achieved, how many events occurred in that period? */
|
/**
|
||||||
|
* when the max(totalValue) was achieved, how many events occurred in that period?
|
||||||
|
* Note that this is not necesarily the highest event count; that isn't tracked.
|
||||||
|
*/
|
||||||
public long getExtremeEventCount() {
|
public long getExtremeEventCount() {
|
||||||
return _extremeEventCount;
|
return _extremeEventCount;
|
||||||
}
|
}
|
||||||
@ -144,13 +148,50 @@ public class Rate {
|
|||||||
load(props, prefix, treatAsCurrent);
|
load(props, prefix, treatAsCurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** accrue the data in the current period as an instantaneous event */
|
/**
|
||||||
|
* Accrue the data in the current period as an instantaneous event.
|
||||||
|
* If value is always a constant, you should be using Frequency instead.
|
||||||
|
* If you always use this call, eventDuration is always zero,
|
||||||
|
* and the various get*Saturation*() and get*EventTime() methods will return zero.
|
||||||
|
*/
|
||||||
public void addData(long value) {
|
public void addData(long value) {
|
||||||
addData(value, 0);
|
synchronized (_lock) {
|
||||||
|
_currentTotalValue += value;
|
||||||
|
_currentEventCount++;
|
||||||
|
_lifetimeTotalValue += value;
|
||||||
|
_lifetimeEventCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accrue the data in the current period as if the event took the specified amount of time
|
* Accrue the data in the current period as if the event took the specified amount of time
|
||||||
|
* If value is always a constant, you should be using Frequency instead.
|
||||||
|
* If eventDuration is nonzero, then the various get*Saturation*() and get*EventTime()
|
||||||
|
* methods will also return nonzero.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* There are at least 4 possible strategies for eventDuration:
|
||||||
|
*
|
||||||
|
* 1) eventDuration is always zero.
|
||||||
|
* The various get*Saturation*() and get*EventTime() methods will return zero.
|
||||||
|
*
|
||||||
|
* 2) Each eventDuration is relatively small, and reflects processing time.
|
||||||
|
* This is probably the original meaning of "saturation", as it allows you
|
||||||
|
* to track how much time is spent gathering the stats.
|
||||||
|
* get*EventTime() will be close to 0.
|
||||||
|
* get*EventSaturation() will return values close to 0,
|
||||||
|
* get*SaturationLimit() will return adjusted values for the totals.
|
||||||
|
*
|
||||||
|
* 3) The total of the eventDurations are approximately equal to total elapsed time.
|
||||||
|
* get*EventTime() will be close to the period.
|
||||||
|
* get*EventSaturation() will return values close to 1,
|
||||||
|
* get*SaturationLimit() will return adjusted values for the totals.
|
||||||
|
*
|
||||||
|
* 4) Each eventDuration is not a duration at all, but someother independent data.
|
||||||
|
* get*EventTime() may be used to retrieve the data.
|
||||||
|
* get*EventSaturation() are probably useless.
|
||||||
|
* get*SaturationLimit() are probably useless.
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param value value to accrue in the current period
|
* @param value value to accrue in the current period
|
||||||
* @param eventDuration how long it took to accrue this data (set to 0 if it was instantaneous)
|
* @param eventDuration how long it took to accrue this data (set to 0 if it was instantaneous)
|
||||||
@ -195,7 +236,7 @@ public class Rate {
|
|||||||
correctedTotalValue = _currentTotalValue *
|
correctedTotalValue = _currentTotalValue *
|
||||||
(_lastEventCount / (double) _currentEventCount);
|
(_lastEventCount / (double) _currentEventCount);
|
||||||
|
|
||||||
if (_lastTotalValue > _extremeTotalValue) {
|
if (_lastTotalValue >= _extremeTotalValue) { // get the most recent if identical
|
||||||
_extremeTotalValue = _lastTotalValue;
|
_extremeTotalValue = _lastTotalValue;
|
||||||
_extremeEventCount = _lastEventCount;
|
_extremeEventCount = _lastEventCount;
|
||||||
_extremeTotalEventTime = _lastTotalEventTime;
|
_extremeTotalEventTime = _lastTotalEventTime;
|
||||||
@ -220,7 +261,10 @@ public class Rate {
|
|||||||
return 0.0D;
|
return 0.0D;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** what was the average value across the events in the most active period? */
|
/**
|
||||||
|
* During the extreme period (i.e. the period with the highest total value),
|
||||||
|
* what was the average value?
|
||||||
|
*/
|
||||||
public double getExtremeAverageValue() {
|
public double getExtremeAverageValue() {
|
||||||
if ((_extremeTotalValue != 0) && (_extremeEventCount > 0))
|
if ((_extremeTotalValue != 0) && (_extremeEventCount > 0))
|
||||||
return _extremeTotalValue / _extremeEventCount;
|
return _extremeTotalValue / _extremeEventCount;
|
||||||
@ -240,7 +284,7 @@ public class Rate {
|
|||||||
* During the last period, how much of the time was spent actually processing events in proportion
|
* During the last period, how much of the time was spent actually processing events in proportion
|
||||||
* to how many events could have occurred if there were no intervals?
|
* to how many events could have occurred if there were no intervals?
|
||||||
*
|
*
|
||||||
* @return percentage, or 0 if event times aren't used
|
* @return ratio, or 0 if event times aren't used
|
||||||
*/
|
*/
|
||||||
public double getLastEventSaturation() {
|
public double getLastEventSaturation() {
|
||||||
if ((_lastEventCount > 0) && (_lastTotalEventTime > 0)) {
|
if ((_lastEventCount > 0) && (_lastTotalEventTime > 0)) {
|
||||||
@ -256,10 +300,11 @@ public class Rate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* During the extreme period, how much of the time was spent actually processing events
|
* During the extreme period (i.e. the period with the highest total value),
|
||||||
|
* how much of the time was spent actually processing events
|
||||||
* in proportion to how many events could have occurred if there were no intervals?
|
* in proportion to how many events could have occurred if there were no intervals?
|
||||||
*
|
*
|
||||||
* @return percentage, or 0 if the statistic doesn't use event times
|
* @return ratio, or 0 if the statistic doesn't use event times
|
||||||
*/
|
*/
|
||||||
public double getExtremeEventSaturation() {
|
public double getExtremeEventSaturation() {
|
||||||
if ((_extremeEventCount > 0) && (_extremeTotalEventTime > 0)) {
|
if ((_extremeEventCount > 0) && (_extremeTotalEventTime > 0)) {
|
||||||
@ -274,7 +319,7 @@ public class Rate {
|
|||||||
* During the lifetime of this stat, how much of the time was spent actually processing events in proportion
|
* During the lifetime of this stat, how much of the time was spent actually processing events in proportion
|
||||||
* to how many events could have occurred if there were no intervals?
|
* to how many events could have occurred if there were no intervals?
|
||||||
*
|
*
|
||||||
* @return percentage, or 0 if event times aren't used
|
* @return ratio, or 0 if event times aren't used
|
||||||
*/
|
*/
|
||||||
public double getLifetimeEventSaturation() {
|
public double getLifetimeEventSaturation() {
|
||||||
if ((_lastEventCount > 0) && (_lifetimeTotalEventTime > 0)) {
|
if ((_lastEventCount > 0) && (_lifetimeTotalEventTime > 0)) {
|
||||||
@ -311,7 +356,8 @@ public class Rate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* using the extreme period's rate, what is the total value that could have been
|
* During the extreme period (i.e. the period with the highest total value),
|
||||||
|
* what is the total value that could have been
|
||||||
* sent if events were constant?
|
* sent if events were constant?
|
||||||
*
|
*
|
||||||
* @return event total at saturation, or 0 if no event times are measured
|
* @return event total at saturation, or 0 if no event times are measured
|
||||||
@ -328,8 +374,9 @@ public class Rate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How large was the last period's value as compared to the largest period ever?
|
* What was the total value, compared to the total value in
|
||||||
*
|
* the extreme period (i.e. the period with the highest total value),
|
||||||
|
* Warning- returns ratio, not percentage (i.e. it is not multiplied by 100 here)
|
||||||
*/
|
*/
|
||||||
public double getPercentageOfExtremeValue() {
|
public double getPercentageOfExtremeValue() {
|
||||||
if ((_lastTotalValue != 0) && (_extremeTotalValue != 0))
|
if ((_lastTotalValue != 0) && (_extremeTotalValue != 0))
|
||||||
@ -340,7 +387,7 @@ public class Rate {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* How large was the last period's value as compared to the lifetime average value?
|
* How large was the last period's value as compared to the lifetime average value?
|
||||||
*
|
* Warning- returns ratio, not percentage (i.e. it is not multiplied by 100 here)
|
||||||
*/
|
*/
|
||||||
public double getPercentageOfLifetimeValue() {
|
public double getPercentageOfLifetimeValue() {
|
||||||
if ((_lastTotalValue != 0) && (_lifetimeTotalValue != 0)) {
|
if ((_lastTotalValue != 0) && (_lifetimeTotalValue != 0)) {
|
||||||
@ -500,6 +547,7 @@ public class Rate {
|
|||||||
return System.currentTimeMillis(); //Clock.getInstance().now();
|
return System.currentTimeMillis(); //Clock.getInstance().now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
Rate rate = new Rate(1000);
|
Rate rate = new Rate(1000);
|
||||||
for (int i = 0; i < 50; i++) {
|
for (int i = 0; i < 50; i++) {
|
||||||
@ -532,4 +580,5 @@ public class Rate {
|
|||||||
} catch (InterruptedException ie) { // nop
|
} catch (InterruptedException ie) { // nop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
******/
|
||||||
}
|
}
|
||||||
|
@ -140,12 +140,17 @@ public class StatManager {
|
|||||||
if (stat != null) stat.addData(data, eventDuration);
|
if (stat != null) stat.addData(data, eventDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int coalesceCounter;
|
||||||
|
/** every this many minutes for frequencies */
|
||||||
|
private static final int FREQ_COALESCE_RATE = 9;
|
||||||
|
|
||||||
public void coalesceStats() {
|
public void coalesceStats() {
|
||||||
synchronized (_frequencyStats) {
|
if (++coalesceCounter % FREQ_COALESCE_RATE == 0) {
|
||||||
for (Iterator<FrequencyStat> iter = _frequencyStats.values().iterator(); iter.hasNext();) {
|
synchronized (_frequencyStats) {
|
||||||
FrequencyStat stat = iter.next();
|
for (FrequencyStat stat : _frequencyStats.values()) {
|
||||||
if (stat != null) {
|
if (stat != null) {
|
||||||
stat.coalesceStats();
|
stat.coalesceStats();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ public class StatisticsManager implements Service {
|
|||||||
stats.setProperty("stat_bandwidthReceiveBps.60m", str);
|
stats.setProperty("stat_bandwidthReceiveBps.60m", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPeriod(Rate rate) { return DataHelper.formatDuration(rate.getPeriod()); }
|
private static String getPeriod(Rate rate) { return DataHelper.formatDuration(rate.getPeriod()); }
|
||||||
|
|
||||||
private final String num(double num) {
|
private final String num(double num) {
|
||||||
if (num < 0) num = 0;
|
if (num < 0) num = 0;
|
||||||
|
Reference in New Issue
Block a user