Skip to content

Commit 91c135a

Browse files
committed
extract LatencyRecorder as a top-level public class
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
1 parent 54cb8c0 commit 91c135a

File tree

2 files changed

+173
-121
lines changed

2 files changed

+173
-121
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 2016-2022 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.mortbay.jetty.load.generator.listeners;
15+
16+
import java.io.Closeable;
17+
import java.io.FileNotFoundException;
18+
import java.util.List;
19+
import java.util.Timer;
20+
import java.util.TimerTask;
21+
import java.util.concurrent.CopyOnWriteArrayList;
22+
23+
import org.HdrHistogram.Histogram;
24+
import org.HdrHistogram.HistogramLogWriter;
25+
import org.HdrHistogram.SingleWriterRecorder;
26+
27+
/**
28+
* A latency recorder that writes to a file one histogram per second in HdrHistogram's
29+
* {@code HistogramLogReader} format.
30+
*/
31+
public class LatencyRecorder
32+
{
33+
private final HistogramLogRecorder recorder;
34+
private final String histogramFilename;
35+
36+
/**
37+
* Creates a new instance.
38+
* @param histogramFilename the histograms file name.
39+
* @throws FileNotFoundException if the file cannot be created.
40+
*/
41+
public LatencyRecorder(String histogramFilename) throws FileNotFoundException
42+
{
43+
this(histogramFilename, 0);
44+
}
45+
/**
46+
* Creates a new instance
47+
* @param histogramFilename the histograms file name.
48+
* @param minBufferSize the minimum buffer size that is used to collect one histogram, 0 for a default value.
49+
* @throws FileNotFoundException if the file cannot be created.
50+
*/
51+
public LatencyRecorder(String histogramFilename, int minBufferSize) throws FileNotFoundException
52+
{
53+
this.recorder = new HistogramLogRecorder(histogramFilename, 3, 1000, minBufferSize);
54+
this.histogramFilename = histogramFilename;
55+
}
56+
57+
/**
58+
* @return the histograms file name.
59+
*/
60+
public String getFilename()
61+
{
62+
return histogramFilename;
63+
}
64+
65+
/**
66+
* Starts the recording of the histograms into the file.
67+
*/
68+
public void startRecording()
69+
{
70+
recorder.startRecording();
71+
}
72+
73+
/**
74+
* Stops the recording of the histograms into the file.
75+
*/
76+
public void stopRecording()
77+
{
78+
recorder.close();
79+
}
80+
81+
/**
82+
* Add a latency value to the current histogram.
83+
* @param value the latency in ns.
84+
*/
85+
public void recordValue(long value)
86+
{
87+
recorder.recordValue(value);
88+
}
89+
90+
private static class HistogramLogRecorder implements Closeable
91+
{
92+
private enum State
93+
{
94+
NOT_RECORDING, RECORDING, CLOSED
95+
}
96+
97+
private final ThreadLocal<SingleWriterRecorder> recorderTl;
98+
private final List<SingleWriterRecorder> recorders = new CopyOnWriteArrayList<>();
99+
private final Timer timer = new Timer();
100+
private final HistogramLogWriter writer;
101+
private volatile HistogramLogRecorder.State state = HistogramLogRecorder.State.NOT_RECORDING;
102+
103+
public HistogramLogRecorder(String histogramFilename, int numberOfSignificantValueDigits, int intervalInMs, int minBufferSize) throws FileNotFoundException
104+
{
105+
recorderTl = ThreadLocal.withInitial(() ->
106+
{
107+
SingleWriterRecorder singleWriterRecorder = new SingleWriterRecorder(numberOfSignificantValueDigits);
108+
recorders.add(singleWriterRecorder);
109+
return singleWriterRecorder;
110+
});
111+
writer = new HistogramLogWriter(histogramFilename);
112+
timer.schedule(new TimerTask()
113+
{
114+
private final Histogram collectiveHistogram = new Histogram(numberOfSignificantValueDigits)
115+
{
116+
public int getNeededByteBufferCapacity()
117+
{
118+
int superNeededByteBufferCapacity = super.getNeededByteBufferCapacity();
119+
if (minBufferSize > 0)
120+
return Math.max(superNeededByteBufferCapacity, minBufferSize);
121+
else
122+
return superNeededByteBufferCapacity;
123+
}
124+
};
125+
private Histogram intervalHistogram;
126+
127+
@Override
128+
public void run()
129+
{
130+
for (SingleWriterRecorder recorder : recorders)
131+
{
132+
intervalHistogram = recorder.getIntervalHistogram(intervalHistogram, false);
133+
collectiveHistogram.add(intervalHistogram);
134+
}
135+
if (state == HistogramLogRecorder.State.RECORDING)
136+
writer.outputIntervalHistogram(collectiveHistogram);
137+
collectiveHistogram.reset();
138+
}
139+
}, intervalInMs, intervalInMs);
140+
}
141+
142+
public void startRecording()
143+
{
144+
if (state != HistogramLogRecorder.State.NOT_RECORDING)
145+
throw new IllegalStateException("current state: " + state);
146+
147+
long now = System.currentTimeMillis();
148+
writer.setBaseTime(now);
149+
writer.outputBaseTime(now);
150+
writer.outputStartTime(now);
151+
state = HistogramLogRecorder.State.RECORDING;
152+
}
153+
154+
@Override
155+
public void close()
156+
{
157+
if (state == HistogramLogRecorder.State.CLOSED)
158+
return;
159+
state = HistogramLogRecorder.State.CLOSED;
160+
161+
timer.cancel();
162+
writer.close();
163+
}
164+
165+
public void recordValue(long value)
166+
{
167+
// Always record values even if state != State.RECORDING, the timer won't write the
168+
// histogram data on disk, but the histogram code will be jit'ed.
169+
recorderTl.get().recordValue(value);
170+
}
171+
}
172+
}

jetty-load-generator-listeners/src/main/java/org/mortbay/jetty/load/generator/listeners/ResponseTimeReportListener.java

Lines changed: 1 addition & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,15 @@
1313

1414
package org.mortbay.jetty.load.generator.listeners;
1515

16-
import java.io.Closeable;
1716
import java.io.FileNotFoundException;
18-
import java.util.List;
1917
import java.util.Timer;
2018
import java.util.TimerTask;
21-
import java.util.concurrent.CopyOnWriteArrayList;
2219

23-
import org.HdrHistogram.Histogram;
24-
import org.HdrHistogram.HistogramLogWriter;
25-
import org.HdrHistogram.SingleWriterRecorder;
2620
import org.mortbay.jetty.load.generator.LoadGenerator;
2721
import org.mortbay.jetty.load.generator.Resource;
2822

2923
/**
30-
* <p>A load generator listener that reports information about the status codes.</p>
24+
* <p>A load generator listener that reports response time histograms.</p>
3125
* <p>Usage:</p>
3226
* <pre>
3327
* // Create the report listener.
@@ -103,119 +97,5 @@ public void onComplete(LoadGenerator generator)
10397
{
10498
stopRecording();
10599
}
106-
107-
private static class LatencyRecorder
108-
{
109-
private final HistogramLogRecorder recorder;
110-
private final String histogramFilename;
111-
112-
public LatencyRecorder(String histogramFilename, int minBufferSize) throws FileNotFoundException
113-
{
114-
this.recorder = new HistogramLogRecorder(histogramFilename, 3, 1000, minBufferSize);
115-
this.histogramFilename = histogramFilename;
116-
}
117-
118-
public String getFilename()
119-
{
120-
return histogramFilename;
121-
}
122-
123-
public void startRecording()
124-
{
125-
recorder.startRecording();
126-
}
127-
128-
public void stopRecording()
129-
{
130-
recorder.close();
131-
}
132-
133-
public void recordValue(long value)
134-
{
135-
recorder.recordValue(value);
136-
}
137-
138-
private static class HistogramLogRecorder implements Closeable
139-
{
140-
private enum State
141-
{
142-
NOT_RECORDING, RECORDING, CLOSED
143-
}
144-
145-
private final ThreadLocal<SingleWriterRecorder> recorderTl;
146-
private final List<SingleWriterRecorder> recorders = new CopyOnWriteArrayList<>();
147-
private final Timer timer = new Timer();
148-
private final HistogramLogWriter writer;
149-
private volatile HistogramLogRecorder.State state = HistogramLogRecorder.State.NOT_RECORDING;
150-
151-
public HistogramLogRecorder(String histogramFilename, int numberOfSignificantValueDigits, int intervalInMs, int minBufferSize) throws FileNotFoundException
152-
{
153-
recorderTl = ThreadLocal.withInitial(() ->
154-
{
155-
SingleWriterRecorder singleWriterRecorder = new SingleWriterRecorder(numberOfSignificantValueDigits);
156-
recorders.add(singleWriterRecorder);
157-
return singleWriterRecorder;
158-
});
159-
writer = new HistogramLogWriter(histogramFilename);
160-
timer.schedule(new TimerTask()
161-
{
162-
private final Histogram collectiveHistogram = new Histogram(numberOfSignificantValueDigits)
163-
{
164-
public int getNeededByteBufferCapacity()
165-
{
166-
int superNeededByteBufferCapacity = super.getNeededByteBufferCapacity();
167-
if (minBufferSize > 0)
168-
return Math.max(superNeededByteBufferCapacity, minBufferSize);
169-
else
170-
return superNeededByteBufferCapacity;
171-
}
172-
};
173-
private Histogram intervalHistogram;
174-
@Override
175-
public void run()
176-
{
177-
for (SingleWriterRecorder recorder : recorders)
178-
{
179-
intervalHistogram = recorder.getIntervalHistogram(intervalHistogram, false);
180-
collectiveHistogram.add(intervalHistogram);
181-
}
182-
if (state == HistogramLogRecorder.State.RECORDING)
183-
writer.outputIntervalHistogram(collectiveHistogram);
184-
collectiveHistogram.reset();
185-
}
186-
}, intervalInMs, intervalInMs);
187-
}
188-
189-
public void startRecording()
190-
{
191-
if (state != HistogramLogRecorder.State.NOT_RECORDING)
192-
throw new IllegalStateException("current state: " + state);
193-
194-
long now = System.currentTimeMillis();
195-
writer.setBaseTime(now);
196-
writer.outputBaseTime(now);
197-
writer.outputStartTime(now);
198-
state = HistogramLogRecorder.State.RECORDING;
199-
}
200-
201-
@Override
202-
public void close()
203-
{
204-
if (state == HistogramLogRecorder.State.CLOSED)
205-
return;
206-
state = HistogramLogRecorder.State.CLOSED;
207-
208-
timer.cancel();
209-
writer.close();
210-
}
211-
212-
public void recordValue(long value)
213-
{
214-
// Always record values even if state != State.RECORDING, the timer won't write the
215-
// histogram data on disk, but the histogram code will be jit'ed.
216-
recorderTl.get().recordValue(value);
217-
}
218-
}
219-
}
220100
}
221101

0 commit comments

Comments
 (0)