Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
3 |
* contributor license agreements. See the NOTICE file distributed with |
4 |
* this work for additional information regarding copyright ownership. |
5 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
6 |
* (the "License"); you may not use this file except in compliance with |
7 |
* the License. You may obtain a copy of the License at |
8 |
* |
9 |
* http://www.apache.org/licenses/LICENSE-2.0 |
10 |
* |
11 |
* Unless required by applicable law or agreed to in writing, software |
12 |
* distributed under the License is distributed on an "AS IS" BASIS, |
13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
* See the License for the specific language governing permissions and |
15 |
* limitations under the License. |
16 |
* |
17 |
*/ |
18 |
|
19 |
package org.apache.jmeter.reporters.graphite; |
20 |
|
21 |
import java.io.Serializable; |
22 |
import java.util.HashSet; |
23 |
import java.util.Map; |
24 |
import java.util.Set; |
25 |
import java.util.concurrent.ConcurrentHashMap; |
26 |
import java.util.concurrent.Executors; |
27 |
import java.util.concurrent.ScheduledExecutorService; |
28 |
import java.util.concurrent.TimeUnit; |
29 |
|
30 |
import org.apache.jmeter.engine.util.NoThreadClone; |
31 |
import org.apache.jmeter.reporters.AbstractListenerElement; |
32 |
import org.apache.jmeter.samplers.Clearable; |
33 |
import org.apache.jmeter.samplers.Remoteable; |
34 |
import org.apache.jmeter.samplers.SampleEvent; |
35 |
import org.apache.jmeter.samplers.SampleListener; |
36 |
import org.apache.jmeter.samplers.SampleResult; |
37 |
import org.apache.jmeter.testbeans.TestBean; |
38 |
import org.apache.jmeter.testelement.TestStateListener; |
39 |
import org.apache.jmeter.threads.JMeterContextService; |
40 |
import org.apache.jmeter.threads.JMeterContextService.ThreadCounts; |
41 |
import org.apache.jmeter.visualizers.Visualizer; |
42 |
import org.apache.jorphan.logging.LoggingManager; |
43 |
import org.apache.log.Logger; |
44 |
|
45 |
/** |
46 |
* Graphite based Listener using Pickle Protocol |
47 |
* @see http://graphite.readthedocs.org/en/latest/overview.html |
48 |
* @since 2.10.1 |
49 |
*/ |
50 |
public class GraphiteListener extends AbstractListenerElement implements SampleListener, Clearable, Serializable, |
51 |
TestStateListener, Remoteable, NoThreadClone, TestBean, Visualizer, Runnable { |
52 |
|
53 |
private static final String CUMULATED_CONTEXT_NAME = "cumulated"; |
54 |
/** |
55 |
* |
56 |
*/ |
57 |
private static final long serialVersionUID = -4862041959317479172L; |
58 |
private static final Logger LOGGER = LoggingManager.getLoggerForClass(); |
59 |
private static final String METRICS_PREFIX = "jmeter."; //$NON-NLS-1$ |
60 |
private static final String CUMULATED_METRICS = "__cumulated__"; //$NON-NLS-1$ |
61 |
private static final String METRIC_ACTIVE_THREADS = "activeThreads"; //$NON-NLS-1$ |
62 |
private static final String METRIC_STARTED_THREADS = "startedThreads"; //$NON-NLS-1$ |
63 |
private static final String METRIC_STOPPED_THREADS = "stoppedThreads"; //$NON-NLS-1$ |
64 |
private static final String METRIC_FAILED_REQUESTS = "failure"; //$NON-NLS-1$ |
65 |
private static final String METRIC_SUCCESSFUL_REQUESTS = "success"; //$NON-NLS-1$ |
66 |
private static final String METRIC_TOTAL_REQUESTS = "total"; //$NON-NLS-1$ |
67 |
private static final String METRIC_MIN_RESPONSE_TIME = "min"; //$NON-NLS-1$ |
68 |
private static final String METRIC_MAX_RESPONSE_TIME = "max"; //$NON-NLS-1$ |
69 |
private static final String METRIC_PERCENTILE90_RESPONSE_TIME = "percentile90"; //$NON-NLS-1$ |
70 |
private static final String METRIC_PERCENTILE95_RESPONSE_TIME = "percentile95"; //$NON-NLS-1$ |
71 |
private static final long ONE_SECOND = 1L; |
72 |
private static final int MAX_POOL_SIZE = 1; |
73 |
|
74 |
private String graphiteHost; |
75 |
private int graphitePort; |
76 |
private String summaryOnly; |
77 |
private String samplersList = ""; //$NON-NLS-1$ |
78 |
private transient Set<String> samplersToFilter; |
79 |
|
80 |
private ConcurrentHashMap<String, SamplerMetric> metricsPerSampler = new ConcurrentHashMap<String, SamplerMetric>(); |
81 |
|
82 |
private PickleMetricsManager pickleMetricsManager; |
83 |
|
84 |
private ScheduledExecutorService scheduler; |
85 |
|
86 |
public GraphiteListener() { |
87 |
super(); |
88 |
} |
89 |
|
90 |
/** {@inheritDoc} */ |
91 |
@Override |
92 |
public void clear() { |
93 |
super.clear(); |
94 |
metricsPerSampler.clear(); |
95 |
} |
96 |
|
97 |
/* (non-Javadoc) |
98 |
* @see org.apache.jmeter.reporters.ResultCollector#testStarted(java.lang.String) |
99 |
*/ |
100 |
@Override |
101 |
public void testStarted(String host) { |
102 |
String[] samplers = samplersList.split(","); |
103 |
samplersToFilter = new HashSet<String>(); |
104 |
for (String samplerName : samplers) { |
105 |
samplersToFilter.add(samplerName); |
106 |
} |
107 |
this.pickleMetricsManager = new PickleMetricsManager(graphiteHost, graphitePort, METRICS_PREFIX); |
108 |
scheduler = Executors.newScheduledThreadPool(MAX_POOL_SIZE); |
109 |
// Don't change this as metrics are per second |
110 |
scheduler.scheduleAtFixedRate(this, ONE_SECOND, ONE_SECOND, TimeUnit.SECONDS); |
111 |
} |
112 |
|
113 |
/* (non-Javadoc) |
114 |
* @see org.apache.jmeter.reporters.ResultCollector#testStarted() |
115 |
*/ |
116 |
@Override |
117 |
public void testStarted() { |
118 |
testStarted(""); |
119 |
} |
120 |
|
121 |
/* (non-Javadoc) |
122 |
* @see org.apache.jmeter.reporters.ResultCollector#testEnded(java.lang.String) |
123 |
*/ |
124 |
@Override |
125 |
public void testEnded(String host) { |
126 |
scheduler.shutdown(); |
127 |
try { |
128 |
scheduler.awaitTermination(30, TimeUnit.SECONDS); |
129 |
} catch (InterruptedException e) { |
130 |
LOGGER.error("Error waiting for end of scheduler"); |
131 |
} |
132 |
samplersToFilter.clear(); |
133 |
pickleMetricsManager.destroy(); |
134 |
metricsPerSampler.clear(); |
135 |
} |
136 |
|
137 |
/* (non-Javadoc) |
138 |
* @see org.apache.jmeter.reporters.ResultCollector#testEnded() |
139 |
*/ |
140 |
@Override |
141 |
public void testEnded() { |
142 |
testEnded(""); |
143 |
} |
144 |
|
145 |
/** {@inheritDoc} */ |
146 |
@Override |
147 |
public void sampleOccurred(SampleEvent e) { |
148 |
SampleResult sampleResult = e.getResult(); |
149 |
if(!"true".equals(summaryOnly) && samplersToFilter.contains(sampleResult.getSampleLabel())) { |
150 |
SamplerMetric samplerMetric = getSamplerMetric(sampleResult.getSampleLabel()); |
151 |
samplerMetric.add(sampleResult); |
152 |
} |
153 |
SamplerMetric cumulatedMetrics = getSamplerMetric(CUMULATED_METRICS); |
154 |
cumulatedMetrics.add(sampleResult); |
155 |
} |
156 |
|
157 |
private SamplerMetric getSamplerMetric(String sampleLabel) { |
158 |
SamplerMetric samplerMetric = metricsPerSampler.get(sampleLabel); |
159 |
if(samplerMetric == null) { |
160 |
samplerMetric = new SamplerMetric(); |
161 |
SamplerMetric oldValue = metricsPerSampler.putIfAbsent(sampleLabel, samplerMetric); |
162 |
if(oldValue != null ){ |
163 |
samplerMetric = oldValue; |
164 |
} |
165 |
} |
166 |
return samplerMetric; |
167 |
} |
168 |
|
169 |
@Override |
170 |
public void clearData() { |
171 |
// NOOP |
172 |
} |
173 |
|
174 |
@Override |
175 |
public void sampleStarted(SampleEvent e) { |
176 |
// NOOP |
177 |
} |
178 |
|
179 |
@Override |
180 |
public void sampleStopped(SampleEvent e) { |
181 |
// NOOP |
182 |
} |
183 |
|
184 |
@Override |
185 |
public void run() { |
186 |
// Need to convert millis to seconds for Graphite |
187 |
long timestamp = TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); |
188 |
for (Map.Entry<String, SamplerMetric> entry : metricsPerSampler.entrySet()) { |
189 |
SamplerMetric metric = entry.getValue(); |
190 |
if(entry.getKey().equals(CUMULATED_METRICS)) { |
191 |
addMetrics(timestamp, CUMULATED_CONTEXT_NAME, metric); |
192 |
} else { |
193 |
addMetrics(timestamp, PickleMetricsManager.sanitizeString(entry.getKey()), metric); |
194 |
} |
195 |
// We are computing on interval basis so cleanup |
196 |
metric.resetForTimeInterval(); |
197 |
} |
198 |
|
199 |
ThreadCounts tc = JMeterContextService.getThreadCounts(); |
200 |
pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME, METRIC_ACTIVE_THREADS, Integer.toString(tc.activeThreads)); |
201 |
pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME, METRIC_STARTED_THREADS, Integer.toString(tc.startedThreads)); |
202 |
pickleMetricsManager.addMetric(timestamp, CUMULATED_CONTEXT_NAME, METRIC_STOPPED_THREADS, Integer.toString(tc.finishedThreads)); |
203 |
|
204 |
pickleMetricsManager.writeAndSendMetrics(); |
205 |
} |
206 |
|
207 |
/** |
208 |
* @param timestamp |
209 |
* @param contextName |
210 |
* @param metric |
211 |
*/ |
212 |
private void addMetrics(long timestamp, String contextName, SamplerMetric metric) { |
213 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_FAILED_REQUESTS, Integer.toString(metric.getFailure())); |
214 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_SUCCESSFUL_REQUESTS, Integer.toString(metric.getSuccess())); |
215 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_TOTAL_REQUESTS, Integer.toString(metric.getTotal())); |
216 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_MIN_RESPONSE_TIME, Long.toString(metric.getMinTime())); |
217 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_MAX_RESPONSE_TIME, Long.toString(metric.getMaxTime())); |
218 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_PERCENTILE90_RESPONSE_TIME, Double.toString(metric.getPercentile(90))); |
219 |
pickleMetricsManager.addMetric(timestamp, contextName, METRIC_PERCENTILE95_RESPONSE_TIME, Double.toString(metric.getPercentile(95))); |
220 |
} |
221 |
|
222 |
/** |
223 |
* @return the summaryOnly |
224 |
*/ |
225 |
public String getSummaryOnly() { |
226 |
return summaryOnly; |
227 |
} |
228 |
|
229 |
/** |
230 |
* @param summaryOnly the summaryOnly to set |
231 |
*/ |
232 |
public void setSummaryOnly(String summaryOnly) { |
233 |
this.summaryOnly = summaryOnly; |
234 |
} |
235 |
|
236 |
@Override |
237 |
public void add(SampleResult sample) { |
238 |
// NOOP |
239 |
} |
240 |
|
241 |
@Override |
242 |
public boolean isStats() { |
243 |
return false; |
244 |
} |
245 |
|
246 |
/** |
247 |
* @return the graphiteHost |
248 |
*/ |
249 |
public String getGraphiteHost() { |
250 |
return graphiteHost; |
251 |
} |
252 |
|
253 |
/** |
254 |
* @param graphiteHost the graphiteHost to set |
255 |
*/ |
256 |
public void setGraphiteHost(String graphiteHost) { |
257 |
this.graphiteHost = graphiteHost; |
258 |
} |
259 |
|
260 |
/** |
261 |
* @return the graphitePort |
262 |
*/ |
263 |
public int getGraphitePort() { |
264 |
return graphitePort; |
265 |
} |
266 |
|
267 |
/** |
268 |
* @param graphitePort the graphitePort to set |
269 |
*/ |
270 |
public void setGraphitePort(int graphitePort) { |
271 |
this.graphitePort = graphitePort; |
272 |
} |
273 |
|
274 |
/** |
275 |
* @return the samplersList |
276 |
*/ |
277 |
public String getSamplersList() { |
278 |
return samplersList; |
279 |
} |
280 |
|
281 |
/** |
282 |
* @param samplersList the samplersList to set |
283 |
*/ |
284 |
public void setSamplersList(String samplersList) { |
285 |
this.samplersList = samplersList; |
286 |
} |
287 |
} |