Skip to content

Commit 2f114bc

Browse files
committed
Initial load
1 parent 7ad19f4 commit 2f114bc

39 files changed

Lines changed: 3721 additions & 1 deletion

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# jquantify
2-
JQuantify
2+
JQuantify is a Java package providing lightweight statistics describing the frequency and duration of application-specific events.
3+
Developers use a simple API to insert count-points or start/stop boundaries at appropriate places within their system.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.berryworks.jquantify;
2+
3+
abstract class Clock {
4+
public static long now() {
5+
return System.currentTimeMillis();
6+
}
7+
}
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
package com.berryworks.jquantify;
2+
3+
/**
4+
* This class is used to count and observe the frequency of events.
5+
* <p/>
6+
* An <code>EventCounter</code> treats events as instantaneous,
7+
* happening at a specific point in time and with no observable duration.
8+
* For observing events that have both frequency and duration, consider
9+
* a <code>SessionCounter</code>.
10+
*/
11+
public class EventCounter extends Metric {
12+
private static final long serialVersionUID = 1L;
13+
protected EventCounterInterval mCurrentInterval;
14+
protected EventCounterInterval mPriorInterval;
15+
protected EventCounterInterval mPeakEvents;
16+
protected long mNow;
17+
18+
public EventCounter() {
19+
}
20+
21+
/**
22+
* Constructs an EventCounter with a specific interval duration.
23+
*
24+
* @param inLabel descriptive String used in reporting
25+
* @param inIntervalSeconds int duration of each interval in seconds
26+
*/
27+
public EventCounter(String inLabel, int inIntervalSeconds) {
28+
super(inLabel, inIntervalSeconds);
29+
createIntervals();
30+
}
31+
32+
/**
33+
* Constructs a new EventCounter with an interval duration of 1 second.
34+
*
35+
* @param inLabel descriptive String used in reporting
36+
*/
37+
public EventCounter(String inLabel) {
38+
this(inLabel, 1);
39+
}
40+
41+
/**
42+
* Get an instance of an EventCounter with a particular label from the
43+
* MetricRepository, creating one if necessary.
44+
*
45+
* @param inLabel Description of the Parameter
46+
* @return The eventCounter value
47+
*/
48+
public static synchronized EventCounter getEventCounter(String inLabel) {
49+
EventCounter metric = (EventCounter) MetricRepository.get(inLabel);
50+
if (metric == null) {
51+
metric = new EventCounter(inLabel);
52+
MetricRepository.put(metric);
53+
}
54+
return metric;
55+
}
56+
57+
/**
58+
* Gets the current Interval, the one to which current events are added.
59+
*
60+
* @return Interval - the current interval
61+
*/
62+
public Interval getCurrentInterval() {
63+
return mCurrentInterval;
64+
}
65+
66+
/**
67+
* Gets the interval in which the greatest number of events were added.
68+
*
69+
* @return Interval - having the greatest getEvents()
70+
*/
71+
public EventCounterInterval getPeakEventsInterval() {
72+
return mPeakEvents;
73+
}
74+
75+
/**
76+
* Gets the number of events that occurred during the interval in which the
77+
* most events occurred. A convenience method equivalent to
78+
* <code>getPeakEventsInterval().getEvents()</code>
79+
*
80+
* @return The peakEvents value
81+
*/
82+
public long getPeakEvents() {
83+
return getPeakEventsInterval().getEvents();
84+
}
85+
86+
@Override
87+
public long getCount() {
88+
return getCumulativeEvents();
89+
}
90+
91+
/**
92+
* Equivalent to getCount(), gets the total number of events added to this <code>EventCounter</code>.
93+
*
94+
* @return total number of events
95+
*/
96+
public long getCumulativeEvents() {
97+
return mCurrentInterval.getCumulativeEvents();
98+
}
99+
100+
/**
101+
* Gets the overall event frequency for this <code>EventCounter</code>
102+
*
103+
* @return float - events per second
104+
*/
105+
public float getCumulativeFreq() {
106+
float age = getAge();
107+
return age < 0.001 ? 0.0f : (getCumulativeEvents() / age);
108+
}
109+
110+
/**
111+
* Gets the current event frequency for this <code>EventCounter</code>. The
112+
* calculation includes the current incomplete interval. In order to reduce
113+
* excessive fluctuations when the current interval has just begun, the
114+
* immediately preceding interval, if one exists, is also included in the
115+
* calculation.
116+
*
117+
* @return float - events per second
118+
*/
119+
public float getCurrentFreq() {
120+
long events = mCurrentInterval.getEvents();
121+
long elapsedMillis = Clock.now() - mCurrentInterval.getStartTime();
122+
if (elapsedMillis <= 0) {
123+
return 0.0f;
124+
} else if (isPriorIntervalRelevant()) {
125+
events += mPriorInterval.getEvents();
126+
elapsedMillis += mPriorInterval.getDuration();
127+
}
128+
return (events * 1000) / (float) elapsedMillis;
129+
}
130+
131+
/**
132+
* Adds a number events.
133+
* <p/>
134+
* All events are considered to have happened now simultaneously.
135+
*
136+
* @param inCount number of events counted by this call
137+
*/
138+
public synchronized void add(int inCount) {
139+
normalize();
140+
mCurrentInterval.add(inCount);
141+
return;
142+
}
143+
144+
/**
145+
* Normalizes this EventCounter if necessary. An EventCounter is considered to
146+
* be normalized if the current time falls within the time span implied by the
147+
* currentInterval. Once enough time has elapsed for the currentInterval to be
148+
* outdated, then normalize() closes it and opens up a new currentInterval.
149+
*/
150+
public void normalize() {
151+
mNow = Clock.now();
152+
long i = mCurrentInterval.intervalsBefore(mNow);
153+
154+
// First check for the common case where the current time, now,
155+
// falls within the current interval. There is nothing to do.
156+
if (i <= 0)
157+
return;
158+
159+
// At this point, we know that enough time has elapsed that the current
160+
// interval referenced by mCurrentInterval is no longer current and therefore
161+
// needs to be updated, along with mPriorInterval. But first, it is the right
162+
// time to consider if the number of events that occurred in this current
163+
// interval -- a total that is now complete -- represents a new peak.
164+
if (mCurrentInterval.getEvents() > mPeakEvents.getEvents()) {
165+
mPeakEvents.copy(mCurrentInterval);
166+
}
167+
168+
if (i == 1) {
169+
// Scenario A: now is in the time interval immediately
170+
// following currentInterval.
171+
normalizePrior_ScenarioA();
172+
normalizeCurrent_ScenarioA();
173+
totalIntervals++;
174+
175+
} else if (i > 1) {
176+
// Scenario B: one or more "empty" time intervals lie between
177+
// currentInterval and now.
178+
normalizePrior_ScenarioB(i - 1);
179+
normalizeCurrent_ScenarioB();
180+
totalIntervals += i;
181+
}
182+
}
183+
184+
protected void normalizeCurrent_ScenarioA() {
185+
mCurrentInterval.setClosed(false);
186+
mCurrentInterval.setStartTime(mCurrentInterval.getStartTime() + mCurrentInterval.getDuration());
187+
mCurrentInterval.setPriorEvents(mCurrentInterval.getPriorEvents() + mCurrentInterval.getEvents());
188+
mCurrentInterval.setEvents(0);
189+
}
190+
191+
protected void normalizePrior_ScenarioA() {
192+
mPriorInterval.setClosed(true);
193+
mPriorInterval.setStartTime(mCurrentInterval.getStartTime());
194+
mPriorInterval.setEvents(mCurrentInterval.getEvents());
195+
mPriorInterval.setPriorEvents(mCurrentInterval.getPriorEvents());
196+
}
197+
198+
protected void normalizeCurrent_ScenarioB() {
199+
mCurrentInterval.setClosed(false);
200+
mCurrentInterval.setStartTime(mPriorInterval.getStartTime() + mCurrentInterval.getDuration());
201+
mCurrentInterval.setPriorEvents(mPriorInterval.getPriorEvents());
202+
mCurrentInterval.setEvents(0);
203+
}
204+
205+
protected void normalizePrior_ScenarioB(long nEmptyIntervals) {
206+
mPriorInterval.setClosed(true);
207+
mPriorInterval.setStartTime(mCurrentInterval.getStartTime() + mCurrentInterval.getDuration() * nEmptyIntervals);
208+
mPriorInterval.setPriorEvents(mCurrentInterval.getPriorEvents() + mCurrentInterval.getEvents());
209+
mPriorInterval.setEvents(0);
210+
}
211+
212+
/**
213+
* Reset this <code>EventCounter</code> to its "zero" condition so that it can
214+
* be reused, avoiding the overhead of constructing a new instance.
215+
*/
216+
public void reset() {
217+
super.reset();
218+
createIntervals();
219+
}
220+
221+
protected void createIntervals() {
222+
mCurrentInterval = new EventCounterInterval(intervalSeconds * 1000, startTime);
223+
mPriorInterval = new EventCounterInterval(intervalSeconds * 1000, startTime);
224+
mPeakEvents = new EventCounterInterval(intervalSeconds * 1000, startTime);
225+
}
226+
227+
private boolean isPriorIntervalRelevant() {
228+
if (mPriorInterval == null) {
229+
return false;
230+
} else {
231+
return mPriorInterval.getStartTime() != mCurrentInterval.getStartTime();
232+
}
233+
}
234+
}

0 commit comments

Comments
 (0)