Author: ljn1981
Date: 2006-09-27 16:57:52 +0000 (Wed, 27 Sep 2006)
New Revision: 10518
Added:
trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java
Modified:
trunk/freenet/src/freenet/clients/http/FProxyToadlet.java
Log:
Make a statistics page.
For now it show the same info as is shown on the top of the darknet page.
We should have a talk about which pieces should still be on the darknet page,
if any and the rest should be removed.
The layout of the status page should also be improved to take advantage of
having a whole page for stats now.
Modified: trunk/freenet/src/freenet/clients/http/FProxyToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/FProxyToadlet.java 2006-09-27
16:26:43 UTC (rev 10517)
+++ trunk/freenet/src/freenet/clients/http/FProxyToadlet.java 2006-09-27
16:57:52 UTC (rev 10518)
@@ -394,6 +394,9 @@
QueueToadlet queueToadlet = new QueueToadlet(core,
core.getFCPServer(), client);
server.register(queueToadlet, "/queue/", true, "Queue",
"manage queued requests");
+ StatisticsToadlet statisticsToadlet = new
StatisticsToadlet(node, core, client);
+ server.register(statisticsToadlet, "/stats/", true,
"Statistics", "view statistics");
+
LocalFileInsertToadlet localFileInsertToadlet = new
LocalFileInsertToadlet(core, client);
server.register(localFileInsertToadlet, "/files/",
true);
Added: trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java
2006-09-27 16:26:43 UTC (rev 10517)
+++ trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java
2006-09-27 16:57:52 UTC (rev 10518)
@@ -0,0 +1,277 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.text.DecimalFormat;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.io.comm.IOStatisticCollector;
+import freenet.node.Node;
+import freenet.node.NodeClientCore;
+import freenet.node.PeerNodeStatus;
+import freenet.support.HTMLNode;
+import freenet.support.SizeUtil;
+import freenet.support.TimeUtil;
+
+public class StatisticsToadlet extends Toadlet {
+
+ public class MyComparator implements Comparator {
+
+ public int compare(Object arg0, Object arg1) {
+ Object[] row0 = (Object[])arg0;
+ Object[] row1 = (Object[])arg1;
+ Integer stat0 = (Integer) row0[2]; // 2 = status
+ Integer stat1 = (Integer) row1[2];
+ int x = stat0.compareTo(stat1);
+ if(x != 0) return x;
+ String name0 = (String) row0[9]; // 9 = node name
+ String name1 = (String) row1[9];
+ return
name0.toLowerCase().compareTo(name1.toLowerCase());
+ }
+
+ }
+
+ private final Node node;
+ private final NodeClientCore core;
+
+ protected StatisticsToadlet(Node n, NodeClientCore core,
HighLevelSimpleClient client) {
+ super(client);
+ this.node = n;
+ this.core = core;
+ }
+
+ public String supportedMethods() {
+ return "GET";
+ }
+
+ /**
+ * Counts the peers in <code>peerNodes</code> that have the specified
+ * status.
+ * @param peerNodeStatuses The peer nodes' statuses
+ * @param status The status to count
+ * @return The number of peers that have the specified status.
+ */
+ private int getPeerStatusCount(PeerNodeStatus[] peerNodeStatuses, int
status) {
+ int count = 0;
+ for (int peerIndex = 0, peerCount = peerNodeStatuses.length;
peerIndex < peerCount; peerIndex++) {
+ if (peerNodeStatuses[peerIndex].getStatusValue() ==
status) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public void handleGet(URI uri, ToadletContext ctx) throws
ToadletContextClosedException, IOException, RedirectException {
+
+ final boolean advancedEnabled = node.isAdvancedDarknetEnabled();
+
+ /* gather connection statistics */
+ PeerNodeStatus[] peerNodeStatuses = node.getPeerNodeStatuses();
+ Arrays.sort(peerNodeStatuses, new Comparator() {
+ public int compare(Object first, Object second) {
+ PeerNodeStatus firstNode = (PeerNodeStatus)
first;
+ PeerNodeStatus secondNode = (PeerNodeStatus)
second;
+ int statusDifference =
firstNode.getStatusValue() - secondNode.getStatusValue();
+ if (statusDifference != 0) {
+ return statusDifference;
+ }
+ return
firstNode.getName().compareToIgnoreCase(secondNode.getName());
+ }
+ });
+
+ int numberOfConnected = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_CONNECTED);
+ int numberOfRoutingBackedOff =
getPeerStatusCount(peerNodeStatuses, Node.PEER_NODE_STATUS_ROUTING_BACKED_OFF);
+ int numberOfTooNew = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_TOO_NEW);
+ int numberOfTooOld = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_TOO_OLD);
+ int numberOfDisconnected = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_DISCONNECTED);
+ int numberOfNeverConnected =
getPeerStatusCount(peerNodeStatuses, Node.PEER_NODE_STATUS_NEVER_CONNECTED);
+ int numberOfDisabled = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_DISABLED);
+ int numberOfBursting = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_BURSTING);
+ int numberOfListening = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_LISTENING);
+ int numberOfListenOnly = getPeerStatusCount(peerNodeStatuses,
Node.PEER_NODE_STATUS_LISTEN_ONLY);
+
+ HTMLNode pageNode = ctx.getPageMaker().getPageNode("Statistics
for " + node.getMyName());
+ HTMLNode contentNode =
ctx.getPageMaker().getContentNode(pageNode);
+
+ // FIXME! We need some nice images
+ long now = System.currentTimeMillis();
+
+ contentNode.addChild(core.alerts.createSummary());
+
+ if(peerNodeStatuses.length>0){
+
+ /* node status values */
+ long nodeUptimeSeconds = (now - node.startupTime) /
1000;
+ int bwlimitDelayTime = (int) node.getBwlimitDelayTime();
+ int nodeAveragePingTime = (int)
node.getNodeAveragePingTime();
+ int networkSizeEstimateSession =
node.getNetworkSizeEstimate(-1);
+ int networkSizeEstimate24h = 0;
+ int networkSizeEstimate48h = 0;
+ if(nodeUptimeSeconds > (24*60*60)) { // 24 hours
+ networkSizeEstimate24h =
node.getNetworkSizeEstimate(now - (24*60*60*1000)); // 48 hours
+ }
+ if(nodeUptimeSeconds > (48*60*60)) { // 48 hours
+ networkSizeEstimate48h =
node.getNetworkSizeEstimate(now - (48*60*60*1000)); // 48 hours
+ }
+ DecimalFormat fix4 = new DecimalFormat("0.0000");
+ double missRoutingDistance =
node.missRoutingDistance.currentValue();
+ DecimalFormat fix1 = new DecimalFormat("##0.0%");
+ double backedoffPercent =
node.backedoffPercent.currentValue();
+ String nodeUptimeString =
TimeUtil.formatTime(nodeUptimeSeconds * 1000); // *1000 to convert to
milliseconds
+
+ HTMLNode overviewTable = contentNode.addChild("table",
"class", "column");
+ HTMLNode overviewTableRow =
overviewTable.addChild("tr");
+ HTMLNode nextTableCell =
overviewTableRow.addChild("td", "class", "first");
+
+ /* node status overview box */
+ if(advancedEnabled) {
+ HTMLNode overviewInfobox =
nextTableCell.addChild("div", "class", "infobox");
+ overviewInfobox.addChild("div", "class",
"infobox-header", "Node status overview");
+ HTMLNode overviewInfoboxContent =
overviewInfobox.addChild("div", "class", "infobox-content");
+ HTMLNode overviewList =
overviewInfoboxContent.addChild("ul");
+ overviewList.addChild("li",
"bwlimitDelayTime:\u00a0" + bwlimitDelayTime + "ms");
+ overviewList.addChild("li",
"nodeAveragePingTime:\u00a0" + nodeAveragePingTime + "ms");
+ overviewList.addChild("li",
"networkSizeEstimateSession:\u00a0" + networkSizeEstimateSession +
"\u00a0nodes");
+ if(nodeUptimeSeconds > (24*60*60)) { // 24
hours
+ overviewList.addChild("li",
"networkSizeEstimate24h:\u00a0" + networkSizeEstimate24h + "\u00a0nodes");
+ }
+ if(nodeUptimeSeconds > (48*60*60)) { // 48
hours
+ overviewList.addChild("li",
"networkSizeEstimate48h:\u00a0" + networkSizeEstimate48h + "\u00a0nodes");
+ }
+ overviewList.addChild("li", "nodeUptime:\u00a0"
+ nodeUptimeString);
+ overviewList.addChild("li",
"missRoutingDistance:\u00a0" + fix4.format(missRoutingDistance));
+ overviewList.addChild("li",
"backedoffPercent:\u00a0" + fix1.format(backedoffPercent));
+ overviewList.addChild("li",
"pInstantReject:\u00a0" + fix1.format(node.pRejectIncomingInstantly()));
+ nextTableCell = overviewTableRow.addChild("td");
+ }
+
+ // Activity box
+ int numInserts = node.getNumInserts();
+ int numRequests = node.getNumRequests();
+ int numTransferringRequests =
node.getNumTransferringRequests();
+ int numARKFetchers = node.getNumARKFetchers();
+
+ HTMLNode activityInfobox =
nextTableCell.addChild("div", "class", "infobox");
+ activityInfobox.addChild("div", "class",
"infobox-header", "Current activity");
+ HTMLNode activityInfoboxContent =
activityInfobox.addChild("div", "class", "infobox-content");
+ if ((numInserts == 0) && (numRequests == 0) &&
(numTransferringRequests == 0) && (numARKFetchers == 0)) {
+ activityInfoboxContent.addChild("#", "Your node
is not processing any requests right now.");
+ } else {
+ HTMLNode activityList =
activityInfoboxContent.addChild("ul");
+ if (numInserts > 0) {
+ activityList.addChild("li",
"Inserts:\u00a0" + numInserts);
+ }
+ if (numRequests > 0) {
+ activityList.addChild("li",
"Requests:\u00a0" + numRequests);
+ }
+ if (numTransferringRequests > 0) {
+ activityList.addChild("li",
"Transferring\u00a0Requests:\u00a0" + numTransferringRequests);
+ }
+ if (advancedEnabled) {
+ if (numARKFetchers > 0) {
+ activityList.addChild("li",
"ARK\u00a0Fetch\u00a0Requests:\u00a0" + numARKFetchers);
+ }
+ long[] total =
IOStatisticCollector.getTotalIO();
+ long total_output_rate = (total[0]) /
nodeUptimeSeconds;
+ long total_input_rate = (total[1]) /
nodeUptimeSeconds;
+ long totalPayload =
node.getTotalPayloadSent();
+ long total_payload_rate = totalPayload
/ nodeUptimeSeconds;
+ int percent = (int) (100 * totalPayload
/ total[0]);
+ activityList.addChild("li", "Total
Output:\u00a0" + SizeUtil.formatSize(total[0]) + "\u00a0(" +
SizeUtil.formatSize(total_output_rate) + "ps)");
+ activityList.addChild("li", "Payload
Output:\u00a0" + SizeUtil.formatSize(totalPayload) + "\u00a0(" +
SizeUtil.formatSize(total_payload_rate) + "ps) ("+percent+"%)");
+ activityList.addChild("li", "Total
Input:\u00a0" + SizeUtil.formatSize(total[1]) + "\u00a0(" +
SizeUtil.formatSize(total_input_rate) + "ps)");
+ long[] rate = node.getNodeIOStats();
+ long delta = (rate[5] - rate[2]) / 1000;
+ long output_rate = (rate[3] - rate[0])
/ delta;
+ long input_rate = (rate[4] - rate[1]) /
delta;
+ activityList.addChild("li", "Output
Rate:\u00a0" + SizeUtil.formatSize(output_rate) + "ps");
+ activityList.addChild("li", "Input
Rate:\u00a0" + SizeUtil.formatSize(input_rate) + "ps");
+ }
+ }
+
+ nextTableCell = advancedEnabled ?
overviewTableRow.addChild("td") : overviewTableRow.addChild("td", "class",
"last");
+
+ // Peer statistics box
+ HTMLNode peerStatsInfobox =
nextTableCell.addChild("div", "class", "infobox");
+ peerStatsInfobox.addChild("div", "class",
"infobox-header", "Peer statistics");
+ HTMLNode peerStatsContent =
peerStatsInfobox.addChild("div", "class", "infobox-content");
+ HTMLNode peerStatsList =
peerStatsContent.addChild("ul");
+ if (numberOfConnected > 0) {
+ HTMLNode peerStatsConnectedListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsConnectedListItem.addChild("span", new
String[] { "class", "title", "style" }, new String[] { "peer_connected",
"Connected: We're successfully connected to these nodes", "border-bottom: 1px
dotted; cursor: help;" }, "Connected");
+ peerStatsConnectedListItem.addChild("span",
":\u00a0" + numberOfConnected);
+ }
+ if (numberOfRoutingBackedOff > 0) {
+ HTMLNode peerStatsRoutingBackedOffListItem =
peerStatsList.addChild("li").addChild("span");
+
peerStatsRoutingBackedOffListItem.addChild("span", new String[] { "class",
"title", "style" }, new String[] { "peer_backedoff", (advancedEnabled ?
"Connected but backed off: These peers are connected but we're backed off of
them" : "Busy: These peers are connected but they're busy") + ", so the node is
not routing requests to them", "border-bottom: 1px dotted; cursor: help;" },
advancedEnabled ? "Backed off" : "Busy");
+
peerStatsRoutingBackedOffListItem.addChild("span", ":\u00a0" +
numberOfRoutingBackedOff);
+ }
+ if (numberOfTooNew > 0) {
+ HTMLNode peerStatsTooNewListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsTooNewListItem.addChild("span", new
String[] { "class", "title", "style" }, new String[] { "peer_too_new",
"Connected but too new: These peers' minimum mandatory build is higher than
this node's build. This node is not routing requests to them", "border-bottom:
1px dotted; cursor: help;" }, "Too New");
+ peerStatsTooNewListItem.addChild("span",
":\u00a0" + numberOfTooNew);
+ }
+ if (numberOfTooOld > 0) {
+ HTMLNode peerStatsTooOldListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsTooOldListItem.addChild("span", new
String[] { "class", "title", "style" }, new String[] { "peer_too_old",
"Connected but too old: This node's minimum mandatory build is higher than
these peers' build. This node is not routing requests to them", "border-bottom:
1px dotted; cursor: help;" }, "Too Old");
+ peerStatsTooOldListItem.addChild("span",
":\u00a0" + numberOfTooOld);
+ }
+ if (numberOfDisconnected > 0) {
+ HTMLNode peerStatsDisconnectedListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsDisconnectedListItem.addChild("span",
new String[] { "class", "title", "style" }, new String[] { "peer_disconnected",
"Not connected: No connection so far but this node is continuously trying to
connect", "border-bottom: 1px dotted; cursor: help;" }, "Disconnected");
+ peerStatsDisconnectedListItem.addChild("span",
":\u00a0" + numberOfDisconnected);
+ }
+ if (numberOfNeverConnected > 0) {
+ HTMLNode peerStatsNeverConnectedListItem =
peerStatsList.addChild("li").addChild("span");
+
peerStatsNeverConnectedListItem.addChild("span", new String[] { "class",
"title", "style" }, new String[] { "peer_never_connected", "Never Connected:
The node has never connected with these peers", "border-bottom: 1px dotted;
cursor: help;" }, "Never Connected");
+
peerStatsNeverConnectedListItem.addChild("span", ":\u00a0" +
numberOfNeverConnected);
+ }
+ if (numberOfDisabled > 0) {
+ HTMLNode peerStatsDisabledListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsDisabledListItem.addChild("span", new
String[] { "class", "title", "style" }, new String[] { "peer_disabled", "Not
connected and disabled: because the user has instructed to not connect to peers
", "border-bottom: 1px dotted; cursor: help;" }, "Disabled");
+ peerStatsDisabledListItem.addChild("span",
":\u00a0" + numberOfDisabled);
+ }
+ if (numberOfBursting > 0) {
+ HTMLNode peerStatsBurstingListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsBurstingListItem.addChild("span", new
String[] { "class", "title", "style" }, new String[] { "peer_bursting", "Not
connected and bursting: this node is, for a short period, trying to connect to
these peers because the user has set BurstOnly on them", "border-bottom: 1px
dotted; cursor: help;" }, "Bursting");
+ peerStatsBurstingListItem.addChild("span",
":\u00a0" + numberOfBursting);
+ }
+ if (numberOfListening > 0) {
+ HTMLNode peerStatsListeningListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsListeningListItem.addChild("span", new
String[] { "class", "title", "style" }, new String[] { "peer_listening", "Not
connected but listening: this node won't try to connect to these peers very
often because the user has set BurstOnly on them", "border-bottom: 1px dotted;
cursor: help;" }, "Listening");
+ peerStatsListeningListItem.addChild("span",
":\u00a0" + numberOfListening);
+ }
+ if (numberOfListenOnly > 0) {
+ HTMLNode peerStatsListenOnlyListItem =
peerStatsList.addChild("li").addChild("span");
+ peerStatsListenOnlyListItem.addChild("span",
new String[] { "class", "title", "style" }, new String[] { "peer_listen_only",
"Not connected and listen only: this node won't try to connect to these peers
at all because the user has set ListenOnly on them", "border-bottom: 1px
dotted; cursor: help;" }, "Listen Only");
+ peerStatsListenOnlyListItem.addChild("span",
":\u00a0" + numberOfListenOnly);
+ }
+
+ // Peer routing backoff reason box
+ if(advancedEnabled) {
+ nextTableCell = overviewTableRow.addChild("td",
"class", "last");
+ HTMLNode backoffReasonInfobox =
nextTableCell.addChild("div", "class", "infobox");
+ backoffReasonInfobox.addChild("div", "class",
"infobox-header", "Peer backoff reasons");
+ HTMLNode backoffReasonContent =
backoffReasonInfobox.addChild("div", "class", "infobox-content");
+ String [] routingBackoffReasons =
node.getPeerNodeRoutingBackoffReasons();
+ if(routingBackoffReasons.length == 0) {
+ backoffReasonContent.addChild("#",
"Good, your node is not backed off from any peers!");
+ } else {
+ HTMLNode reasonList =
backoffReasonContent.addChild("ul");
+ for(int
i=0;i<routingBackoffReasons.length;i++) {
+ int reasonCount =
node.getPeerNodeRoutingBackoffReasonSize(routingBackoffReasons[i]);
+ if(reasonCount > 0) {
+
reasonList.addChild("li", routingBackoffReasons[i] + '\u00a0' + reasonCount);
+ }
+ }
+ }
+ }
+ }
+
+ StringBuffer pageBuffer = new StringBuffer();
+ pageNode.generate(pageBuffer);
+ this.writeReply(ctx, 200, "text/html", "OK",
pageBuffer.toString());
+ }
+}