001: package com.jamonapi;
002:
003: /**
004:
005: * Static MonitorFactory that is good to use in most cases. In the situation
006: *when you want a different factory then instanciated FactoryEnabled directly.
007: *Note this is mostly a wrapper for FactoryEnabled and FactoryDisabled. You can also
008: *get the underlying factory with a call to getFactory()
009: *
010: * Created on December 10, 2005, 11:41 AM
011: *
012: * Partial Change history for JAMon 2.0
013: *
014: * 12/29/05 - coded the ability to enable/disable monitors - 2 hrs
015: * 12/30/05 - improved performance of time monitors 20% by not recalculating last active time.
016: Time went from 2443 ms. to 1983 ms. - 1 hr
017: * 1/2/05 - fixed bug where i was using the execution time and not the end time to set the last
018: * access stats.
019: * 1/6/06 - save most recent value/last value. Ed came up with a good reason for this.
020: * 1/9/06 - coded reset (mon, range, freqdist)
021: * 1/17/06 - coded ability for ranges to either be < (time) or <= (most others). note this makes time
022: * inconsistent with previous release where it was < for the first entry (0), and <= for the others.
023: * 1/17/06 - worked on range copy function. wasn't properly initializes FrequencyDist objects
024: * 1/17/06 - so far this release about 800k starts/stops a second. previous release 500k. About 60% faster.
025: * 1/19/06 - worked on null range object having the same number of frequencydists as its real monitors range
026: * 1/20/06 - added range getheader and getdata methods for display in drop down.
027: * 1/21/06 - added display of enabled status to returned data.
028: * 1/21/06 - added getBasicHeader, and getBasicData
029: * 1/21/06 - added getComposite by range name/units
030: * 1/21/06 - changed order of last value (after total now)
031: * 1/21/06 - had nullmonitor return same units as its real monitor
032: * 1/21/06 - hid some implementation details in an implementation MonitorImp interface. This helped
033: * get rid of some ugly casting without exposing end users to the implementation interface
034: * 1/21/06 - added MonitorComposite.hasData() method
035: * 1/22/06 - added getBasicData(), getDisplayData(), and getData() methods to monitor composite
036: * 1/22/06 - created FrequencyDistImp to hide implementation of frequencies from end users.
037: * 1/22/06 - created RangeImp to hide the implementation of ranges from end users
038: * 1/27/06 - Added stats to range that are more like monitor stats (i.e. getStdDev, getMin, getMax) and
039: * so was able to reduce code by finding a common abstraction between Monitors and FrequencyDists.
040: * The abstraction is called BaseStats
041: * 1/27/06 - Coded toString() method for Monitors (it has changed is appearance)
042: * 1/31/06 - Added FactoryDisabled, and FactoryEnabled and changed MonitorFactory to use them.
043: * 2/4/06 - Improved overall and synchronized performane. Performed synchronization performance tests. see results on delme directory
044: * html and also grouped bookmakr jamon sync test. I got 5 simultaneous loops going
045: * of 1 milliona start stops (JAMonAdminOld.jsp). Results follow
046: * 1) post changes of 2/7/06 new jamon with synchronization code - max was 20 seconds! fastest was 1.9 seconds. Synchronized
047: * speed was 5 times faster than when I started on 2/4, and 3 times faster than jamon 1.0!
048: * 2) post changes of 2/4/06 new jamon with synchronization code - max was 47 seconds. fastest 2.1 seconds
049: * 3) pre-changes of 2/4/06 new jamon with synchronization code - max was 75 seconds. fastest 2.6 seconds
050: * 4) old jamon with synchronziation code - max was 60 seconds htough one execution was slower than new 2.9 seconds
051: * 5) new jamon nonsynchronized hashamp - max was 100 seconds, fastest 5 seconds (not sure why)
052: * 6) new code no synchronziation (ideal time) - max was 10 seconds, fastest 2.1 seconds. so performance isn't much better
053: * one no blocking happens.
054: * 2/7/06 - Put better synchronization in code that does not call start/stop (just add). I no longer track active in this
055: * case. Performance test 1,000,000 times: with add no range 670 ms., start/stop timer 1600 ms. 20% improvement over
056: * 12/30 performance. JAMon 1.0 was 300,000 calls to start/stop a second. JAMon 2.0 is 625,000 calls a second!
057: * 2/11/06 - Created the JAMonAdmin.jsp over the last several days with lots of features like arraysql, variable formatting,
058: * and variable reports. Included LocaleContext for internationalization.
059: * 2/16/06 - Commented code and worked on LocaleContext
060: * - have primary active and global active work at (the unit level), range level, not hte factory level.
061: * 2/20/06 - Completed updates to JAMon user's guide
062: * 2/25/06 - Added a few methods back due to backwards compatibility issues, and also added Eric's improvements to jamon admin page
063: * 2/26/06 - Added all methods into MonitorFactory class that were part of JAMon 1.0. Backwards compatibility of JAMon 2.0 for this
064: * most used class should be ok. The only difference that I can think of is that the getReport methods returned an exception before
065: * and now don't.
066: * - Changed some text in JAMonAdmin.jsp
067: * 3/1/06 - Prepared for final release.
068: *- contact hsqldb, and don calderwood, jack shirazi
069: - pause/resume - see email
070: - persistance
071: - add examples to help per email - utilities
072: * - help file wording and erics changes
073: * - left off in this class for review of code. MonitorFactory
074: *
075: */
076:
077: import java.util.*;
078:
079: public class MonitorFactory {
080:
081: public static final String EXCEPTIONS_LABEL = "com.jamonapi.Exceptions";
082: private static MonitorFactoryInterface factory; // current factory
083: private static MonitorFactoryInterface enabledFactory; // factory for enabled monitors
084: private static MonitorFactoryInterface disabledFactory; // factory for disabled moniors
085: private static MonitorFactoryInterface debugFactory;
086:
087: static {
088: // enable the factory by default.
089: init();
090: }
091:
092: /** Get the current Factory (could be the enabled or disabled factory depending on what is enabled)
093: **/
094:
095: public static MonitorFactoryInterface getFactory() {
096: return factory;
097: }
098:
099: /** Returns the factory for creating debug monitors. The debug factory can be disabled independently from the
100: * regular factory. Debug monitors are no different than monitors returned by the regular monitor factory.
101: * However the debug factory can be used to monitor items in a test environment and disable them in production.
102: *
103: * Sample Call: MonitorFactory.getDebugFactory().start();
104: *
105: */
106:
107: public static MonitorFactoryInterface getDebugFactory() {
108: // both the regular factory (isEnabled()) and the debug factory must be enabled
109: // in order to return the non null factory.
110:
111: if (isEnabled())
112: return debugFactory;
113: else
114: return disabledFactory;
115: }
116:
117: /** <p>Aggregate the passed in value with the monitor associated with the label, and the units. The aggregation tracks
118: * hits, avg, total, min, max and more. Note the monitor returned is threadsafe. However, it is best to get a monitor
119: * vi this method and not reuse the handle as TimeMonitors are not thread safe (see the getTimeMonitor method.</p>
120: *
121: * <b>Sample call:</b><br>
122: * <blockquote><code><pre>
123: * Monitor mon=MonitorFactory.add("bytes.sent","MB", 1024);
124: *</pre></code></blockquote><br><br>
125: *
126: */
127:
128: public static Monitor add(String label, String units, double value) {
129: return factory.add(label, units, value);
130: }
131:
132: /** Used when you want to create your own key for the monitor. This works similarly to a group by clause where the key is
133: * any columns used after the group by clause.
134: */
135: public static Monitor add(MonKey key, double value) {
136: return factory.add(key, value);
137: }
138:
139: /** <p>Return a timing monitor with units in milliseconds. stop() should be called on the returned monitor to indicate the time
140: * that the process took. Note time monitors keep the starttime as an instance variable and so every time you want to use a TimeMonitor
141: * you should get a new instance. </p>
142: *
143:
144: * <b>Sample call:</b><br>
145: * <blockquote><code><pre>
146: * Monitor mon=MonitorFactory.start("pageHits");<br>
147: ...code being timed...<br>
148: mon.stop();
149: *
150: *</pre></code></blockquote><br><br>
151: *
152:
153: */
154:
155: public static Monitor start(String label) {
156: return factory.start(label);
157: }
158:
159: /** <p>Return a timing monitor with units in milliseconds, that is not aggregated into the jamon stats. stop() should be called on the returned monitor to indicate the time
160: * that the process took. Note time monitors keep the starttime as an instance variable and so every time you want to use a TimeMonitor
161: * you should get a new instance. </p>
162: *
163: * <b>Sample call:</b><br>
164: * <blockquote><code><pre>
165: * Monitor mon=MonitorFactory.start();<br>
166: ...code being timed...<br>
167: mon.stop();
168: *
169: *</pre></code></blockquote><br><br>
170: *
171: */
172:
173: public static Monitor start() {
174: return factory.start();
175: }
176:
177: public static Monitor getMonitor() {
178: return factory.getMonitor();
179: }
180:
181: /** <p>Return a timing monitor with units in milliseconds, that is not aggregated into the jamon stats. The concept of primary allows
182: you to correlate performance of all monitors with the most resource intensive things the app does which helps you determine scalability.
183: </p>
184: *
185: * <b>Sample call:</b><br>
186: * <blockquote><code><pre>
187: * Monitor mon=MonitorFactory.startPrimary("myPrimaryMonitor");<br>
188: ...code being timed...<br>
189: mon.stop();
190: *
191: *</pre></code></blockquote><br><br>
192: *
193: */
194:
195: public static Monitor startPrimary(String label) {
196: return factory.startPrimary(label);
197: }
198:
199: /** Start a monitor with the specified key and mark it as primary */
200: public static Monitor startPrimary(MonKey key) {
201: return factory.startPrimary(key);
202: }
203:
204: /** Start using the passed in key. Note activity stats are incremented */
205: public static Monitor start(MonKey key) {
206: return factory.start(key);
207: }
208:
209: /** <p>Return the monitor associated with the label, and units. All statistics associated with the monitor can then be accessed such
210: as hits, total, avg, min, and max. If the monitor does not exist it will be created.
211: </p>
212: *
213: * <b>Sample call:</b><br>
214: * <blockquote><code><pre>
215: * Monitor mon=MonitorFactory.getMonitor("myPrimaryMonitor");<br>
216: *
217: *</pre></code></blockquote><br><br>
218: *
219: */
220:
221: public static Monitor getMonitor(String label, String units) {
222: return factory.getMonitor(label, units);
223: }
224:
225: /** Get the monitor associated with the passed in key. It will be created if it doesn't exist */
226: public static Monitor getMonitor(MonKey key) {
227: return factory.getMonitor(key);
228: }
229:
230: /** <p>Return the time monitor associated with the label. All statistics associated with the monitor can then be accessed such
231: as hits, total, avg, min, and max. If the monitor does not exist it will be created.
232: </p>
233: *
234: * <b>Sample call:</b><br>
235: * <blockquote><code><pre>
236: * Monitor mon=MonitorFactory.getTimeMonitor("myPrimaryMonitor");<br>
237: *
238: *</pre></code></blockquote><br><br>
239: *
240: */
241: public static Monitor getTimeMonitor(String label) {
242: return factory.getTimeMonitor(label);
243: }
244:
245: /** Get the time monitor associated with the passed in key. It will be created if it doesn't exist. The units
246: * are in ms.*/
247: public static Monitor getTimeMonitor(MonKey key) {
248: return factory.getTimeMonitor(key);
249: }
250:
251: /** <p>Determine if the monitor associated with the label, and the units currently exists.
252: </p>
253: *
254: * <b>Sample call:</b><br>
255: * <blockquote><code><pre>
256: * Monitor mon=MonitorFactory.getTimeMonitor("myPrimaryMonitor");<br>
257: *
258: *</pre></code></blockquote><br><br>
259: *
260: */
261:
262: public static boolean exists(String label, String units) {
263: return factory.exists(label, units);
264: }
265:
266: /** Return true if the monitor associated with the passed in key exists */
267: public static boolean exists(MonKey key) {
268: return factory.exists(key);
269: }
270:
271: /** <p>Return the composite monitor (a collection of monitors) associated with the passed in units. Note in JAMon 1.0 this
272: * method would take a lable and would return all monitors that matched that criterion. This ability is now better performed
273: * using ArraySQL from the FormattedDataSet API. See JAMonAdmin.jsp for an example.
274: </p>
275:
276: *
277: * <b>Sample call:</b><br>
278: * <blockquote><code><pre>
279: * Monitor mon=MonitorFactory.getComposite("ms.");<br>
280: * mon=MonitorFactory.getComposite("allMonitors");<br>
281: *
282: *</pre></code></blockquote><br><br>
283: *
284: */
285:
286: public static MonitorComposite getComposite(String units) {
287: return factory.getComposite(units);
288: }
289:
290: /**
291: * This returns the number of monitors in this factory.
292: */
293:
294: public static int getNumRows() {
295: return factory.getNumRows();
296: }
297:
298: /**
299: * Return the header for displaying what ranges are available.
300: *
301: */
302:
303: public static String[] getRangeHeader() {
304: return factory.getRangeHeader();
305: }
306:
307: /**
308: * Return the ranges in this factory.
309: *
310: */
311:
312: public static Object[][] getRangeNames() {
313: return factory.getRangeNames();
314: }
315:
316: /** Return the composite monitor of all monitors for this factory */
317:
318: public static MonitorComposite getRootMonitor() {
319: return factory.getRootMonitor();
320: }
321:
322: /** Return the version of JAMon */
323:
324: public static String getVersion() {
325: return factory.getVersion();
326: }
327:
328: /** Remove/delete the specified monitor */
329:
330: public static void remove(String label, String units) {
331: factory.remove(label, units);
332: }
333:
334: /** Remove the monitor associated with the passed in key */
335: public static void remove(MonKey key) {
336: factory.remove(key);
337: }
338:
339: /**
340: * Use the specified map to hold the monitors. This map should be
341: * threadsafe. This allows for the use of a faster map than
342: *
343: * the default synchronzied HashMap()
344: *
345: */
346:
347: public static void setMap(Map map) {
348: factory.setMap(map);
349: }
350:
351: /**
352: * Associate a range with a key/unit. Any monitor with the given unit will
353: * have this range. Any monitor with
354: *
355: * no range associated with its unit will have no range.
356: *
357: */
358:
359: public static void setRangeDefault(String key,
360: RangeHolder rangeHolder) {
361: factory.setRangeDefault(key, rangeHolder);
362: }
363:
364: /**
365: * Enable/Disable MonitorFactory. When enabled (true) the factory returns
366: * monitors that store aggregate stats. When disabled (false)
367: *
368: * null/noop monitors are returned. enable()/disable() can also be used to
369: * perform the same function
370: *
371: *
372: *
373: */
374:
375: public static void setEnabled(boolean enable) {
376: if (enable)
377: factory = enabledFactory;
378: else
379: factory = disabledFactory;
380:
381: }
382:
383: /**
384: *
385: * Enable or disable the debug factory. The debug factory can be
386: * enabled/disabled at runtime. Calling this method with a false
387: *
388: * also disables calls to MonitorFactory.getDebugFactory(int
389: * debugPriorityLevel)
390: *
391: *
392: *
393: * Sample Call:
394: *
395: * MonitorFactory.setDebugEnabled(false);
396: *
397: * MonitorFactory.getDebugFactory().start(); // no stats are gathered.
398: *
399: */
400:
401: public static void setDebugEnabled(boolean enable) {
402: if (enable)
403: debugFactory = enabledFactory;
404: else
405: debugFactory = disabledFactory;
406:
407: }
408:
409: /**
410: * Enable MonitorFactory. When enabled the factory returns monitors that
411: * store aggregate stats. This method has the same
412: *
413: * effect as calling MonitorFactor.setEnabled(true).
414: *
415: */
416:
417: public static void enable() {
418: setEnabled(true);
419: }
420:
421: /**
422: * Disable MonitorFactory. When disabled the factory returns null/noop
423: * monitors. This method has the same
424: *
425: * effect as calling MonitorFactor.setEnabled(true).
426: *
427: */
428:
429: public static void disable() {
430: setEnabled(false);
431: }
432:
433: /**
434: * Is the MonitorFactory currently enabled?
435: *
436: */
437:
438: public static boolean isEnabled() {
439: return (factory == enabledFactory) ? true : false;
440: }
441:
442: /**
443: * Is the Debug Monitor Factory currently enabled?
444: *
445: */
446:
447: public static boolean isDebugEnabled() {
448: return (debugFactory == enabledFactory) ? true : false;
449: }
450:
451: public static boolean isGlobalActiveEnabled() {
452: return factory.isGlobalActiveEnabled();
453: }
454:
455: public static void enableGlobalActive(boolean enable) {
456: factory.enableGlobalActive(enable);
457: }
458:
459: /**
460: * Reset/remove all monitors. If the factory is disabled this method has no
461: * action.
462: */
463:
464: public static void reset() {
465:
466: if (isEnabled())
467: init();
468: }
469:
470: private static void init() {
471: // enable the factory by default.
472: factory = debugFactory = enabledFactory = new FactoryEnabled();
473: disabledFactory = new FactoryDisabled(enabledFactory);
474: }
475:
476: /**
477: * This returns the header for basic data with no range info in the header.
478: * This method is deprecated. use the methods associated
479: *
480: * with the CompositeMonitor. The various getXXXHeader() methods of
481: * CompositeMonitors can return this information and more.
482: *
483: *
484: *
485: *
486: */
487:
488: public static String[] getHeader() {
489: return factory.getRootMonitor().getBasicHeader();
490: }
491:
492: /**
493: * This returns the data for basic data with no range info.
494: *
495: * The various getXXXData() methods of CompositeMonitors can return this
496: * information and more.
497: *
498: *
499: *
500: *
501: *
502: */
503:
504: public static Object[][] getData() {
505: return factory.getRootMonitor().getBasicData();
506: }
507:
508: /**
509: * This returns an HTML report for basic data with no range info in the
510: * header.
511: *
512: */
513:
514: public static String getReport() {
515: return factory.getRootMonitor().getReport();
516: }
517:
518: /**
519: * This returns an HTML report for basic data with no range info in the
520: * header for the past in units.
521: *
522: * This method will be removed in the next release.
523: */
524:
525: public static String getReport(String units) {
526: return getComposite(units).getReport();
527: }
528:
529: /** Iterator that contains Monitor's that are in this factory */
530: public Iterator iterator() {
531: return factory.iterator();
532: }
533:
534: /** return a test range holder */
535:
536: private static RangeHolder getTestHolder() {
537:
538: RangeHolder rh = new RangeHolder();
539: rh.add("10_display", 10);
540: rh.add("20_display", 20);
541: rh.add("30_display", 30);
542: rh.add("40_display", 40);
543: rh.add("50_display", 50);
544: rh.add("60_display", 60);
545: rh.add("70_display", 70);
546: rh.add("80_display", 80);
547: rh.add("90_display", 90);
548: rh.add("100_display", 100);
549: rh.add("110_display", 110);
550: rh.add("120_display", 120);
551: rh.add("130_display", 130);
552: rh.add("140_display", 140);
553: rh.add("150_display", 150);
554: // note last range is always called lastRange and is added automatically
555: return rh;
556:
557: }
558:
559: public static void main(String[] args) {
560:
561: Monitor timer = MonitorFactory.start("totaltime");
562: for (int i = 1; i <= 10; i++) {
563: System.out.println(MonitorFactory.add("NIC.bytes.sent",
564: "bytes", i * 1000));
565: System.out.println(MonitorFactory.add("negativetest",
566: "neg", -1000.0 * i));
567: }
568:
569: System.out.println("");
570: Monitor m = null;
571: m = MonitorFactory.start("purchasesTimeTestNoRange");
572:
573: for (int i = 1; i <= 1000000; i++)
574: MonitorFactory.add("purchasesNoRange", "dollars", 1000.0);
575:
576: System.out.println("purchasesTimeTestNoRange="
577: + m.stop().getTotal());
578:
579: m = MonitorFactory.start("testTimerTimeTest");
580: for (int i = 1; i <= 1000000; i++)
581: MonitorFactory.start("testTimer").stop();
582:
583: System.out.println("testTimerTimeTest=" + m.stop().getTotal());
584: for (int i = -5; i <= 20; i++) {
585: MonitorFactory.add("purchases", "dollars", i * 50);
586:
587: }
588:
589: System.out.println("");
590:
591: System.out.println(MonitorFactory.add("NIC.bytes.received",
592: "bytes", 250.0));
593: System.out.println(MonitorFactory.add("NIC.bytes.received",
594: "bytes", 250.0));
595:
596: timer.stop();
597:
598: for (int i = -5; i < 25; i++)
599: MonitorFactory.add("timetest", "ms.", i * 5);
600:
601: System.out.println(MonitorFactory.getMonitor("purchases",
602: "dollars").getHits());
603: System.out.println(MonitorFactory.getTimeMonitor("testTimer")
604: .getHits());
605: // case sensitive so won't print
606: System.out.println(MonitorFactory.getTimeMonitor("testtimer")
607: .getHits());
608: System.out.println("Total time=" + timer.getTotal());
609:
610: MonitorFactory.reset();
611: MonitorFactory.setRangeDefault("dollars", getTestHolder());
612: m = MonitorFactory.start("purchasesTimeTestRange");
613: for (int i = 1; i <= 1000000; i++)
614: MonitorFactory.add("purchasesRange", "dollars", 1000.0);
615:
616: System.out.println("purchasesTimeTestRange="
617: + m.stop().getTotal());
618: Object[][] data = null;
619: MonitorFactory.setRangeDefault("bytes", getTestHolder());
620: MonitorFactory.setRangeDefault("cents", getTestHolder());
621: MonitorFactory.setRangeDefault("minutes", getTestHolder());
622: MonitorFactory.setRangeDefault("MB", getTestHolder());
623: MonitorFactory.setRangeDefault("KB", getTestHolder());
624: MonitorFactory.setRangeDefault("points", getTestHolder());
625:
626: String[] header = MonitorFactory.getComposite("ms.")
627: .getHeader();
628:
629: data = MonitorFactory.getComposite("ms.").getData();
630: header = MonitorFactory.getComposite("ms.").getBasicHeader();
631:
632: data = MonitorFactory.getComposite("ms.").getBasicData();
633: header = MonitorFactory.getComposite("ms.").getDisplayHeader();
634:
635: data = MonitorFactory.getComposite("ms.").getDisplayData();
636: MonitorFactory.getComposite("ms.").disable();
637:
638: header = MonitorFactory.getComposite("ms.").getHeader();
639: data = MonitorFactory.getComposite("ms.").getData();
640:
641: System.out.println("header length=" + header.length
642: + ", data length=" + data.length);
643: System.out.println("JAMon Version="
644: + MonitorFactory.getVersion());
645:
646: }
647:
648: }
|