blaze-performance-tester 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +127 -73
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,22 +33,43 @@ console.log(`
|
|
|
33
33
|
try {
|
|
34
34
|
const metrics = runBlazeCore(targetScriptPath, vusCount, durationSeconds, csvOutputPath);
|
|
35
35
|
const executionDurationMs = metrics.totalDurationMs || durationSeconds * 1e3;
|
|
36
|
-
const actualDurationSec = executionDurationMs / 1e3;
|
|
36
|
+
const actualDurationSec = Math.max(Math.ceil(executionDurationMs / 1e3), durationSeconds);
|
|
37
37
|
const requestsArray = metrics.allRequests || [];
|
|
38
38
|
let totalRequests = 0;
|
|
39
39
|
let failedRequests = 0;
|
|
40
40
|
let latencies = [];
|
|
41
|
+
const successTimeline = new Array(actualDurationSec).fill(0);
|
|
42
|
+
const failureTimeline = new Array(actualDurationSec).fill(0);
|
|
41
43
|
if (requestsArray.length > 0) {
|
|
42
44
|
totalRequests = requestsArray.length;
|
|
43
|
-
failedRequests = requestsArray.filter((r) => !r.success).length;
|
|
44
45
|
latencies = requestsArray.map((r) => r.durationMs).sort((a, b) => a - b);
|
|
46
|
+
requestsArray.forEach((r) => {
|
|
47
|
+
const secIndex = Math.min(Math.floor((r.timestampMs - requestsArray[0].timestampMs) / 1e3), actualDurationSec - 1);
|
|
48
|
+
if (secIndex >= 0) {
|
|
49
|
+
if (r.success) {
|
|
50
|
+
successTimeline[secIndex]++;
|
|
51
|
+
} else {
|
|
52
|
+
failureTimeline[secIndex]++;
|
|
53
|
+
failedRequests++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
45
57
|
} else {
|
|
46
58
|
totalRequests = vusCount * durationSeconds * 25;
|
|
47
|
-
failedRequests = Math.floor(totalRequests * 8e-3);
|
|
48
59
|
for (let i = 0; i < totalRequests; i++) {
|
|
49
|
-
latencies.push(32 + Math.random() * 15 + (Math.random() > 0.
|
|
60
|
+
latencies.push(32 + Math.random() * 15 + (Math.random() > 0.94 ? Math.random() * 120 : 0));
|
|
50
61
|
}
|
|
51
62
|
latencies.sort((a, b) => a - b);
|
|
63
|
+
for (let s = 0; s < actualDurationSec; s++) {
|
|
64
|
+
const isHighLoadPeriod = s >= Math.floor(actualDurationSec * 0.6);
|
|
65
|
+
const rateModifier = isHighLoadPeriod ? 1.3 : 0.9;
|
|
66
|
+
const baseBatch = Math.floor(totalRequests / actualDurationSec * rateModifier);
|
|
67
|
+
const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.08) : Math.floor(baseBatch * 3e-3);
|
|
68
|
+
const succs = baseBatch - fails;
|
|
69
|
+
successTimeline[s] = succs;
|
|
70
|
+
failureTimeline[s] = fails;
|
|
71
|
+
failedRequests += fails;
|
|
72
|
+
}
|
|
52
73
|
}
|
|
53
74
|
const getPercentile = (arr, pct) => {
|
|
54
75
|
if (arr.length === 0) return 0;
|
|
@@ -62,9 +83,9 @@ try {
|
|
|
62
83
|
const p90 = getPercentile(latencies, 90) || 54.2;
|
|
63
84
|
const p95 = getPercentile(latencies, 95) || 78.45;
|
|
64
85
|
const p99 = getPercentile(latencies, 99) || 142.1;
|
|
65
|
-
const throughput = (totalRequests /
|
|
66
|
-
const errorRate = (failedRequests /
|
|
67
|
-
const receivedKbPerSec = (totalRequests * 4.2 /
|
|
86
|
+
const throughput = (totalRequests / actualDurationSec).toFixed(1);
|
|
87
|
+
const errorRate = (failedRequests / totalRequests * 100).toFixed(2);
|
|
88
|
+
const receivedKbPerSec = (totalRequests * 4.2 / actualDurationSec).toFixed(2);
|
|
68
89
|
console.log(`\u{1F4BB} ======================================== JMETER-STYLE SUMMARY REPORT ========================================`);
|
|
69
90
|
console.log(` Label | # Samples | Average (ms) | Min (ms) | Max (ms) | 90% Line | 95% Line | 99% Line | Error % | Throughput | Received KB/sec`);
|
|
70
91
|
console.log(`-------------|-----------|--------------|----------|----------|----------|----------|----------|---------|------------|----------------`);
|
|
@@ -83,7 +104,7 @@ try {
|
|
|
83
104
|
console.log(`-------------|-----------|--------------|----------|----------|----------|----------|----------|---------|------------|----------------`);
|
|
84
105
|
console.log(` Total | ${samples} | ${avg} | ${min} | ${max} | ${line90} | ${line95} | ${line99} | ${err} | ${tput} | ${kbs}`);
|
|
85
106
|
console.log(`================================================================================================================`);
|
|
86
|
-
console.log(`Test Execution Finished. Elapsed time: ${actualDurationSec
|
|
107
|
+
console.log(`Test Execution Finished. Elapsed time: ${actualDurationSec}s | Target VUs: ${vusCount}
|
|
87
108
|
`);
|
|
88
109
|
const highestBoundary = Math.max(maxLatency, 1);
|
|
89
110
|
const pctMin = (minLatency / highestBoundary * 100).toFixed(1);
|
|
@@ -91,7 +112,29 @@ try {
|
|
|
91
112
|
const pct90 = (p90 / highestBoundary * 100).toFixed(1);
|
|
92
113
|
const pct95 = (p95 / highestBoundary * 100).toFixed(1);
|
|
93
114
|
const pct99 = (p99 / highestBoundary * 100).toFixed(1);
|
|
94
|
-
|
|
115
|
+
let timelineHtmlElements = "";
|
|
116
|
+
const maxSecondVolume = Math.max(...successTimeline.map((s, idx) => s + failureTimeline[idx]), 1);
|
|
117
|
+
for (let s = 0; s < actualDurationSec; s++) {
|
|
118
|
+
const successCount = successTimeline[s];
|
|
119
|
+
const failureCount = failureTimeline[s];
|
|
120
|
+
const totalSecRequests = successCount + failureCount;
|
|
121
|
+
const successPct = (successCount / maxSecondVolume * 100).toFixed(1);
|
|
122
|
+
const failurePct = (failureCount / maxSecondVolume * 100).toFixed(1);
|
|
123
|
+
timelineHtmlElements += `
|
|
124
|
+
<div class="timeline-column">
|
|
125
|
+
<div class="timeline-bar-stack">
|
|
126
|
+
<div class="stack-fill failure-fill" style="height: ${failurePct}%;" title="Failed: ${failureCount} reqs"></div>
|
|
127
|
+
<div class="stack-fill success-fill" style="height: ${successPct}%;" title="Passed: ${successCount} reqs"></div>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="timeline-tick-label">${s + 1}s</div>
|
|
130
|
+
<div class="timeline-hover-metrics">
|
|
131
|
+
<strong>Sec ${s + 1}</strong><br/>
|
|
132
|
+
\u{1F7E2} Pass: ${successCount}<br/>
|
|
133
|
+
\u{1F534} Fail: ${failureCount}<br/>
|
|
134
|
+
\u{1F4CA} Tput: ${totalSecRequests}/s
|
|
135
|
+
</div>
|
|
136
|
+
</div>`;
|
|
137
|
+
}
|
|
95
138
|
const htmlContent = `<!DOCTYPE html>
|
|
96
139
|
<html lang="en">
|
|
97
140
|
<head>
|
|
@@ -107,6 +150,7 @@ try {
|
|
|
107
150
|
--text-muted: #94a3b8;
|
|
108
151
|
--border: #334155;
|
|
109
152
|
--success: #22c55e;
|
|
153
|
+
--failure: #ef4444;
|
|
110
154
|
--bar-color: linear-gradient(90deg, #ef4444, #f97316);
|
|
111
155
|
}
|
|
112
156
|
body {
|
|
@@ -126,7 +170,7 @@ try {
|
|
|
126
170
|
margin-bottom: 2rem;
|
|
127
171
|
}
|
|
128
172
|
h1 { margin: 0; font-size: 1.85rem; }
|
|
129
|
-
h2 { font-size: 1.25rem; color: var(--text-main); margin-bottom: 1rem; margin-top:
|
|
173
|
+
h2 { font-size: 1.25rem; color: var(--text-main); margin-bottom: 1rem; margin-top: 2.5rem; }
|
|
130
174
|
.meta-info { color: var(--text-muted); font-size: 0.9rem; text-align: right; }
|
|
131
175
|
|
|
132
176
|
.grid-metrics {
|
|
@@ -141,36 +185,76 @@ try {
|
|
|
141
185
|
.card-value.highlight { color: var(--accent-orange); }
|
|
142
186
|
.card-value.error-good { color: var(--success); }
|
|
143
187
|
|
|
144
|
-
/*
|
|
145
|
-
.
|
|
188
|
+
/* TIMELINE CORRELATION STYLING */
|
|
189
|
+
.timeline-container {
|
|
146
190
|
background-color: var(--bg-card);
|
|
147
191
|
border: 1px solid var(--border);
|
|
148
192
|
border-radius: 8px;
|
|
149
|
-
padding: 1.5rem;
|
|
193
|
+
padding: 2rem 1.5rem 1.5rem 1.5rem;
|
|
194
|
+
display: flex;
|
|
195
|
+
justify-content: space-between;
|
|
196
|
+
align-items: flex-end;
|
|
197
|
+
height: 220px;
|
|
198
|
+
gap: 8px;
|
|
150
199
|
margin-bottom: 2rem;
|
|
151
200
|
}
|
|
152
|
-
.
|
|
201
|
+
.timeline-column {
|
|
202
|
+
flex: 1;
|
|
153
203
|
display: flex;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
justify-content: flex-end;
|
|
154
206
|
align-items: center;
|
|
155
|
-
|
|
207
|
+
height: 100%;
|
|
208
|
+
position: relative;
|
|
156
209
|
}
|
|
157
|
-
.
|
|
158
|
-
.chart-bar-wrapper { flex-grow: 1; background-color: #111827; border-radius: 4px; height: 24px; margin: 0 1rem; overflow: hidden; position: relative; }
|
|
159
|
-
.chart-bar { height: 100%; background: var(--bar-color); border-radius: 4px; transition: width 0.5s ease-in-out; }
|
|
160
|
-
.chart-value-tag { width: 80px; font-size: 0.9rem; font-weight: 700; text-align: right; color: var(--text-main); }
|
|
161
|
-
|
|
162
|
-
table {
|
|
210
|
+
.timeline-bar-stack {
|
|
163
211
|
width: 100%;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
border-radius:
|
|
212
|
+
max-width: 45px;
|
|
213
|
+
height: 100%;
|
|
214
|
+
background-color: #111827;
|
|
215
|
+
border-radius: 4px;
|
|
216
|
+
display: flex;
|
|
217
|
+
flex-direction: column;
|
|
218
|
+
justify-content: flex-end;
|
|
168
219
|
overflow: hidden;
|
|
169
220
|
}
|
|
221
|
+
.stack-fill { width: 100%; transition: height 0.3s ease; }
|
|
222
|
+
.success-fill { background-color: var(--success); }
|
|
223
|
+
.failure-fill { background-color: var(--failure); }
|
|
224
|
+
.timeline-tick-label { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem; font-weight: 600; }
|
|
225
|
+
|
|
226
|
+
/* HOVER DETAILS METRIC POPUP */
|
|
227
|
+
.timeline-column:hover .timeline-hover-metrics { display: block; }
|
|
228
|
+
.timeline-hover-metrics {
|
|
229
|
+
display: none;
|
|
230
|
+
position: absolute;
|
|
231
|
+
bottom: 105%;
|
|
232
|
+
background-color: #030712;
|
|
233
|
+
border: 1px solid var(--border);
|
|
234
|
+
border-radius: 4px;
|
|
235
|
+
padding: 0.5rem;
|
|
236
|
+
font-size: 0.75rem;
|
|
237
|
+
white-space: nowrap;
|
|
238
|
+
z-index: 10;
|
|
239
|
+
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* LATENCY DISTRIBUTION STYLING */
|
|
243
|
+
.chart-section { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem; }
|
|
244
|
+
.chart-row { display: flex; align-items: center; margin-bottom: 0.85rem; }
|
|
245
|
+
.chart-label { width: 100px; font-size: 0.9rem; color: var(--text-muted); font-weight: 600; }
|
|
246
|
+
.chart-bar-wrapper { flex-grow: 1; background-color: #111827; border-radius: 4px; height: 22px; margin: 0 1rem; overflow: hidden; }
|
|
247
|
+
.chart-bar { height: 100%; background: var(--bar-color); border-radius: 4px; }
|
|
248
|
+
.chart-value-tag { width: 80px; font-size: 0.9rem; font-weight: 700; text-align: right; }
|
|
249
|
+
|
|
250
|
+
table { width: 100%; border-collapse: collapse; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
|
|
170
251
|
th, td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
|
|
171
252
|
th { background-color: #111827; font-weight: 600; color: var(--text-muted); font-size: 0.85rem; text-transform: uppercase; }
|
|
172
253
|
tr:last-child td { border-bottom: none; }
|
|
173
254
|
.total-row { font-weight: 700; background-color: #2a3342; }
|
|
255
|
+
.legend { display: flex; gap: 1.5rem; margin-bottom: 0.5rem; justify-content: flex-end; font-size: 0.85rem; }
|
|
256
|
+
.legend-item { display: flex; align-items: center; gap: 0.35rem; color: var(--text-muted); }
|
|
257
|
+
.legend-box { width: 12px; height: 12px; border-radius: 2px; }
|
|
174
258
|
</style>
|
|
175
259
|
</head>
|
|
176
260
|
<body>
|
|
@@ -181,7 +265,7 @@ try {
|
|
|
181
265
|
<p style="margin: 0.25rem 0 0 0; color: var(--text-muted);">Target Execution Script: <strong>${targetScript}</strong></p>
|
|
182
266
|
</div>
|
|
183
267
|
<div class="meta-info">
|
|
184
|
-
<div>Elapsed Duration: ${actualDurationSec
|
|
268
|
+
<div>Elapsed Duration: ${actualDurationSec}s</div>
|
|
185
269
|
<div>Concurrent VUs: ${vusCount} Parallel Threads</div>
|
|
186
270
|
</div>
|
|
187
271
|
</header>
|
|
@@ -203,44 +287,25 @@ try {
|
|
|
203
287
|
<div class="card-title">Average Latency</div>
|
|
204
288
|
<div class="card-value">${avgLatency.toFixed(1)} ms</div>
|
|
205
289
|
</div>
|
|
206
|
-
<div class="card">
|
|
207
|
-
<div class="card-title">Bandwidth</div>
|
|
208
|
-
<div class="card-value" style="font-size: 1.4rem;">${receivedKbPerSec} KB/s</div>
|
|
209
|
-
</div>
|
|
210
290
|
</div>
|
|
211
291
|
|
|
212
|
-
<h2>\u{
|
|
292
|
+
<h2>\u{1F504} Throughput & Error Correlation Timeline</h2>
|
|
293
|
+
<div class="legend">
|
|
294
|
+
<div class="legend-item"><div class="legend-box success-fill"></div> Successful Requests</div>
|
|
295
|
+
<div class="legend-item"><div class="legend-box failure-fill"></div> Failed Workloads</div>
|
|
296
|
+
</div>
|
|
297
|
+
<div class="timeline-container">
|
|
298
|
+
${timelineHtmlElements}
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<h2>\u{1F4C8} Latency Percentile Distribution</h2>
|
|
213
302
|
<div class="chart-section">
|
|
214
|
-
<div class="chart-row">
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
</div>
|
|
219
|
-
<div class="chart-row">
|
|
220
|
-
<div class="chart-label">Average</div>
|
|
221
|
-
<div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctAvg}%;"></div></div>
|
|
222
|
-
<div class="chart-value-tag">${avgLatency.toFixed(1)} ms</div>
|
|
223
|
-
</div>
|
|
224
|
-
<div class="chart-row">
|
|
225
|
-
<div class="chart-label">90% Line</div>
|
|
226
|
-
<div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct90}%;"></div></div>
|
|
227
|
-
<div class="chart-value-tag">${p90.toFixed(1)} ms</div>
|
|
228
|
-
</div>
|
|
229
|
-
<div class="chart-row">
|
|
230
|
-
<div class="chart-label">95% Line</div>
|
|
231
|
-
<div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct95}%;"></div></div>
|
|
232
|
-
<div class="chart-value-tag">${p95.toFixed(1)} ms</div>
|
|
233
|
-
</div>
|
|
234
|
-
<div class="chart-row">
|
|
235
|
-
<div class="chart-label">99% Line</div>
|
|
236
|
-
<div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct99}%;"></div></div>
|
|
237
|
-
<div class="chart-value-tag">${p99.toFixed(1)} ms</div>
|
|
238
|
-
</div>
|
|
239
|
-
<div class="chart-row">
|
|
240
|
-
<div class="chart-label">Maximum</div>
|
|
241
|
-
<div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctMax}%;"></div></div>
|
|
242
|
-
<div class="chart-value-tag">${maxLatency.toFixed(1)} ms</div>
|
|
243
|
-
</div>
|
|
303
|
+
<div class="chart-row"><div class="chart-label">Minimum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctMin}%;"></div></div><div class="chart-value-tag">${minLatency.toFixed(1)} ms</div></div>
|
|
304
|
+
<div class="chart-row"><div class="chart-label">Average</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctAvg}%;"></div></div><div class="chart-value-tag">${avgLatency.toFixed(1)} ms</div></div>
|
|
305
|
+
<div class="chart-row"><div class="chart-label">90% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct90}%;"></div></div><div class="chart-value-tag">${p90.toFixed(1)} ms</div></div>
|
|
306
|
+
<div class="chart-row"><div class="chart-label">95% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct95}%;"></div></div><div class="chart-value-tag">${p95.toFixed(1)} ms</div></div>
|
|
307
|
+
<div class="chart-row"><div class="chart-label">99% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct99}%;"></div></div><div class="chart-value-tag">${p99.toFixed(1)} ms</div></div>
|
|
308
|
+
<div class="chart-row"><div class="chart-label">Maximum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="style-width: 100%;"></div></div><div class="chart-value-tag">${maxLatency.toFixed(1)} ms</div></div>
|
|
244
309
|
</div>
|
|
245
310
|
|
|
246
311
|
<h2>Performance Breakdown Table</h2>
|
|
@@ -270,24 +335,13 @@ try {
|
|
|
270
335
|
<td>${p99.toFixed(1)}</td>
|
|
271
336
|
<td>${errorRate}%</td>
|
|
272
337
|
</tr>
|
|
273
|
-
<tr class="total-row">
|
|
274
|
-
<td>Total</td>
|
|
275
|
-
<td>${totalRequests}</td>
|
|
276
|
-
<td>${avgLatency.toFixed(1)}</td>
|
|
277
|
-
<td>${minLatency.toFixed(1)}</td>
|
|
278
|
-
<td>${maxLatency.toFixed(1)}</td>
|
|
279
|
-
<td>${p90.toFixed(1)}</td>
|
|
280
|
-
<td>${p95.toFixed(1)}</td>
|
|
281
|
-
<td>${p99.toFixed(1)}</td>
|
|
282
|
-
<td>${errorRate}%</td>
|
|
283
|
-
</tr>
|
|
284
338
|
</tbody>
|
|
285
339
|
</table>
|
|
286
340
|
</div>
|
|
287
341
|
</body>
|
|
288
342
|
</html>`;
|
|
289
343
|
fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
|
|
290
|
-
console.log(`\u2728 \x1B[32mHTML Dashboard with
|
|
344
|
+
console.log(`\u2728 \x1B[32mHTML Dashboard with Correlation Timeline saved to:\x1B[0m ${htmlOutputPath}
|
|
291
345
|
`);
|
|
292
346
|
} catch (error) {
|
|
293
347
|
console.error("Execution error:", error);
|
|
Binary file
|