diff --git a/apps/netmonitor/java/src/net/i2p/netmonitor/DataHarvester.java b/apps/netmonitor/java/src/net/i2p/netmonitor/DataHarvester.java index 1ac63cb6b..5e5f0f148 100644 --- a/apps/netmonitor/java/src/net/i2p/netmonitor/DataHarvester.java +++ b/apps/netmonitor/java/src/net/i2p/netmonitor/DataHarvester.java @@ -5,6 +5,7 @@ import net.i2p.util.Log; import net.i2p.util.Clock; import java.util.Properties; import java.util.Iterator; +import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.Locale; @@ -21,8 +22,22 @@ class DataHarvester { private static final Log _log = new Log(DataHarvester.class); private static final DataHarvester _instance = new DataHarvester(); public static final DataHarvester getInstance() { return _instance; } + /** + * Contains the list of StatGroup objects loaded from the harvest.config file + * {@see StatGroupLoader} where each statGroup defines a set of stats to pull + * from each router's options. + * + */ + private List _statGroups; - protected DataHarvester() {} + /** + * Where are we reading the stat groups from? For now, "harvester.config". + */ + private static final String STAT_GROUP_CONFIG_FILENAME = "harvester.config"; + + protected DataHarvester() { + _statGroups = StatGroupLoader.loadStatGroups(STAT_GROUP_CONFIG_FILENAME); + } /** * Harvest all of the data from the peers and store it in the monitor. @@ -45,9 +60,7 @@ class DataHarvester { _log.info("Harvest the data from " + peer.getIdentity().getHash().toBase64()); harvestRank(monitor, peer, peers); harvestRankAs(monitor, peer); - harvestEncryptionTime(monitor, peer); - harvestDroppedJobs(monitor, peer); - harvestProcessingTime(monitor, peer); + harvestGroups(monitor, peer); } /** @@ -138,88 +151,56 @@ class DataHarvester { } /** - * How long does it take the peer to perform an elGamal encryption? Stored in - * the peer summary as "encryptTime", containing 4 doubles (numMs for 1 minute, - * quantity in the last minute, numMs for 1 hour, quantity in the last hour) - * - * @param peer who are we checking the encryption time of + * Harvest all data points from the peer + * */ - private void harvestEncryptionTime(NetMonitor monitor, RouterInfo peer) { - double minuteMs = getDouble(peer, "stat_crypto.elGamal.encrypt.60s", 0); - double hourMs = getDouble(peer, "stat_crypto.elGamal.encrypt.60m", 0); - double minuteQuantity = getDouble(peer, "stat_crypto.elGamal.encrypt.60s", 7); - double hourQuantity = getDouble(peer, "stat_crypto.elGamal.encrypt.60m", 7); - if ( (minuteMs == -1) || (hourMs == -1) || (minuteQuantity == -1) || (hourQuantity == -1) ) - return; - - double times[] = new double[4]; - times[0] = minuteMs; - times[1] = minuteQuantity; - times[2] = hourMs; - times[3] = hourQuantity; - - String description = "how long it takes to do an ElGamal encryption"; - String valDescr[] = new String[4]; - valDescr[0] = "encryption time avg ms (minute)"; - valDescr[1] = "# encryptions (minute)"; - valDescr[2] = "encryption time avg ms (hour)"; - valDescr[3] = "# encryptions (hour)"; - monitor.addData(peer.getIdentity().getHash().toBase64(), "encryptTime", description, valDescr, peer.getPublished(), times); + private void harvestGroups(NetMonitor monitor, RouterInfo peer) { + _log.debug("Harvesting group data for " + peer.getIdentity().getHash().toBase64()); + for (int i = 0; i < _statGroups.size(); i++) { + StatGroup group = (StatGroup)_statGroups.get(i); + harvestGroup(monitor, peer, group); + } } /** - * How jobs has the peer dropped in the last minute / hour? Stored in - * the peer summary as "droppedJobs", containing 2 doubles (num jobs for 1 minute, - * num jobs for 1 hour) + * Harvest the data points for the given group from the peer and toss them + * into the monitor * - * @param peer who are we checking the frequency of dropping jobs for */ - private void harvestDroppedJobs(NetMonitor monitor, RouterInfo peer) { - double minute = getDouble(peer, "stat_jobQueue.droppedJobs.60s", 0); - double hour = getDouble(peer, "stat_jobQueue.droppedJobs.60m", 0); - double quantity[] = new double[2]; - quantity[0] = minute; - quantity[1] = hour; - if ( (minute == -1) || (hour == -1) ) - return; - - String valDescr[] = new String[2]; - valDescr[0] = "# dropped jobs (minute)"; - valDescr[1] = "# dropped jobs (hour)"; - String description = "how many dropped jobs"; - monitor.addData(peer.getIdentity().getHash().toBase64(), "droppedJobs", description, valDescr, peer.getPublished(), quantity); + private void harvestGroup(NetMonitor monitor, RouterInfo peer, StatGroup group) { + _log.debug("Harvesting group data for " + peer.getIdentity().getHash().toBase64() + " / " + group.getDescription()); + double values[] = harvestGroupValues(peer, group); + if (values == null) return; + + String description = "how long it takes to do an ElGamal encryption"; + String valDescr[] = new String[group.getStatCount()]; + for (int i = 0; i < group.getStatCount(); i++) + valDescr[i] = group.getStat(i).getStatDescription(); + monitor.addData(peer.getIdentity().getHash().toBase64(), group.getDescription(), group.getDescription(), valDescr, peer.getPublished(), values); } - - /** - * How long does it take to process an outbound message? Stored in - * the peer summary as "processingTime", containing 4 doubles (avg ms for 1 minute, - * num messages for 1 minute, avg ms for 1 hour, num messages for 1 hour) + + /** + * Pull up a list of all values associated with the group (in the order that the + * group specifies). * - * @param peer who are we checking the frequency of dropping jobs for + * @return values or null on error */ - private void harvestProcessingTime(NetMonitor monitor, RouterInfo peer) { - double minuteMs = getDouble(peer, "stat_transport.sendProcessingTime.60s", 0); - double minuteFreq = getDouble(peer, "stat_transport.sendProcessingTime.60s", 7); - double hourMs = getDouble(peer, "stat_transport.sendProcessingTime.60m", 0); - double hourFreq = getDouble(peer, "stat_transport.sendProcessingTime.60m", 7); - if ( (minuteMs == -1) || (hourMs == -1) || (minuteFreq == -1) || (hourFreq == -1) ) - return; - - double times[] = new double[4]; - times[0] = minuteMs; - times[1] = minuteFreq; - times[2] = hourMs; - times[3] = hourFreq; - - String valDescr[] = new String[4]; - valDescr[0] = "process time avg ms (minute)"; - valDescr[1] = "process events (minute)"; - valDescr[2] = "process time avg ms (hour)"; - valDescr[3] = "process events (hour)"; - String description = "how long does it take to process a message"; - monitor.addData(peer.getIdentity().getHash().toBase64(), "processingTime", description, valDescr, peer.getPublished(), times); + private double[] harvestGroupValues(RouterInfo peer, StatGroup group) { + List values = new ArrayList(8); + for (int i = 0; i < group.getStatCount(); i++) { + StatGroup.StatDescription stat = group.getStat(i); + double val = getDouble(peer, stat.getOptionName(), stat.getOptionField()); + if (val == -1) + return null; + else + values.add(new Double(val)); + } + double rv[] = new double[values.size()]; + for (int i = 0; i < values.size(); i++) + rv[i] = ((Double)values.get(i)).doubleValue(); + return rv; } - + /** * Pull a value from the peer's option as a double, assuming the standard semicolon * delimited formatting diff --git a/apps/netmonitor/java/src/net/i2p/netmonitor/StatGroup.java b/apps/netmonitor/java/src/net/i2p/netmonitor/StatGroup.java new file mode 100644 index 000000000..dcf9e5817 --- /dev/null +++ b/apps/netmonitor/java/src/net/i2p/netmonitor/StatGroup.java @@ -0,0 +1,53 @@ +package net.i2p.netmonitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Stupid little structure to configure the DataHarvester's gathering of statistics. + * + */ +public class StatGroup { + private String _groupDescription; + private List _stats; + + public StatGroup(String description) { + _groupDescription = description; + _stats = new ArrayList(); + } + + public String getDescription() { return _groupDescription; } + public int getStatCount() { return _stats.size(); } + public StatDescription getStat(int index) { return (StatDescription)_stats.get(index); } + public void addStat(String description, String optionName, int optionField) { + StatDescription descr = new StatDescription(description, optionName, optionField); + _stats.add(descr); + } + + public class StatDescription { + private String _statDescription; + private String _optionName; + private int _optionField; + + public StatDescription(String descr, String optionName, int optionField) { + _statDescription = descr; + _optionName = optionName; + _optionField = optionField; + } + + /** brief description of this data point */ + public String getStatDescription() { return _statDescription; } + /** + * if this is harvested from the RouterInfo's options, this specifies + * which key in that map to pull from (or null if it isn't harvested + * from there) + */ + public String getOptionName() { return _optionName; } + /** + * if this is harvested from the RouterInfo's options, this specifies + * which field in value of that map to pull from (or -1 if it isn't harvested + * from there) + */ + public int getOptionField() { return _optionField; } + } +} \ No newline at end of file diff --git a/apps/netmonitor/java/src/net/i2p/netmonitor/StatGroupLoader.java b/apps/netmonitor/java/src/net/i2p/netmonitor/StatGroupLoader.java new file mode 100644 index 000000000..4a4dfdb37 --- /dev/null +++ b/apps/netmonitor/java/src/net/i2p/netmonitor/StatGroupLoader.java @@ -0,0 +1,92 @@ +package net.i2p.netmonitor; + + +import net.i2p.util.Log; +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; +import java.util.List; +import java.util.ArrayList; +import java.util.Properties; + +/** + * Load up the StatGroups from the location specified to configure the data harvester. + * The stat groups are formatted in a simple properties file style, e.g.:
+ * # dropped jobs + * statGroup.0.name=droppedJobs + * statGroup.0.detail.0.name=num dropped jobs (minute) + * statGroup.0.detail.0.option=stat_jobQueue.droppedJobs.60m + * statGroup.0.detail.0.field=3 + * statGroup.0.detail.1.name=num dropped jobs (hour) + * statGroup.0.detail.1.option=stat_jobQueue.droppedJobs.60h + * statGroup.0.detail.1.field=3 + * # + * statGroup.1.name=encryptTime + * statGroup.1.detail.0.name=encryption time avg ms (minute) + * statGroup.1.detail.0.option=stat_crypto.elGamal.encrypt.60s + * statGroup.1.detail.0.field=0 + * statGroup.1.detail.1.name=num encryptions (minute) + * statGroup.1.detail.1.option=stat_crypto.elGamal.encrypt.60s + * statGroup.1.detail.1.field=7 + * statGroup.1.detail.2.name=encryption time avg ms (hour) + * statGroup.1.detail.2.option=stat_crypto.elGamal.encrypt.60s + * statGroup.1.detail.2.field=0 + * statGroup.1.detail.3.name=num encryptions (hour) + * statGroup.1.detail.3.option=stat_crypto.elGamal.encrypt.60s + * statGroup.1.detail.3.field=7 + *+ */ +class StatGroupLoader { + private static final Log _log = new Log(StatGroupLoader.class); + /** + * Load a list of stat groups from the file specified + * + * @return list of StatGroup objects + */ + public static List loadStatGroups(String filename) { + _log.debug("Loading stat groups from " + filename); + FileInputStream fis = null; + File f = new File(filename); + try { + fis = new FileInputStream(f); + Properties p = new Properties(); + p.load(fis); + _log.debug("Config loaded from " + filename); + return loadStatGroups(p); + } catch (IOException ioe) { + _log.error("Error loading the stat groups from " + f.getAbsolutePath(), ioe); + return new ArrayList(); + } + } + + private static List loadStatGroups(Properties props) { + List rv = new ArrayList(8); + int groupNum = 0; + while (true) { + String description = props.getProperty("statGroup." + groupNum + ".name"); + if (description == null) break; + int detailNum = 0; + StatGroup group = new StatGroup(description); + while (true) { + String detailPrefix = "statGroup." + groupNum + ".detail." + detailNum + '.'; + String name = props.getProperty(detailPrefix + "name"); + if (name == null) break; + String option = props.getProperty(detailPrefix + "option"); + if (option == null) break; + String field = props.getProperty(detailPrefix + "field"); + if (field == null) break; + try { + int fieldNum = Integer.parseInt(field); + group.addStat(name, option, fieldNum); + } catch (NumberFormatException nfe) { + _log.warn("Unable to parse the field number from [" + field + "]", nfe); + break; + } + detailNum++; + } + rv.add(group); + groupNum++; + } + return rv; + } +} \ No newline at end of file