package de.is24.util.monitoring.helper; import de.is24.util.monitoring.CorePlugin; import de.is24.util.monitoring.InApplicationMonitor; import de.is24.util.monitoring.StateValueProvider; /** * <p>Convenience class that makes it easier to log values in a histogram-like fashion. * Values are automatically grouped into ranges/"bins" of similar values; the width of the * "bins" into which values are grouped can be specified by the constructor parameter "factor". * New values are divided by the given factor in order to determine the bin in which the * value should be counted, e.g. if you construct a HistogramLikeValue with a factor of "1000" * and add the following values</p><pre> * 50 * 80 * 100 * 150 * 1050 * 1100</pre> * <p>then these values are grouped into two different bins, one between 0-999 and one between 1000-1999. * In this example, you would have 4 values in the bin named "biggerThan0" and 2 values in * the bin named "biggerThan1000".</p> * <p>See unit test for details.</p> * * <p>Furthermore, if you specify a "maxLimit" as optional constructor argument, all values bigger * than this maximum threshold will be grouped in one single bin (biggerThan<maxLimit>) rather * than grouped into separate bins as specified by "factor".</p> * * <p>The "bins" are not sized automatically because this would require storing all added * values so that the bin values could be re-calculated.</p> * * <p>The HistogramLikeValue automatically creates counter and timer values in the background; * the names of these Reportables are constructed using the "baseName" specified as * constructor argument. In the end, the following InApplicationMonitor reportables are created * by this class:</p><pre> * <basename>.total Timer recording all values * <basename>.biggerThan<value> Counter for each bin that is created * <basename>.currentMax StateValue holding the current maximum value * </pre> * @author ptraeder * */ public class HistogramLikeValue { public static final String NAME_BIGGER_THAN = ".biggerThan"; public static final String NAME_FACTOR = ".factor"; public static final String NAME_CURRENT_MAX = ".currentMax"; private String baseName; private String timerName; private String factorName; private long factor; private long maxLimit = Long.MAX_VALUE; private long currentMaxValue = 0L; private String maxValueName; private String maxLimitName; /** * @param baseName the base name to use for InApplicationMonitor value name * @param factor the factor to divide new values by in order to group them into bins */ public HistogramLikeValue(String baseName, final long factor) { this.baseName = baseName; this.timerName = baseName + ".total"; this.maxValueName = baseName + NAME_CURRENT_MAX; this.maxLimitName = baseName + ".maxLimit"; this.factorName = baseName + NAME_FACTOR; this.factor = factor; register(InApplicationMonitor.getInstance().getCorePlugin()); } private void register(CorePlugin corePlugin) { // publish the currentMaxValue and the maxLimit as state values corePlugin.registerStateValue(new StateValueProvider() { @Override public String getName() { return maxValueName; } @Override public long getValue() { return currentMaxValue; } }); corePlugin.registerStateValue(new StateValueProvider() { @Override public String getName() { return maxLimitName; } @Override public long getValue() { return maxLimit; } }); corePlugin.registerStateValue(new StateValueProvider() { @Override public String getName() { return factorName; } @Override public long getValue() { return factor; } }); } /** * @param baseName the base name to use for InApplicationMonitor value name * @param factor the factor to divide new values by in order to group them into bins * @param maxLimit the upper limit up to which bins are created - all values bigger than maxLimit are grouped into one single bin */ public HistogramLikeValue(String baseName, long factor, long maxLimit) { this(baseName, factor); this.maxLimit = maxLimit; } public String getBaseName() { return baseName; } private String getBinName(long value) { StringBuilder binName = new StringBuilder(); binName.append(baseName); binName.append(NAME_BIGGER_THAN); binName.append(value * factor); return binName.toString(); } /** * adds a new value to the InApplicationMonitor, grouping it into the appropriate bin. * * @param newValue the value that should be added */ public void addValue(long newValue) { // keep a "total" timer for all values InApplicationMonitor.getInstance().addTimerMeasurement(timerName, newValue); // keep track of the current maximum value if (newValue > currentMaxValue) { currentMaxValue = newValue; } // select the bin to put this value in long binIndex; if (newValue > maxLimit) { binIndex = maxLimit / factor; } else { binIndex = newValue / factor; } // add the new value to the appropriate bin String binName = getBinName(binIndex); InApplicationMonitor.getInstance().incrementCounter(binName); } }