001: package com.jamonapi;
002:
003: import java.util.*;
004:
005: /** TimeStatsDistMonitor keeps track of statistics for JAMon time ranges. See www.jamonapi.com for more info.
006: * One powerful feature of this class is that it correlates performance to the number of active monitors with this label,
007: * the number of primary active monitors and the number of globally active monitors.
008: **/
009:
010: public class TimeStatsDistMonitor extends AccumulateMonitor {
011: static private Counter allActive = new Counter(); // tracks the number of ALL monitors that are active
012: static private Counter primaryActive = new Counter(); // tracks the number of primary monitors that are active
013:
014: private int active = 0;
015:
016: public TimeStatsDistMonitor() {
017: super ();
018: populateDist();
019: }
020:
021: public TimeStatsDistMonitor(AccumulateMonitorInterface childMonitor) {
022: super (childMonitor);
023: populateDist();
024: }
025:
026: /** increase(...) is called by TimingMonitor right before stop is called. The distribution statistics are tracked in this method **/
027: synchronized protected void increaseThis(long value) {
028: distribution(value);
029: }
030:
031: /** Increment counters for the number of active monitors of this instance, globally active monitors and primary active monitors. These
032: * numbers can be associated to performance to give a feel for scalability and other performance relationships in the application.
033: * see www.jamonapi.com for more info on the active concept and JAMon time ranges
034: **/
035: synchronized protected void startThis() {
036: // Counts how many total monitors are currently in progress as a rough check of how busy the system is
037: // these need to be synchronized because they can be called from other objects start methods so they wouldn't
038: // by syncrhonized by the startThis() method
039: allActive.increment();
040: if (isPrimary())
041: primaryActive.increment();
042:
043: active++;
044:
045: }
046:
047: /** Decrement counters for the number of active monitors of this instance, globally active monitors and primary active monitors. These
048: * numbers can be associated to performance to give a feel for scalability and other performance relationships in the application.
049: * see www.jamonapi.com for more info on the active concept and JAMon time ranges
050: **/
051: synchronized protected void stopThis() {
052: if (active > 0)
053: active--;
054:
055: if (isPrimary()) // see should i use ints or longs. what about checking for>0 before decrement??????
056: primaryActive.decrement();
057:
058: allActive.decrement();
059: }
060:
061: // Index constants for array referencing
062: private static final int i10 = 0;
063: private static final int i20 = 1;
064: private static final int i40 = 2;
065: private static final int i80 = 3;
066: private static final int i160 = 4;
067: private static final int i320 = 5;
068: private static final int i640 = 6;
069: private static final int i1280 = 7;
070: private static final int i2560 = 8;
071: private static final int i5120 = 9;
072: private static final int i10240 = 10;
073: private static final int i20480 = 11;
074: private static final int iLAST_GROUP = 12;
075: private static final int DIST_SIZE = 13;
076:
077: private FrequencyDist[] dist;
078:
079: private void populateDist() {
080: dist = new FrequencyDist[DIST_SIZE];
081:
082: for (int i = 0; i < DIST_SIZE; i++)
083: dist[i] = new FrequencyDist();
084: }
085:
086: // Note because distribution is always accessed via a synchronized method it doesn't have to be synchronized.
087: private void distribution(long value) {
088: // calculates hits, avg, and global active for the distribution to which the passed arg 'value' belongs
089: if (value >= 0)
090: getDist(value).increment(value);
091: else
092: throw new RuntimeException(
093: "Value passed to TimeStatsMonitor.distribution(value) must be positive. It was="
094: + value);
095:
096: }
097:
098: // This method is passed the number of milliseconds representing this monitors accrued time. This method makes sure
099: // the statistics we are tracking are put in the proper time range
100: private FrequencyDist getDist(long value) {
101: if (value <= 10)
102: return dist[i10];
103: else if (value <= 20)
104: return dist[i20];
105: else if (value <= 40)
106: return dist[i40];
107: else if (value <= 80)
108: return dist[i80];
109: else if (value <= 160)
110: return dist[i160];
111: else if (value <= 320)
112: return dist[i320];
113: else if (value <= 640)
114: return dist[i640];
115: else if (value <= 1280)
116: return dist[i1280];
117: else if (value <= 2560)
118: return dist[i2560];
119: else if (value <= 5120)
120: return dist[i5120];
121: else if (value <= 10240)
122: return dist[i10240];
123: else if (value <= 20480)
124: return dist[i20480];
125: else
126: return dist[iLAST_GROUP];
127:
128: }
129:
130: synchronized protected void resetThis() {
131: populateDist();
132: }
133:
134: synchronized protected String toStringThis() {
135: return "";
136: }
137:
138: synchronized protected void getDataThis(ArrayList rowData) {
139: rowData.add(isPrimary() ? "Yes" : " ");
140:
141: for (int i = 0; i < DIST_SIZE; i++)
142: rowData.add(dist[i].toString());
143:
144: }
145:
146: protected void getHeaderThis(ArrayList header) {
147: final String SUFFIX = "ms.";// Hits/Avg (Avg Active/Primary Active/Global Active)";
148:
149: header.add("Primary");
150: header.add("0-10" + SUFFIX);
151: header.add("11-20" + SUFFIX);
152: header.add("21-40" + SUFFIX);
153: header.add("41-80" + SUFFIX);
154: header.add("81-160" + SUFFIX);
155: header.add("161-320" + SUFFIX);
156: header.add("321-640" + SUFFIX);
157: header.add("641-1280" + SUFFIX);
158: header.add("1281-2560" + SUFFIX);
159: header.add("2561-5120" + SUFFIX);
160: header.add("5121-10240" + SUFFIX);
161: header.add("10241-20480" + SUFFIX);
162: header.add(">20480" + SUFFIX);
163:
164: }
165:
166: // Note becauset FrequencyDis is always accessed via a synchronized method it doesn't have to be synchronized.
167: // This class tracks statistics for each JAMon time range. Statistics include hits, total execution time, and the
168: // activity statistics.
169: final class FrequencyDist {
170: private int hits;
171: private long totalTime; // used to calculate avg time for this distribution
172: private long allActiveTotal; // used to calculate the average active total monitors for this distribution
173: private long primaryActiveTotal;
174: private int activeTotal;
175:
176: private void increment(long value) {
177: hits++;
178: totalTime += value;
179:
180: allActiveTotal += allActive.count; // total of all monitors active
181: primaryActiveTotal += primaryActive.count; // total of all monitors marked primary as active
182: activeTotal += active; // total of this monitor active
183: }
184:
185: private float avgTotalActive() {
186: // avg active of all monitors active when this method was called
187: if (hits == 0)
188: return 0f;
189: else
190: return (float) allActiveTotal / hits;
191: }
192:
193: private float avgActive() {
194: // avg active of this monitor when this method was called
195: if (hits == 0)
196: return 0f;
197: else
198: return (float) activeTotal / hits;
199: }
200:
201: private float avgPrimaryActive() {
202: // avg active of all monitors marked as primary when this method was called
203: if (hits == 0)
204: return 0f;
205: else
206: return (float) primaryActiveTotal / hits;
207: }
208:
209: private long avgTime() {
210: if (hits == 0)
211: return 0;
212: else
213: return totalTime / hits;
214: }
215:
216: public String toString() {
217: final String SPACE = " ";
218: if (hits == 0)
219: return SPACE;
220: else
221: return convertToString(hits) + "/"
222: + convertToString(avgTime()) + SPACE + "("
223: + convertToString(avgActive()) + "/"
224: + convertToString(avgPrimaryActive()) + "/"
225: + convertToString(avgTotalActive()) + ")";
226: }
227: }
228:
229: /** Test method for this class **/
230: public static void main(String[] args) throws Exception {
231: TimeStatsDistMonitor mon = new TimeStatsDistMonitor();
232:
233: System.out
234: .println("There should be 1 entry for each distribution range");
235: System.out
236: .println("All entries should have 5/low value of range/3");
237:
238: mon.start();
239: mon.start();
240: mon.start();
241:
242: for (int i = 1; i <= 5; i++) {
243: mon.increase(0);
244: mon.increase(20);
245: mon.increase(40);
246: mon.increase(80);
247: mon.increase(160);
248: mon.increase(320);
249: mon.increase(640);
250: mon.increase(1280);
251: mon.increase(2560);
252: mon.increase(5120);
253: mon.increase(10240);
254: mon.increase(20480);
255: mon.increase(50000);
256: }
257:
258: mon.stop();
259: mon.stop();
260: mon.stop();
261:
262: ArrayList rowData = new ArrayList();
263: mon.getData(rowData);
264:
265: for (int i = 0; i < rowData.size(); i++)
266: System.out.println("distribution index " + i + "="
267: + rowData.get(i));
268:
269: }
270: }
|