kg.apc.jmeter.reporters.LoadosophiaClient Maven / Gradle / Ivy
The newest version!
package kg.apc.jmeter.reporters;
import kg.apc.jmeter.notifier.StatusNotifierCallback;
import kg.apc.jmeter.perfmon.PerfMonCollector;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.BackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.loadosophia.jmeter.LoadosophiaAPIClient;
import org.loadosophia.jmeter.LoadosophiaUploadResults;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.Stack;
import java.util.TimeZone;
import java.util.TreeMap;
public class LoadosophiaClient implements BackendListenerClient {
private static final Logger log = LoggingManager.getLoggerForClass();
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
protected String address = JMeterUtils.getPropDefault("sense.address", "https://sense.blazemeter.com/");
protected long delay = JMeterUtils.getPropDefault("sense.delay", 1000);
protected String fileName;
protected boolean isOnlineInitiated = false;
protected String token;
protected String project;
protected String color;
protected String title;
protected LoadosophiaAPIClient apiClient;
// this field set from LoadosophiaUploader after BackendListener created instance of this class
protected ResultCollector resultCollector;
protected StatusNotifierCallback informer;
// BackendListener called this method when test was started
@Override
public void setupTest(BackendListenerContext context) throws Exception {
init(context);
}
private void init(BackendListenerContext context) {
token = context.getParameter(LoadosophiaUploader.UPLOAD_TOKEN);
project = context.getParameter(LoadosophiaUploader.PROJECT);
color = context.getParameter(LoadosophiaUploader.COLOR);
title = context.getParameter(LoadosophiaUploader.TITLE);
fileName = context.getParameter(LoadosophiaUploader.FILE_NAME);
isOnlineInitiated = Boolean.parseBoolean(context.getParameter(LoadosophiaUploader.USE_ONLINE));
}
public void initiateOnline() {
apiClient = new LoadosophiaAPIClient(informer, address, token, project, color, title);
if (isOnlineInitiated) {
try {
log.info("Starting BM.Sense online test");
String url = apiClient.startOnline();
informer.notifyAbout("Started active test: " + url + "
");
} catch (IOException ex) {
informer.notifyAbout("Failed to start active test");
log.warn("Failed to initiate active test", ex);
this.isOnlineInitiated = false;
}
}
}
@Override
public void handleSampleResults(List list, BackendListenerContext backendListenerContext) {
if (list != null && isOnlineInitiated) {
try {
JSONArray array = getDataToSend(list);
log.info(array.toString());
apiClient.sendOnlineData(array);
} catch (IOException ex) {
log.warn("Failed to send active test data", ex);
}
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
log.warn("Backend listener client thread was interrupted");
}
}
}
// BackendListener called this method when test was ended
@Override
public void teardownTest(BackendListenerContext context) throws Exception {
stop();
}
protected void stop() {
String redirectLink = "";
try {
flush();
if (fileName == null) {
throw new IOException("File for upload was not set, search for errors above in log");
}
LinkedList monFiles;
if (hasStandardSet()) {
monFiles = PerfMonCollector.getFiles();
} else {
monFiles = new LinkedList<>();
}
LoadosophiaUploadResults uploadResult = this.apiClient.sendFiles(new File(fileName), monFiles);
redirectLink = uploadResult.getRedirectLink();
informer.notifyAbout("Uploaded successfully, go to results: " + redirectLink + "
");
} catch (Throwable ex) {
informer.notifyAbout("Failed to upload results to BM.Sense, see log for detais: " + ex.getMessage());
log.error("Failed to upload results to BM.Sense", ex);
}
if (isOnlineInitiated) {
finishOnline(redirectLink + "#tab=tabTimelines");
}
resultCollector.clearData();
if (hasStandardSet()) {
PerfMonCollector.clearFiles();
}
}
private void finishOnline(String redirectLink) {
log.info("Ending BM.Sense online test");
try {
apiClient.endOnline(redirectLink);
} catch (IOException ex) {
log.warn("Failed to finalize active test", ex);
}
isOnlineInitiated = false;
}
public JSONArray getDataToSend(List list) {
JSONArray data = new JSONArray();
SortedMap> sortedResults = sortResults(list);
for (Long sec : sortedResults.keySet()) {
data.add(getAggregateSecond(sec, sortedResults.get(sec)));
}
return data;
}
private SortedMap> sortResults(List list) {
SortedMap> sortedResults = new TreeMap<>();
for (SampleResult result : list) {
long time = result.getEndTime() / 1000;
if (!sortedResults.containsKey(time)) {
sortedResults.put(time, new LinkedList());
}
sortedResults.get(time).add(result);
}
return sortedResults;
}
private JSONObject getAggregateSecond(Long sec, List raw) {
/*
"rc": item.http_codes,
"net": item.net_codes
*/
JSONObject result = new JSONObject();
Date ts = new Date(sec * 1000);
log.debug("Aggregating " + sec);
result.put("ts", format.format(ts));
int avg_rt = 0;
Long[] rtimes = new Long[raw.size()];
String[] rcodes = new String[raw.size()];
int cnt = 0;
int failedCount = 0;
long maxThreadCount = 0;
for (SampleResult res : raw) {
if (maxThreadCount < res.getAllThreads()) {
maxThreadCount = res.getAllThreads();
}
avg_rt += res.getTime();
rtimes[cnt] = res.getTime();
rcodes[cnt] = res.getResponseCode();
if (!res.isSuccessful()) {
failedCount++;
}
cnt++;
}
result.put("rps", cnt);
result.put("threads", maxThreadCount);
result.put("avg_rt", avg_rt / cnt);
result.put("quantiles", getQuantilesJSON(rtimes));
result.put("net", getNetJSON(failedCount, cnt - failedCount));
result.put("rc", getRCJSON(rcodes));
result.put("planned_rps", 0); // JMeter has no such feature like Yandex.Tank
return result;
}
public static JSONObject getQuantilesJSON(Long[] rtimes) {
JSONObject result = new JSONObject();
Arrays.sort(rtimes);
double[] quantiles = {0.25, 0.50, 0.75, 0.80, 0.90, 0.95, 0.98, 0.99, 1.00};
Stack timings = new Stack<>();
timings.addAll(Arrays.asList(rtimes));
double level = 1.0;
Object timing = 0;
for (int qn = quantiles.length - 1; qn >= 0; qn--) {
double quan = quantiles[qn];
while (level >= quan && !timings.empty()) {
timing = timings.pop();
level -= 1.0 / rtimes.length;
}
result.element(String.valueOf(quan * 100), timing);
}
return result;
}
private JSONObject getNetJSON(int failedCount, int succCount) {
JSONObject result = new JSONObject();
result.put("0", succCount);
result.put("1", failedCount);
return result;
}
private JSONObject getRCJSON(String[] rcodes) {
JSONObject result = new JSONObject();
for (String rcode : rcodes) {
int oldval = 0;
if (result.containsKey(rcode)) {
oldval = (Integer) result.get(rcode);
}
result.put(rcode, oldval + 1);
}
return result;
}
@Override
public Arguments getDefaultParameters() {
return null;
}
@Override
public SampleResult createSampleResult(BackendListenerContext backendListenerContext, SampleResult sampleResult) {
return sampleResult;
}
private void flush() {
// FIXME: trying to handle safe upgrade, needs to be removed in the future
// @see https://issues.apache.org/bugzilla/show_bug.cgi?id=56807
try {
Class c = ResultCollector.class;
Method m = c.getDeclaredMethod("flushFile");
m.setAccessible(true);
m.invoke(resultCollector);
log.info("Successfully flushed results file");
} catch (NoSuchMethodException ex) {
log.warn("Cannot flush results file since you are using old version of JMeter, consider upgrading to latest. Currently the results may be incomplete.");
} catch (InvocationTargetException | IllegalAccessException e) {
log.error("Failed to flush file", e);
}
}
private boolean hasStandardSet() {
boolean hasStandardSet = true;
try {
Class.forName("kg.apc.jmeter.perfmon.PerfMonCollector");
} catch (ClassNotFoundException e) {
hasStandardSet = false;
}
return hasStandardSet;
}
public LoadosophiaAPIClient getApiClient() {
return apiClient;
}
public void setApiClient(LoadosophiaAPIClient apiClient) {
this.apiClient = apiClient;
}
public ResultCollector getResultCollector() {
return resultCollector;
}
public void setResultCollector(ResultCollector resultCollector) {
this.resultCollector = resultCollector;
}
public StatusNotifierCallback getInformer() {
return informer;
}
public void setInformer(StatusNotifierCallback informer) {
this.informer = informer;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public boolean isOnlineInitiated() {
return isOnlineInitiated;
}
public void setOnlineInitiated(boolean onlineInitiated) {
isOnlineInitiated = onlineInitiated;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy