001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.services.perflog;
019:
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.Map;
024:
025: import org.sape.carbon.core.component.ComponentConfiguration;
026: import org.sape.carbon.core.component.lifecycle.Configurable;
027:
028: /**
029: * <p>This extension on the basic performance logger provides the additional
030: * capability to track timings over a period of time and get report on their
031: * minimums, maximum, averages and standard deviations. This implementation
032: * uses a rolling, array-based bounded buffer to store up to the last "n"
033: * timings.</p>
034: *
035: * Copyright 2003 Sapient
036: * @since carbon 1.2
037: * @author Greg Hinkle, January 2003
038: * @version $Revision: 1.6 $($Author: dvoet $ / $Date: 2003/05/05 21:21:30 $)
039: */
040: public class BoundedBufferPerformanceLogger extends
041: DefaultPerformanceLogger implements ReportingPerformanceLogger,
042: Configurable {
043:
044: /**
045: * A synchronized map of TimingInfo objects to the tracked object
046: */
047: protected Map timingMap = Collections
048: .synchronizedMap(new HashMap());
049:
050: /**
051: * Holds the number of timings to track for statistics generation.
052: */
053: protected int trackedTimings;
054:
055: /**
056: * This implementation adds additional steps to track a set of timings
057: * in order to calculate statistics on the executions.
058: *
059: * @param trackedObject the object that is being timed
060: * @param time the duration of the execution to track
061: */
062: protected void trackTiming(Object trackedObject, long time) {
063: super .trackTiming(trackedObject, time);
064: registerTiming(trackedObject, time);
065: }
066:
067: /**
068: * This implementation will add a new TimingInfo object to the main tracking
069: * map if one is not found corresponding to the tracked action. It will
070: * then register the supplied timing with that object.
071: *
072: * @param trackedObject the object being tracked
073: * @param time the time tracked of the call
074: */
075: protected void registerTiming(Object trackedObject, long time) {
076:
077: // Warning: Known non-synchronization
078: // Sacrificing data to maintain speed of logging
079: // (may loose the first few timings)
080: TimingInfo timingInfo = (TimingInfo) this .timingMap
081: .get(trackedObject);
082:
083: if (timingInfo == null) {
084: timingInfo = new TimingInfo(trackedObject,
085: this .trackedTimings);
086:
087: this .timingMap.put(trackedObject, timingInfo);
088: }
089: timingInfo.addTiming(time);
090: }
091:
092: /**
093: * Holds the characters used to pad in the pad method.
094: *
095: * @see #pad
096: */
097: private static final String PAD = " ";
098:
099: /**
100: * Pads the information fields to a fixed length for display.
101: *
102: * @param str the string to pad
103: * @param len the max length to pad to
104: * @return a new padded version of the string
105: */
106: private String pad(String str, int len) {
107: if (str.length() > len) {
108: return str.substring(0, len - 1) + " ";
109: } else {
110: return str + PAD.substring(0, len - str.length());
111: }
112: }
113:
114: /**
115: * Returns a string report of timings that are being tracked by this
116: * perfomance logger.
117: *
118: * @return A report of information for this logger
119: */
120: public String getReport() {
121: StringBuffer buf = new StringBuffer(1000);
122: buf.append("Extended performance logger report [");
123: buf.append(this .timingMap.size());
124: buf.append("] items.\n");
125:
126: buf
127: .append("Min Max Avg StdDev Tracked Object\n");
128: Iterator iter = this .timingMap.values().iterator();
129: while (iter.hasNext()) {
130: TimingInfo info = (TimingInfo) iter.next();
131:
132: buf.append(pad(String.valueOf(info.getMin()), 10));
133: buf.append(pad(String.valueOf(info.getMax()), 10));
134: buf.append(pad(String.valueOf(info.getAverage()), 10));
135: buf.append(pad(String.valueOf(info.getStandardDeviation()),
136: 10));
137: buf.append(String.valueOf(info.getTrackedObject()));
138: buf.append("\n");
139: }
140: return buf.toString();
141: }
142:
143: /**
144: * Configure the component. This is preceded and followed by the suspend
145: * and resume operations if they are available on the component.
146: *
147: * @param configuration BoundedBufferPerformanceLoggerConfiguration to
148: * configure this component
149: * @throws org.sape.carbon.core.component.lifecycle.LifecycleException
150: */
151: public void configure(ComponentConfiguration configuration) {
152:
153: this .trackedTimings = ((BoundedBufferPerformanceLoggerConfiguration) configuration)
154: .getTrackedTimingsCount();
155: }
156:
157: /**
158: * <p>This inner class supports the tracking of timings on a
159: * single tracked item. It stores the item to be tracked as well
160: * as a array-based bounded buffer. This buffer of configurable
161: * size stores the last n timings for use in determing rolling
162: * averages for the timings as well as standard deviation.</p>
163: *
164: * <p>The provided minimum and maximum values are calculated
165: * for all timings regardless of whether they are within the
166: * bounded-buffer at a given point in time.</p>
167: */
168: public static class TimingInfo {
169: /** Holds the object being tracked. */
170: private Object trackedObject;
171:
172: /** Holds the size of the timingArray. */
173: private int capacity;
174:
175: /** Indicates if the timingArray is full. */
176: private boolean full = false;
177:
178: /** Holds the timings for the method. */
179: private long[] timingArray;
180:
181: /**
182: * Holds the current index for the new timing. The index
183: * wraps around back to zero after reaching the capacity and
184: * replaces old values.
185: */
186: private int index;
187:
188: /** Holds the min time taken. */
189: private long min = Long.MAX_VALUE;
190:
191: /** Holds the max time taken. */
192: private long max;
193:
194: /**
195: * Creates a new timing info object for the specified tracked object
196: * of the specified size.
197: *
198: * @param trackedObject The object to be tracked.
199: * @param capacity the number of timings that will be tracked.
200: */
201: public TimingInfo(Object trackedObject, int capacity) {
202: this .trackedObject = trackedObject;
203: this .timingArray = new long[capacity];
204: this .capacity = capacity;
205: }
206:
207: /**
208: * Retrieves the object being tracked.
209: *
210: * @return the object being tracked
211: */
212: public Object getTrackedObject() {
213: return this .trackedObject;
214: }
215:
216: /**
217: * Adds a new timing into the statistics.
218: *
219: * @param time the length of time of the method call
220: */
221: public synchronized void addTiming(long time) {
222: this .timingArray[index++] = time;
223: if (this .index >= this .capacity) {
224: this .index = 0;
225: this .full = true;
226: }
227:
228: if (this .max < time) {
229: this .max = time;
230: }
231:
232: if (this .min > time) {
233: this .min = time;
234: }
235: }
236:
237: /**
238: * Returns the mininum time taken.
239: *
240: * @return the min time taken
241: */
242: public long getMin() {
243: return this .min;
244: }
245:
246: /**
247: * Returns the max time taken.
248: *
249: * @return max time taken
250: */
251: public long getMax() {
252: return this .max;
253: }
254:
255: /**
256: * Returns the amount of timings stored in the object.
257: *
258: * @return amount of timings stored in the object
259: */
260: public synchronized int size() {
261: if (full) {
262: return this .capacity;
263: } else {
264: return this .index;
265: }
266: }
267:
268: /**
269: * Calcluate standard deviation in timings using Knuth's algorithm.
270: *
271: * @return standard deviation
272: */
273: public synchronized double getStandardDeviation() {
274: final int n = size();
275: if (n < 2) {
276: return Double.NaN;
277: }
278:
279: double avg = this .timingArray[0];
280: double sum = 0;
281: for (int i = 1; i < n - 1; i++) {
282: double newavg = avg + (this .timingArray[i] - avg)
283: / (i + 1);
284: sum += (this .timingArray[i] - avg)
285: * (this .timingArray[i] - newavg);
286:
287: avg = newavg;
288: }
289: return Math.sqrt(sum / (n - 1));
290: }
291:
292: /**
293: * Calculates the average time of the calls.
294: *
295: * @return the average time of the calls.
296: */
297: public synchronized double getAverage() {
298: final int n = size();
299: double sum = 0;
300: for (int i = 0; i < n; i++) {
301: sum += this .timingArray[i];
302: }
303: return (sum / (double) n);
304: }
305: }
306: }
|