blaze-performance-tester 2.0.2 → 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 +152 -47
- 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,24 +104,54 @@ 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
|
`);
|
|
109
|
+
const highestBoundary = Math.max(maxLatency, 1);
|
|
110
|
+
const pctMin = (minLatency / highestBoundary * 100).toFixed(1);
|
|
111
|
+
const pctAvg = (avgLatency / highestBoundary * 100).toFixed(1);
|
|
112
|
+
const pct90 = (p90 / highestBoundary * 100).toFixed(1);
|
|
113
|
+
const pct95 = (p95 / highestBoundary * 100).toFixed(1);
|
|
114
|
+
const pct99 = (p99 / highestBoundary * 100).toFixed(1);
|
|
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
|
+
}
|
|
88
138
|
const htmlContent = `<!DOCTYPE html>
|
|
89
139
|
<html lang="en">
|
|
90
140
|
<head>
|
|
91
141
|
<meta charset="UTF-8">
|
|
92
142
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
93
|
-
<title>Blaze Performance
|
|
143
|
+
<title>Blaze Performance Dashboard</title>
|
|
94
144
|
<style>
|
|
95
145
|
:root {
|
|
96
146
|
--bg-main: #0f172a;
|
|
97
147
|
--bg-card: #1e293b;
|
|
98
|
-
--accent-fire: #ef4444;
|
|
99
148
|
--accent-orange: #f97316;
|
|
100
149
|
--text-main: #f8fafc;
|
|
101
150
|
--text-muted: #94a3b8;
|
|
102
151
|
--border: #334155;
|
|
103
152
|
--success: #22c55e;
|
|
153
|
+
--failure: #ef4444;
|
|
154
|
+
--bar-color: linear-gradient(90deg, #ef4444, #f97316);
|
|
104
155
|
}
|
|
105
156
|
body {
|
|
106
157
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
@@ -109,10 +160,7 @@ try {
|
|
|
109
160
|
margin: 0;
|
|
110
161
|
padding: 2rem;
|
|
111
162
|
}
|
|
112
|
-
.container {
|
|
113
|
-
max-width: 1200px;
|
|
114
|
-
margin: 0 auto;
|
|
115
|
-
}
|
|
163
|
+
.container { max-width: 1200px; margin: 0 auto; }
|
|
116
164
|
header {
|
|
117
165
|
display: flex;
|
|
118
166
|
justify-content: space-between;
|
|
@@ -121,39 +169,92 @@ try {
|
|
|
121
169
|
padding-bottom: 1.5rem;
|
|
122
170
|
margin-bottom: 2rem;
|
|
123
171
|
}
|
|
124
|
-
h1 { margin: 0; font-size: 1.85rem;
|
|
172
|
+
h1 { margin: 0; font-size: 1.85rem; }
|
|
173
|
+
h2 { font-size: 1.25rem; color: var(--text-main); margin-bottom: 1rem; margin-top: 2.5rem; }
|
|
125
174
|
.meta-info { color: var(--text-muted); font-size: 0.9rem; text-align: right; }
|
|
126
175
|
|
|
127
176
|
.grid-metrics {
|
|
128
177
|
display: grid;
|
|
129
|
-
grid-template-columns: repeat(auto-fit, minmax(
|
|
178
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
130
179
|
gap: 1rem;
|
|
131
|
-
margin-bottom:
|
|
132
|
-
}
|
|
133
|
-
.card {
|
|
134
|
-
background-color: var(--bg-card);
|
|
135
|
-
border: 1px solid var(--border);
|
|
136
|
-
border-radius: 8px;
|
|
137
|
-
padding: 1.25rem;
|
|
180
|
+
margin-bottom: 2rem;
|
|
138
181
|
}
|
|
139
|
-
.card
|
|
140
|
-
.card-
|
|
182
|
+
.card { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; }
|
|
183
|
+
.card-title { color: var(--text-muted); font-size: 0.85rem; font-weight: 600; text-transform: uppercase; margin-bottom: 0.5rem; }
|
|
184
|
+
.card-value { font-size: 1.75rem; font-weight: 700; }
|
|
141
185
|
.card-value.highlight { color: var(--accent-orange); }
|
|
142
186
|
.card-value.error-good { color: var(--success); }
|
|
143
187
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
border-collapse: collapse;
|
|
188
|
+
/* TIMELINE CORRELATION STYLING */
|
|
189
|
+
.timeline-container {
|
|
147
190
|
background-color: var(--bg-card);
|
|
148
191
|
border: 1px solid var(--border);
|
|
149
192
|
border-radius: 8px;
|
|
150
|
-
|
|
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;
|
|
151
199
|
margin-bottom: 2rem;
|
|
152
200
|
}
|
|
201
|
+
.timeline-column {
|
|
202
|
+
flex: 1;
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
justify-content: flex-end;
|
|
206
|
+
align-items: center;
|
|
207
|
+
height: 100%;
|
|
208
|
+
position: relative;
|
|
209
|
+
}
|
|
210
|
+
.timeline-bar-stack {
|
|
211
|
+
width: 100%;
|
|
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;
|
|
219
|
+
overflow: hidden;
|
|
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; }
|
|
153
251
|
th, td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
|
|
154
252
|
th { background-color: #111827; font-weight: 600; color: var(--text-muted); font-size: 0.85rem; text-transform: uppercase; }
|
|
155
253
|
tr:last-child td { border-bottom: none; }
|
|
156
|
-
.total-row { font-weight: 700; background-color: #
|
|
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; }
|
|
157
258
|
</style>
|
|
158
259
|
</head>
|
|
159
260
|
<body>
|
|
@@ -164,7 +265,7 @@ try {
|
|
|
164
265
|
<p style="margin: 0.25rem 0 0 0; color: var(--text-muted);">Target Execution Script: <strong>${targetScript}</strong></p>
|
|
165
266
|
</div>
|
|
166
267
|
<div class="meta-info">
|
|
167
|
-
<div>Elapsed Duration: ${actualDurationSec
|
|
268
|
+
<div>Elapsed Duration: ${actualDurationSec}s</div>
|
|
168
269
|
<div>Concurrent VUs: ${vusCount} Parallel Threads</div>
|
|
169
270
|
</div>
|
|
170
271
|
</header>
|
|
@@ -186,10 +287,25 @@ try {
|
|
|
186
287
|
<div class="card-title">Average Latency</div>
|
|
187
288
|
<div class="card-value">${avgLatency.toFixed(1)} ms</div>
|
|
188
289
|
</div>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
290
|
+
</div>
|
|
291
|
+
|
|
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>
|
|
302
|
+
<div class="chart-section">
|
|
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>
|
|
193
309
|
</div>
|
|
194
310
|
|
|
195
311
|
<h2>Performance Breakdown Table</h2>
|
|
@@ -219,24 +335,13 @@ try {
|
|
|
219
335
|
<td>${p99.toFixed(1)}</td>
|
|
220
336
|
<td>${errorRate}%</td>
|
|
221
337
|
</tr>
|
|
222
|
-
<tr class="total-row">
|
|
223
|
-
<td>Total</td>
|
|
224
|
-
<td>${totalRequests}</td>
|
|
225
|
-
<td>${avgLatency.toFixed(1)}</td>
|
|
226
|
-
<td>${minLatency.toFixed(1)}</td>
|
|
227
|
-
<td>${maxLatency.toFixed(1)}</td>
|
|
228
|
-
<td>${p90.toFixed(1)}</td>
|
|
229
|
-
<td>${p95.toFixed(1)}</td>
|
|
230
|
-
<td>${p99.toFixed(1)}</td>
|
|
231
|
-
<td>${errorRate}%</td>
|
|
232
|
-
</tr>
|
|
233
338
|
</tbody>
|
|
234
339
|
</table>
|
|
235
340
|
</div>
|
|
236
341
|
</body>
|
|
237
342
|
</html>`;
|
|
238
343
|
fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
|
|
239
|
-
console.log(`\u2728 \x1B[32mHTML Dashboard
|
|
344
|
+
console.log(`\u2728 \x1B[32mHTML Dashboard with Correlation Timeline saved to:\x1B[0m ${htmlOutputPath}
|
|
240
345
|
`);
|
|
241
346
|
} catch (error) {
|
|
242
347
|
console.error("Execution error:", error);
|
|
Binary file
|