| 1 | |
package kg.apc.jmeter.reporters; |
| 2 | |
|
| 3 | |
import java.text.SimpleDateFormat; |
| 4 | |
import java.util.Arrays; |
| 5 | |
import java.util.Date; |
| 6 | |
import java.util.Iterator; |
| 7 | |
import java.util.LinkedList; |
| 8 | |
import java.util.List; |
| 9 | |
import java.util.SortedMap; |
| 10 | |
import java.util.Stack; |
| 11 | |
import java.util.TreeMap; |
| 12 | |
import net.sf.json.JSONArray; |
| 13 | |
import net.sf.json.JSONObject; |
| 14 | |
import org.apache.jmeter.samplers.SampleResult; |
| 15 | |
import org.apache.jorphan.logging.LoggingManager; |
| 16 | |
import org.apache.log.Logger; |
| 17 | |
|
| 18 | 4 | public class LoadosophiaAggregator { |
| 19 | |
|
| 20 | 1 | private static final Logger log = LoggingManager.getLoggerForClass(); |
| 21 | 4 | private SortedMap<Long, List<SampleResult>> buffer = new TreeMap<Long, List<SampleResult>>(); |
| 22 | |
private static final long SEND_SECONDS = 5; |
| 23 | 4 | private long lastTime = 0; |
| 24 | 4 | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); |
| 25 | |
|
| 26 | |
public void addSample(SampleResult res) { |
| 27 | 109 | if (log.isDebugEnabled()) { |
| 28 | 0 | log.debug("Got sample to process: " + res); |
| 29 | |
} |
| 30 | |
|
| 31 | 109 | Long time = res.getEndTime() / 1000; |
| 32 | 109 | if (!buffer.containsKey(time)) { |
| 33 | |
|
| 34 | 8 | if (time < lastTime) { |
| 35 | |
|
| 36 | 0 | Iterator<Long> it = buffer.keySet().iterator(); |
| 37 | 0 | while (it.hasNext()) { |
| 38 | 0 | time = it.next(); |
| 39 | |
} |
| 40 | |
} |
| 41 | 8 | buffer.put(time, new LinkedList<SampleResult>()); |
| 42 | |
} |
| 43 | 109 | lastTime = time; |
| 44 | 109 | buffer.get(time).add(res); |
| 45 | 109 | } |
| 46 | |
|
| 47 | |
public boolean haveDataToSend() { |
| 48 | 104 | return buffer.size() > SEND_SECONDS + 1; |
| 49 | |
} |
| 50 | |
|
| 51 | |
public JSONArray getDataToSend() { |
| 52 | 2 | JSONArray data = new JSONArray(); |
| 53 | 2 | Iterator<Long> it = buffer.keySet().iterator(); |
| 54 | 2 | int cnt = 0; |
| 55 | 7 | while (cnt < SEND_SECONDS && it.hasNext()) { |
| 56 | 5 | Long sec = it.next(); |
| 57 | 5 | List<SampleResult> raw = buffer.get(sec); |
| 58 | 5 | data.add(getAggregateSecond(raw)); |
| 59 | 5 | it.remove(); |
| 60 | 5 | cnt++; |
| 61 | 5 | } |
| 62 | 2 | return data; |
| 63 | |
} |
| 64 | |
|
| 65 | |
private JSONObject getAggregateSecond(List<SampleResult> raw) { |
| 66 | |
|
| 67 | |
|
| 68 | |
|
| 69 | |
|
| 70 | 5 | JSONObject result = new JSONObject(); |
| 71 | 5 | Date ts = new Date(raw.iterator().next().getEndTime()); |
| 72 | 5 | result.put("ts", format.format(ts)); |
| 73 | |
|
| 74 | 5 | int threads = 0; |
| 75 | 5 | int avg_rt = 0; |
| 76 | 5 | Long[] rtimes = new Long[raw.size()]; |
| 77 | 5 | String[] rcodes = new String[raw.size()]; |
| 78 | 5 | int cnt = 0; |
| 79 | 5 | int failedCount = 0; |
| 80 | 5 | for (Iterator<SampleResult> it = raw.iterator(); it.hasNext();) { |
| 81 | 7 | SampleResult res = it.next(); |
| 82 | 7 | threads += res.getAllThreads(); |
| 83 | 7 | avg_rt += res.getTime(); |
| 84 | 7 | rtimes[cnt] = Long.valueOf(res.getTime()); |
| 85 | 7 | rcodes[cnt] = res.getResponseCode(); |
| 86 | 7 | if (!res.isSuccessful()) { |
| 87 | 7 | failedCount++; |
| 88 | |
} |
| 89 | 7 | cnt++; |
| 90 | 7 | } |
| 91 | 5 | result.put("rps", cnt); |
| 92 | 5 | result.put("threads", threads / cnt); |
| 93 | 5 | result.put("avg_rt", avg_rt / cnt); |
| 94 | 5 | result.put("quantiles", getQuantilesJSON(rtimes)); |
| 95 | 5 | result.put("net", getNetJSON(failedCount, cnt - failedCount)); |
| 96 | 5 | result.put("rc", getRCJSON(rcodes)); |
| 97 | 5 | result.put("planned_rps", 0); |
| 98 | 5 | return result; |
| 99 | |
} |
| 100 | |
|
| 101 | |
public static JSONObject getQuantilesJSON(Long[] rtimes) { |
| 102 | 7 | JSONObject result = new JSONObject(); |
| 103 | 7 | Arrays.sort(rtimes); |
| 104 | |
|
| 105 | 7 | double[] quantiles = {0.25, 0.50, 0.75, 0.80, 0.90, 0.95, 0.98, 0.99, 1.00}; |
| 106 | |
|
| 107 | 7 | Stack<Long> timings = new Stack(); |
| 108 | 7 | timings.addAll(Arrays.asList(rtimes)); |
| 109 | 7 | double level = 1.0; |
| 110 | 7 | long timing = 0; |
| 111 | 70 | for (int qn = quantiles.length - 1; qn >= 0; qn--) { |
| 112 | 63 | double quan = quantiles[qn]; |
| 113 | 78 | while (level >= quan && !timings.empty()) { |
| 114 | 15 | timing = timings.pop(); |
| 115 | 15 | level -= 1.0 / rtimes.length; |
| 116 | |
} |
| 117 | 63 | result.element(String.valueOf(quan * 100), timing); |
| 118 | |
} |
| 119 | |
|
| 120 | 7 | return result; |
| 121 | |
} |
| 122 | |
|
| 123 | |
private JSONObject getNetJSON(int failedCount, int succCount) { |
| 124 | 5 | JSONObject result = new JSONObject(); |
| 125 | 5 | result.put("0", succCount); |
| 126 | 5 | result.put("1", failedCount); |
| 127 | 5 | return result; |
| 128 | |
} |
| 129 | |
|
| 130 | |
private JSONObject getRCJSON(String[] rcodes) { |
| 131 | 5 | JSONObject result = new JSONObject(); |
| 132 | 12 | for (int i = 0; i < rcodes.length; i++) { |
| 133 | 7 | int oldval = 0; |
| 134 | 7 | if (result.containsKey(rcodes[i])) { |
| 135 | 2 | oldval = (Integer) result.get(rcodes[i]); |
| 136 | |
} |
| 137 | 7 | result.put(rcodes[i], oldval + 1); |
| 138 | |
|
| 139 | |
} |
| 140 | 5 | return result; |
| 141 | |
} |
| 142 | |
} |