diff --git a/apps/routerconsole/java/src/net/i2p/router/web/OldConsoleHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/OldConsoleHelper.java index b88187681d38c31ec29596f2240e9188a7b390ab..100d7140a79bd6cc0192eeedebf95c97def305a2 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/OldConsoleHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/OldConsoleHelper.java @@ -6,8 +6,14 @@ import java.io.OutputStreamWriter; public class OldConsoleHelper extends HelperBase { + private boolean _full; + public OldConsoleHelper() {} + public void setFull(String f) { + _full = f != null && f.length() > 0; + } + public String getConsole() { try { if (_out != null) { @@ -27,11 +33,11 @@ public class OldConsoleHelper extends HelperBase { StatsGenerator gen = new StatsGenerator(_context); try { if (_out != null) { - gen.generateStatsPage(_out); + gen.generateStatsPage(_out, _full); return ""; } else { ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024); - gen.generateStatsPage(new OutputStreamWriter(baos)); + gen.generateStatsPage(new OutputStreamWriter(baos), _full); return baos.toString(); } } catch (IOException ioe) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java b/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java index e2aee86be8db01f043a3ebc2b77a4e6d3d0991bd..b229e8483925a10a4846031afbcaa9bcded7fa34 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java @@ -22,12 +22,13 @@ import net.i2p.util.Log; public class StatsGenerator { private Log _log; private RouterContext _context; + public StatsGenerator(RouterContext context) { _context = context; _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); buf.append("<div class=\"joblog\"><form action=\"/stats.jsp\">"); 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(" ("); 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( _("These statistics are primarily used for development and debugging.")); out.write(buf.toString()); buf.setLength(0); @@ -85,7 +87,7 @@ public class StatsGenerator { if (_context.statManager().isFrequency(stat)) renderFrequency(stat, buf); else - renderRate(stat, buf); + renderRate(stat, buf, showAll); out.write(buf.toString()); buf.setLength(0); } @@ -99,38 +101,50 @@ public class StatsGenerator { buf.append("<i>"); buf.append(freq.getDescription()); buf.append("</i><br>"); + if (freq.getEventCount() <= 0) { + buf.append(_("No lifetime events")).append("<br>\n"); + return; + } long uptime = _context.router().getUptime(); long periods[] = freq.getPeriods(); Arrays.sort(periods); + buf.append("<ul>"); for (int i = 0; i < periods.length; i++) { if (periods[i] > uptime) break; + buf.append("<li>"); renderPeriod(buf, periods[i], _("frequency")); 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(", max "); + buf.append("; "); + buf.append(_("Highest events per period")); + buf.append(": "); buf.append(num(curFreq.getMaxAverageEventsPerPeriod())); - if ( (curFreq.getMaxAverageEventsPerPeriod() > 0) && (curFreq.getAverageEventsPerPeriod() > 0) ) { - buf.append(", current is "); - buf.append(pct(curFreq.getAverageEventsPerPeriod()/curFreq.getMaxAverageEventsPerPeriod())); - buf.append(" of max"); - } - buf.append(")"); + buf.append("; "); + //if (showAll && (curFreq.getMaxAverageEventsPerPeriod() > 0) && (curFreq.getAverageEventsPerPeriod() > 0) ) { + // buf.append("(current is "); + // buf.append(pct(curFreq.getAverageEventsPerPeriod()/curFreq.getMaxAverageEventsPerPeriod())); + // buf.append(" of max)"); + //} //buf.append(" <i>avg interval between updates:</i> (").append(num(curFreq.getAverageInterval())).append("ms, min "); //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(" events (averaged "); - buf.append(" using the lifetime of "); - buf.append(curFreq.getEventCount()); - buf.append(" events)"); - buf.append("<br>\n"); + buf.append("</li>\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); String d = rate.getDescription(); if (! "".equals(d)) { @@ -153,40 +167,39 @@ public class StatsGenerator { buf.append("<li>"); renderPeriod(buf, periods[i], _("rate")); if (curRate.getLastEventCount() > 0) { - buf.append( "<i>").append(_("avg value")).append(":</i> ("); + buf.append(_("Average")).append(":</i> "); buf.append(num(curRate.getAverageValue())); - buf.append(" peak "); + buf.append("; "); + buf.append(_("Highest average")); + buf.append(": "); buf.append(num(curRate.getExtremeAverageValue())); - buf.append(", ["); - buf.append(pct(curRate.getPercentageOfExtremeValue())); - buf.append(" of max"); - buf.append(", and "); - buf.append(pct(curRate.getPercentageOfLifetimeValue())); - buf.append(" of lifetime average]"); - - 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("; "); + + // This is rarely interesting + // Don't bother to translate + if (showAll) { + buf.append("Highest total in a period: "); + buf.append(num(curRate.getExtremeTotalValue())); + buf.append("; "); + } + + // Saturation stats, which nobody understands, even when it isn't meaningless + // Don't bother to translate + if (showAll && curRate.getLifetimeTotalEventTime() > 0) { + buf.append("Saturation: "); buf.append(pct(curRate.getLastEventSaturation())); - buf.append(")"); - buf.append(" <i>saturated limit:</i> ("); + buf.append("; Saturated limit: "); buf.append(num(curRate.getLastSaturationLimit())); - buf.append(")"); - buf.append(" <i>peak saturation:</i> ("); + buf.append("; Peak saturation: "); buf.append(pct(curRate.getExtremeEventSaturation())); - buf.append(")"); - buf.append(" <i>peak saturated limit:</i> ("); + buf.append("; Peak saturated limit: "); buf.append(num(curRate.getExtremeSaturationLimit())); - buf.append(")"); + buf.append("; "); } - buf.append(" <i>").append(_("events")).append(":</i> "); - buf.append(curRate.getLastEventCount()); - buf.append(" <i>in this period which ended:</i> "); - buf.append(DataHelper.formatDuration(now - curRate.getLastCoalesceDate())); - buf.append(" ago "); + + 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 { buf.append(" <i>").append(_("No events")).append("</i> "); } @@ -194,38 +207,38 @@ public class StatsGenerator { if (numPeriods > 0) { double avgFrequency = curRate.getLifetimeEventCount() / (double)numPeriods; double peakFrequency = curRate.getExtremeEventCount(); - buf.append(" (").append(_("lifetime average")).append(": "); + buf.append(" (").append(_("Average event count")).append(": "); 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(")"); } if (curRate.getSummaryListener() != null) { buf.append(" <a href=\"viewstat.jsp?stat=").append(name); 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("&period=").append(periods[i]).append("&showEvents=true\" title=\"Render summarized event counts\">events</a>"); - buf.append(" (as <a href=\"viewstat.jsp?stat=").append(name); + buf.append("&period=").append(periods[i]).append("&showEvents=true\">").append(_("Graph Event Count")).append("</a> - "); + buf.append("<a href=\"viewstat.jsp?stat=").append(name); buf.append("&period=").append(periods[i]); - buf.append("&format=xml\" title=\"Dump stat history as XML\">XML</a>"); - buf.append(" in a format <a href=\"http://people.ee.ethz.ch/~oetiker/webtools/rrdtool\">RRDTool</a> understands)"); + buf.append("&format=xml\">").append(_("Export Data as XML")).append("</a>"); } buf.append("</li>\n"); } // 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(" over "); - buf.append(rate.getLifetimeEventCount()); - buf.append(" events<br></li>"); - buf.append("</ul>"); - buf.append("<br>\n"); + buf.append(" ("); + buf.append(ngettext((int) rate.getLifetimeEventCount(), "1 event", "{0} events")); + buf.append(")<br></li>" + + "</ul>" + + "<br>\n"); } private static void renderPeriod(StringBuilder buf, long period, String name) { buf.append("<b>"); - buf.append(DataHelper.formatDuration(period)); + buf.append(DataHelper.formatDuration2(period)); buf.append(" "); buf.append(name); buf.append(":</b> "); @@ -246,4 +259,9 @@ public class StatsGenerator { private String _(String s, Object o) { 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); + } } diff --git a/apps/routerconsole/jsp/stats.jsp b/apps/routerconsole/jsp/stats.jsp index d140d7c2cce6dbc1e017204ee1799ace9888c812..61466d98617fb8b4a1b05991938148824b691c77 100644 --- a/apps/routerconsole/jsp/stats.jsp +++ b/apps/routerconsole/jsp/stats.jsp @@ -10,6 +10,7 @@ <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="writer" value="<%=out%>" /> +<jsp:setProperty name="oldhelper" property="full" value="<%=request.getParameter("f")%>" /> <h1><%=intl._("I2P Router Statistics")%></h1> <div class="main" id="main"> <jsp:getProperty name="oldhelper" property="stats" /> diff --git a/core/java/src/net/i2p/stat/Frequency.java b/core/java/src/net/i2p/stat/Frequency.java index 6be7dc3c908223df3d9e3079c66864ad53f15def..facde2fc006dc7283322d04cf2eb32a96e4a728a 100644 --- a/core/java/src/net/i2p/stat/Frequency.java +++ b/core/java/src/net/i2p/stat/Frequency.java @@ -1,30 +1,41 @@ 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 { private double _avgInterval; private double _minAverageInterval; - private long _period; + private final long _period; private long _lastEvent; - private long _start = now(); - private long _count = 0; + private final long _start = now(); + private long _count; private final Object _lock = this; // new Object(); // in case we want to do fancy sync later + /** @param period ms */ 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() { - synchronized (_lock) { return _period; - } } - /** when did the last event occur? */ + /** + * when did the last event occur? + * @deprecated unused + */ public long getLastEvent() { synchronized (_lock) { return _lastEvent; @@ -34,7 +45,7 @@ public class Frequency { /** * on average over the last $period, after how many milliseconds are events coming in, * as calculated during the last event occurrence? - * + * @return milliseconds; returns period + 1 if no events in previous period */ public double getAverageInterval() { 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() { synchronized (_lock) { 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() { synchronized (_lock) { 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() { synchronized (_lock) { - if (_minAverageInterval > 0) return _period / _minAverageInterval; - + if (_minAverageInterval > 0 && _minAverageInterval <= _period) return _period / _minAverageInterval; + 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() { synchronized (_lock) { long duration = now() - _start; - if ((duration <= 0) || (_count <= 0)) return 0; + if ((duration <= 0) || (_count <= 0)) return Double.MAX_VALUE; return duration / (double) _count; } @@ -80,11 +104,8 @@ public class Frequency { /** using the strict average interval, how many events occur within an average period? */ public double getStrictAverageEventsPerPeriod() { double avgInterval = getStrictAverageInterval(); - synchronized (_lock) { - if (avgInterval > 0) return _period / avgInterval; - - return 0; - } + if (avgInterval > 0) return _period / avgInterval; + return 0; } /** how many events have occurred within the lifetime of this stat? */ @@ -115,18 +136,23 @@ public class Frequency { */ private void recalculate(boolean eventOccurred) { synchronized (_lock) { + // This calculates something of a rolling average interval. long now = now(); long interval = now - _lastEvent; - if (interval >= _period) - interval = _period - 1; + if (interval > _period) + interval = _period; else if (interval <= 0) interval = 1; - double oldWeight = 1 - (interval / (float) _period); - double newWeight = (interval / (float) _period); - - double oldInterval = _avgInterval * oldWeight; - double newInterval = interval * newWeight; - _avgInterval = oldInterval + newInterval; + if (interval >= _period && !eventOccurred) { + // ensure getAverageEventsPerPeriod() will return 0 + _avgInterval = _period + 1; + } else { + double oldWeight = 1 - (interval / (float) _period); + double newWeight = (interval / (float) _period); + double oldInterval = _avgInterval * oldWeight; + double newInterval = interval * newWeight; + _avgInterval = oldInterval + newInterval; + } 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() { return System.currentTimeMillis(); } diff --git a/core/java/src/net/i2p/stat/FrequencyStat.java b/core/java/src/net/i2p/stat/FrequencyStat.java index 994d26807a5c4af8e7d6d0ff08ba3ff5d5ac4bf2..dfa7bcfe675298c564d2169f4d8d95372c19e4ad 100644 --- a/core/java/src/net/i2p/stat/FrequencyStat.java +++ b/core/java/src/net/i2p/stat/FrequencyStat.java @@ -3,13 +3,13 @@ package net.i2p.stat; /** coordinate an event frequency over various periods */ public class FrequencyStat { /** unique name of the statistic */ - private String _statName; + private final String _statName; /** grouping under which the stat is kept */ - private String _groupName; + private final String _groupName; /** describe the stat */ - private String _description; + private final String _description; /** actual frequency objects for this statistic */ - private Frequency _frequencies[]; + private final Frequency _frequencies[]; public FrequencyStat(String name, String description, String group, long periods[]) { _statName = name; @@ -26,10 +26,12 @@ public class FrequencyStat { _frequencies[i].eventOccurred(); } - /** coalesce all the stats */ + /** + * coalesce all the stats + */ public void coalesceStats() { - //for (int i = 0; i < _frequencies.length; i++) - // _frequencies[i].coalesceStats(); + for (int i = 0; i < _frequencies.length; i++) + _frequencies[i].recalculate(); } public String getName() { @@ -58,9 +60,37 @@ public class FrequencyStat { 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 public int 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); + } + } diff --git a/core/java/src/net/i2p/stat/Rate.java b/core/java/src/net/i2p/stat/Rate.java index 25305bce742c51587d41a88f36139327a29d3898..fd0fae49f00d197c575c866c87cc21aca98bfdd2 100644 --- a/core/java/src/net/i2p/stat/Rate.java +++ b/core/java/src/net/i2p/stat/Rate.java @@ -10,6 +10,7 @@ import net.i2p.util.Log; * 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. * + * If value is always a constant, you should be using Frequency instead. */ public class Rate { private final static Log _log = new Log(Rate.class); @@ -70,7 +71,10 @@ public class Rate { 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() { return _extremeEventCount; } @@ -144,13 +148,50 @@ public class Rate { 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) { - 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 + * 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 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 * (_lastEventCount / (double) _currentEventCount); - if (_lastTotalValue > _extremeTotalValue) { + if (_lastTotalValue >= _extremeTotalValue) { // get the most recent if identical _extremeTotalValue = _lastTotalValue; _extremeEventCount = _lastEventCount; _extremeTotalEventTime = _lastTotalEventTime; @@ -220,7 +261,10 @@ public class Rate { 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() { if ((_extremeTotalValue != 0) && (_extremeEventCount > 0)) 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 * 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() { 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? * - * @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() { 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 * 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() { 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? * * @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() { 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? - * + * Warning- returns ratio, not percentage (i.e. it is not multiplied by 100 here) */ public double getPercentageOfLifetimeValue() { if ((_lastTotalValue != 0) && (_lifetimeTotalValue != 0)) { @@ -500,6 +547,7 @@ public class Rate { return System.currentTimeMillis(); //Clock.getInstance().now(); } +/****** public static void main(String args[]) { Rate rate = new Rate(1000); for (int i = 0; i < 50; i++) { @@ -532,4 +580,5 @@ public class Rate { } catch (InterruptedException ie) { // nop } } +******/ } diff --git a/core/java/src/net/i2p/stat/StatManager.java b/core/java/src/net/i2p/stat/StatManager.java index 0393d577a339e312e67a6d5dba4f5c1dbfee541c..cc47e00414f5c62607505f135c1893b1429efb00 100644 --- a/core/java/src/net/i2p/stat/StatManager.java +++ b/core/java/src/net/i2p/stat/StatManager.java @@ -140,12 +140,17 @@ public class StatManager { 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() { - synchronized (_frequencyStats) { - for (Iterator<FrequencyStat> iter = _frequencyStats.values().iterator(); iter.hasNext();) { - FrequencyStat stat = iter.next(); - if (stat != null) { - stat.coalesceStats(); + if (++coalesceCounter % FREQ_COALESCE_RATE == 0) { + synchronized (_frequencyStats) { + for (FrequencyStat stat : _frequencyStats.values()) { + if (stat != null) { + stat.coalesceStats(); + } } } } diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index 03afd73b0591a21862d8d5641af5283c9233af48..5a00f154c84bbb299408be373b5eb789532676b7 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -280,7 +280,7 @@ public class StatisticsManager implements Service { 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) { if (num < 0) num = 0;