blaze-performance-tester 3.0.18 → 3.0.20
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 +242 -143
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -172,9 +172,10 @@ async function runBlazeCoreEngine(config) {
|
|
|
172
172
|
}
|
|
173
173
|
async function runIntelligentAgenticStressTest(config) {
|
|
174
174
|
console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
|
|
175
|
-
console.log(`\u{1F3AF} Target Constraints: Apdex >= ${config.targetApdex} | Error Rate <= ${(config.targetErrorRate * 100).toFixed(1)}% | SLA Target <= ${config.maxAllowedLatencyMs}ms`);
|
|
176
175
|
const history = [];
|
|
177
176
|
let aggregateErrorLogs = [];
|
|
177
|
+
let total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
|
|
178
|
+
let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
|
|
178
179
|
let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
|
|
179
180
|
let baseStep = config.stepSize;
|
|
180
181
|
let isStable = true;
|
|
@@ -185,13 +186,25 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
185
186
|
if (config.clusterLogs) {
|
|
186
187
|
aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
|
|
187
188
|
}
|
|
189
|
+
total2xx += metrics.c2xx;
|
|
190
|
+
total3xx += metrics.c3xx;
|
|
191
|
+
total4xx += metrics.c4xx;
|
|
192
|
+
total5xx += metrics.c5xx;
|
|
193
|
+
globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
|
|
194
|
+
globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
|
|
195
|
+
globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
|
|
196
|
+
globalMetricsAccumulator.ttfb.push(metrics.avgTtfbMs);
|
|
197
|
+
globalMetricsAccumulator.latencies.push(metrics.avgLatencyMs);
|
|
198
|
+
globalMetricsAccumulator.bandwidths.push(metrics.bandwidthMb);
|
|
188
199
|
const currentState = {
|
|
189
200
|
threads: currentThreads,
|
|
190
201
|
avgLatency: metrics.avgLatencyMs,
|
|
191
202
|
p95Latency: metrics.p95LatencyMs,
|
|
192
203
|
errorRate: metrics.errorRate,
|
|
193
204
|
apdex: metrics.apdexScore,
|
|
194
|
-
throughput: metrics.requestsPerSecond
|
|
205
|
+
throughput: metrics.requestsPerSecond,
|
|
206
|
+
successRequests: metrics.c2xx + metrics.c3xx,
|
|
207
|
+
failedRequests: metrics.c4xx + metrics.c5xx
|
|
195
208
|
};
|
|
196
209
|
printMatrixDashboard(metrics, currentThreads);
|
|
197
210
|
history.push(currentState);
|
|
@@ -199,53 +212,48 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
199
212
|
const current = currentState;
|
|
200
213
|
const previous = history[history.length - 2];
|
|
201
214
|
const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
|
|
202
|
-
console.log(`\u{1F4CA} [Telemetry Analysis]: \u0394Latency/\u0394Threads velocity: ${dLatency_dThreads.toFixed(2)}ms/thread`);
|
|
203
215
|
if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
|
|
204
216
|
verdictReason = "The primary breakdown point was identified due to infrastructure saturation where throughput flattened while latency spiked rapidly.";
|
|
205
|
-
console.log(`\u26A0\uFE0F [Agent Alert]: ${verdictReason} Backing off.`);
|
|
206
217
|
isStable = false;
|
|
207
218
|
break;
|
|
208
219
|
}
|
|
209
|
-
const predictedLatencyNextStep = current.avgLatency + dLatency_dThreads * baseStep;
|
|
210
|
-
if (predictedLatencyNextStep > config.maxAllowedLatencyMs && current.apdex < 0.94) {
|
|
211
|
-
console.log(`\u{1F52E} [Predictive Model]: Risk Warning. Next step projects to hit ${predictedLatencyNextStep.toFixed(0)}ms. Compressing step size.`);
|
|
212
|
-
baseStep = Math.max(2, Math.floor(baseStep * 0.35));
|
|
213
|
-
}
|
|
214
220
|
}
|
|
215
221
|
if (currentState.p95Latency > config.maxAllowedLatencyMs || currentState.apdex < config.targetApdex) {
|
|
216
222
|
verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
|
|
217
|
-
console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. P95 Latency: ${currentState.p95Latency.toFixed(1)}ms | Apdex: ${currentState.apdex.toFixed(2)}`);
|
|
218
223
|
isStable = false;
|
|
219
224
|
break;
|
|
220
225
|
}
|
|
221
226
|
if (currentState.errorRate > config.targetErrorRate) {
|
|
222
227
|
verdictReason = "The primary breakdown point was identified due to HTTP error rates spiking past target service level boundaries.";
|
|
223
|
-
console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Errors: ${(currentState.errorRate * 100).toFixed(2)}%`);
|
|
224
228
|
isStable = false;
|
|
225
229
|
break;
|
|
226
230
|
}
|
|
227
|
-
if (currentThreads === config.maxThreads)
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
231
|
+
if (currentThreads === config.maxThreads) break;
|
|
230
232
|
const apdexMargin = (currentState.apdex - config.targetApdex) / (1 - config.targetApdex);
|
|
231
233
|
const adaptiveMultiplier = Math.max(0.15, Math.min(2, isNaN(apdexMargin) ? 1 : apdexMargin));
|
|
232
234
|
let nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
|
|
233
235
|
if (currentThreads + nextStep > config.maxThreads) {
|
|
234
236
|
nextStep = config.maxThreads - currentThreads;
|
|
235
237
|
}
|
|
236
|
-
console.log(`\u{1F4C8} [Step Adjustment]: Scaling factor ${adaptiveMultiplier.toFixed(2)}x. Next jump: +${nextStep} threads.`);
|
|
237
238
|
currentThreads += nextStep;
|
|
238
239
|
}
|
|
239
240
|
let semanticReport = null;
|
|
240
241
|
if (config.clusterLogs && aggregateErrorLogs.length > 0) {
|
|
241
|
-
console.log(`
|
|
242
|
-
\u{1F9E0} [Semantic Clustering]: Executing classification analyzer across ${aggregateErrorLogs.length} error entries...`);
|
|
243
242
|
const analyzer = new LogAnalyzer();
|
|
244
243
|
semanticReport = analyzer.process(aggregateErrorLogs);
|
|
245
|
-
console.log(`\u2705 Analysis Complete. Identified ${semanticReport.uniqueClustersCount} distinct signature blueprints.`);
|
|
246
244
|
}
|
|
245
|
+
const finalSummaryCards = {
|
|
246
|
+
apdex: history[history.length - 1]?.apdex || 0,
|
|
247
|
+
throughput: history[history.length - 1]?.throughput || 0,
|
|
248
|
+
bandwidth: mathUtils.mean(globalMetricsAccumulator.bandwidths),
|
|
249
|
+
ttfb: mathUtils.mean(globalMetricsAccumulator.ttfb),
|
|
250
|
+
dns: mathUtils.mean(globalMetricsAccumulator.dns),
|
|
251
|
+
tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
|
|
252
|
+
tls: mathUtils.mean(globalMetricsAccumulator.tls),
|
|
253
|
+
stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies))
|
|
254
|
+
};
|
|
247
255
|
generateFinalAgentReport(history);
|
|
248
|
-
exportHtmlDashboard(history, config, verdictReason, semanticReport);
|
|
256
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total2xx, total3xx, total4xx, total5xx }, finalSummaryCards);
|
|
249
257
|
}
|
|
250
258
|
async function runStandardStressTest(config) {
|
|
251
259
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
@@ -257,16 +265,33 @@ async function runStandardStressTest(config) {
|
|
|
257
265
|
p95Latency: metrics.p95LatencyMs,
|
|
258
266
|
errorRate: metrics.errorRate,
|
|
259
267
|
apdex: metrics.apdexScore,
|
|
260
|
-
throughput: metrics.requestsPerSecond
|
|
268
|
+
throughput: metrics.requestsPerSecond,
|
|
269
|
+
successRequests: metrics.c2xx + metrics.c3xx,
|
|
270
|
+
failedRequests: metrics.c4xx + metrics.c5xx
|
|
261
271
|
}];
|
|
262
272
|
let semanticReport = null;
|
|
263
273
|
if (config.clusterLogs && rawErrors.length > 0) {
|
|
264
274
|
const analyzer = new LogAnalyzer();
|
|
265
275
|
semanticReport = analyzer.process(rawErrors);
|
|
266
276
|
}
|
|
277
|
+
const finalSummaryCards = {
|
|
278
|
+
apdex: metrics.apdexScore,
|
|
279
|
+
throughput: metrics.requestsPerSecond,
|
|
280
|
+
bandwidth: metrics.bandwidthMb,
|
|
281
|
+
ttfb: metrics.avgTtfbMs,
|
|
282
|
+
dns: metrics.avgDnsMs,
|
|
283
|
+
tcp: metrics.avgTcpMs,
|
|
284
|
+
tls: metrics.avgTlsMs,
|
|
285
|
+
stdDev: metrics.stdDevMs
|
|
286
|
+
};
|
|
267
287
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
268
288
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
269
|
-
exportHtmlDashboard(history, config, verdictReason, semanticReport
|
|
289
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport, {
|
|
290
|
+
total2xx: metrics.c2xx,
|
|
291
|
+
total3xx: metrics.c3xx,
|
|
292
|
+
total4xx: metrics.c4xx,
|
|
293
|
+
total5xx: metrics.c5xx
|
|
294
|
+
}, finalSummaryCards);
|
|
270
295
|
}
|
|
271
296
|
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
272
297
|
const data = [];
|
|
@@ -286,15 +311,15 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
286
311
|
let errorMessage;
|
|
287
312
|
let statusCode = 200;
|
|
288
313
|
if (isError) {
|
|
289
|
-
statusCode = 500;
|
|
314
|
+
statusCode = Math.random() > 0.3 ? 500 : 404;
|
|
290
315
|
errorMessage = `[${(/* @__PURE__ */ new Date()).toISOString()}] ` + errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
291
316
|
}
|
|
292
317
|
data.push({
|
|
293
318
|
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
294
319
|
timestampMs: startTime + Math.random() * (durationSec * 1e3),
|
|
295
|
-
dnsTimeMs: 1 + Math.random() *
|
|
296
|
-
tcpTimeMs:
|
|
297
|
-
tlsTimeMs:
|
|
320
|
+
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
321
|
+
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
322
|
+
tlsTimeMs: 6 + Math.random() * 1.8,
|
|
298
323
|
statusCode,
|
|
299
324
|
errorMessage
|
|
300
325
|
});
|
|
@@ -307,12 +332,24 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
307
332
|
const avgLatencyMs = mathUtils.mean(durations);
|
|
308
333
|
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
309
334
|
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
310
|
-
const
|
|
335
|
+
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
336
|
+
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
337
|
+
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
338
|
+
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
339
|
+
let c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
340
|
+
requests.forEach((r) => {
|
|
341
|
+
if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
342
|
+
else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
|
|
343
|
+
else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
|
|
344
|
+
else if (r.statusCode >= 500) c5xx++;
|
|
345
|
+
});
|
|
346
|
+
const errorCount = c4xx + c5xx;
|
|
311
347
|
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
312
|
-
const T =
|
|
348
|
+
const T = 50;
|
|
313
349
|
const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
|
|
314
350
|
const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
|
|
315
351
|
const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
|
|
352
|
+
const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
|
|
316
353
|
return {
|
|
317
354
|
totalRequests,
|
|
318
355
|
requestsPerSecond: totalRequests / durationSec,
|
|
@@ -320,7 +357,16 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
320
357
|
stdDevMs,
|
|
321
358
|
p95LatencyMs,
|
|
322
359
|
errorRate,
|
|
323
|
-
apdexScore
|
|
360
|
+
apdexScore,
|
|
361
|
+
c2xx,
|
|
362
|
+
c3xx,
|
|
363
|
+
c4xx,
|
|
364
|
+
c5xx,
|
|
365
|
+
avgDnsMs,
|
|
366
|
+
avgTcpMs,
|
|
367
|
+
avgTlsMs,
|
|
368
|
+
avgTtfbMs,
|
|
369
|
+
bandwidthMb
|
|
324
370
|
};
|
|
325
371
|
}
|
|
326
372
|
function printMatrixDashboard(m, threads) {
|
|
@@ -344,11 +390,12 @@ function generateFinalAgentReport(history) {
|
|
|
344
390
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
345
391
|
console.log("=======================================================\n");
|
|
346
392
|
}
|
|
347
|
-
function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
348
|
-
const labels = history.map((h) => `${
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
const
|
|
393
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards) {
|
|
394
|
+
const labels = history.map((h, i) => `${i + 1}s`);
|
|
395
|
+
const successRequestsData = history.map((h) => h.successRequests);
|
|
396
|
+
const failedWorkloadsData = history.map((h) => h.failedRequests);
|
|
397
|
+
const activeThreadsData = history.map((h) => h.threads);
|
|
398
|
+
const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
|
|
352
399
|
const waveListItems = history.map((h) => {
|
|
353
400
|
return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
|
|
354
401
|
}).join("");
|
|
@@ -357,14 +404,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
357
404
|
logClustersHtml = `
|
|
358
405
|
<div class="card" style="margin-top: 2rem;">
|
|
359
406
|
<h3>\u{1F9E0} Semantic Log Clustering & Error Classification</h3>
|
|
360
|
-
<p style="color: #94a3b8; font-size: 0.9rem;">Captured <strong>${semanticReport.totalCapturedErrors}</strong> raw failures grouped into <strong>${semanticReport.uniqueClustersCount}</strong> unique structural patterns.</p>
|
|
361
407
|
<table>
|
|
362
408
|
<thead>
|
|
363
409
|
<tr>
|
|
364
410
|
<th style="width: 10%;">Count</th>
|
|
365
411
|
<th style="width: 25%;">Classification Category</th>
|
|
366
412
|
<th style="width: 10%;">Severity</th>
|
|
367
|
-
<th style="width: 55%;">Structural Abstract Blueprint
|
|
413
|
+
<th style="width: 55%;">Structural Abstract Blueprint</th>
|
|
368
414
|
</tr>
|
|
369
415
|
</thead>
|
|
370
416
|
<tbody>
|
|
@@ -378,12 +424,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
378
424
|
<td><span style="font-size: 1.1rem; font-weight: bold; color: #f8fafc;">${c.count}</span></td>
|
|
379
425
|
<td><span style="color: ${catColor}; font-weight: 600;">${c.category}</span></td>
|
|
380
426
|
<td><span style="background: ${sevColor}; color: #fff; padding: 0.15rem 0.4rem; border-radius:3px; font-size:0.75rem; font-weight:bold;">${c.severity}</span></td>
|
|
381
|
-
<td>
|
|
382
|
-
<code style="color: #cbd5e1; background: #0f172a; padding: 0.3rem 0.5rem; display: block; border-radius: 4px; font-size: 0.85rem; word-break: break-all;">${c.template}</code>
|
|
383
|
-
<div style="margin-top: 0.4rem; font-size: 0.8rem; color: #64748b;">
|
|
384
|
-
<strong>Real Example Trace:</strong> <span style="font-family: monospace;">${c.examples[0] || ""}</span>
|
|
385
|
-
</div>
|
|
386
|
-
</td>
|
|
427
|
+
<td><code style="color: #cbd5e1; background: #0f172a; padding: 0.3rem 0.5rem; display: block; border-radius: 4px; font-size: 0.85rem;">${c.template}</code></td>
|
|
387
428
|
</tr>`;
|
|
388
429
|
}).join("")}
|
|
389
430
|
</tbody>
|
|
@@ -395,52 +436,50 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
395
436
|
<title>Blaze Core Performance Report</title>
|
|
396
437
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
397
438
|
<style>
|
|
398
|
-
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #
|
|
399
|
-
.container { max-width:
|
|
400
|
-
.header { border-bottom: 1px solid #
|
|
439
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
|
|
440
|
+
.container { max-width: 1300px; margin: 0 auto; }
|
|
441
|
+
.header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
|
|
401
442
|
h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
|
|
402
443
|
.meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
.
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
.
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
.
|
|
438
|
-
|
|
439
|
-
h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #334155; padding-bottom: 0.5rem; }
|
|
444
|
+
|
|
445
|
+
/* \u{1F3B4} CARDS DASHBOARD GRID SYSTEM */
|
|
446
|
+
.cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
|
|
447
|
+
.metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
|
|
448
|
+
.card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
|
449
|
+
.card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
|
|
450
|
+
.card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-left: 0.2rem; }
|
|
451
|
+
.card-value.green { color: #10b981; }
|
|
452
|
+
.card-value.orange { color: #f97316; }
|
|
453
|
+
.card-value.purple { color: #a855f7; }
|
|
454
|
+
.card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
|
|
455
|
+
|
|
456
|
+
.top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
|
|
457
|
+
.verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
|
|
458
|
+
.verdict-title { text-transform: uppercase; font-size: 0.85rem; font-weight: 700; color: #f97316; margin-bottom: 0.5rem; }
|
|
459
|
+
.verdict-text { font-size: 0.95rem; line-height: 1.5; color: #e2e8f0; margin-bottom: 0.75rem; }
|
|
460
|
+
.verdict-waves { background: #1f2937; border-radius: 6px; padding: 0.75rem 1rem; font-family: monospace; color: #9ca3af; font-size: 0.9rem; }
|
|
461
|
+
.wave-item { margin: 0.25rem 0; }
|
|
462
|
+
|
|
463
|
+
.matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
|
|
464
|
+
.matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
|
|
465
|
+
.matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
|
|
466
|
+
.matrix-row:last-child { border-bottom: none; }
|
|
467
|
+
.matrix-label { font-size: 0.9rem; color: #f8fafc; display: flex; align-items: center; gap: 0.5rem; }
|
|
468
|
+
.matrix-badge { font-size: 0.7rem; font-weight: bold; padding: 0.1rem 0.3rem; border-radius: 4px; }
|
|
469
|
+
.badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
|
|
470
|
+
.badge-3xx { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; }
|
|
471
|
+
.badge-4xx { background: rgba(234, 179, 8, 0.2); color: #fde047; }
|
|
472
|
+
.badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
|
|
473
|
+
.matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
|
|
474
|
+
|
|
475
|
+
.graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem; }
|
|
476
|
+
.graph-title { font-size: 1.15rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
|
|
477
|
+
|
|
478
|
+
.card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
|
|
479
|
+
h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem; }
|
|
440
480
|
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
|
|
441
|
-
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #
|
|
481
|
+
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #1e293b; }
|
|
442
482
|
th { color: #94a3b8; font-weight: 600; }
|
|
443
|
-
tr:hover { background: #273549; }
|
|
444
483
|
.badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
|
|
445
484
|
.badge-success { background: #16a34a; color: #f0fdf4; }
|
|
446
485
|
.badge-fail { background: #dc2626; color: #fef2f2; }
|
|
@@ -448,22 +487,81 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
448
487
|
<div class="container">
|
|
449
488
|
<div class="header">
|
|
450
489
|
<h1>\u{1F525} Blaze Core Performance Analysis</h1>
|
|
451
|
-
<div class="meta">Target Script: <code>${config.targetScript}</code
|
|
490
|
+
<div class="meta">Target Script: <code>${config.targetScript}</code></div>
|
|
491
|
+
</div>
|
|
492
|
+
|
|
493
|
+
<!-- \u{1F3B4} METRICS CARDS GRID PANEL -->
|
|
494
|
+
<div class="cards-wrapper">
|
|
495
|
+
<div class="metric-card">
|
|
496
|
+
<div class="card-title">Apdex Score (T: 50ms)</div>
|
|
497
|
+
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
|
|
498
|
+
</div>
|
|
499
|
+
<div class="metric-card">
|
|
500
|
+
<div class="card-title">Throughput</div>
|
|
501
|
+
<div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
|
|
502
|
+
</div>
|
|
503
|
+
<div class="metric-card">
|
|
504
|
+
<div class="card-title">Network Bandwidth</div>
|
|
505
|
+
<div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
|
|
506
|
+
</div>
|
|
507
|
+
<div class="metric-card">
|
|
508
|
+
<div class="card-title">Avg Time To First Byte</div>
|
|
509
|
+
<div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
|
|
510
|
+
</div>
|
|
511
|
+
<div class="metric-card">
|
|
512
|
+
<div class="card-title">DNS Lookup</div>
|
|
513
|
+
<div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
|
|
514
|
+
</div>
|
|
515
|
+
<div class="metric-card">
|
|
516
|
+
<div class="card-title">TCP Connect</div>
|
|
517
|
+
<div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
|
|
518
|
+
</div>
|
|
519
|
+
<div class="metric-card">
|
|
520
|
+
<div class="card-title">TLS Handshake</div>
|
|
521
|
+
<div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
|
|
522
|
+
</div>
|
|
523
|
+
<div class="metric-card">
|
|
524
|
+
<div class="card-title">Stability (Std Dev)</div>
|
|
525
|
+
<div class="card-value">±${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
|
|
526
|
+
</div>
|
|
452
527
|
</div>
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
<div class="verdict-
|
|
456
|
-
|
|
457
|
-
|
|
528
|
+
|
|
529
|
+
<div class="top-layout-grid">
|
|
530
|
+
<div class="verdict-box">
|
|
531
|
+
<div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
|
|
532
|
+
<div class="verdict-text">${verdictReason}</div>
|
|
533
|
+
<div class="verdict-waves">${waveListItems}</div>
|
|
458
534
|
</div>
|
|
459
|
-
|
|
460
|
-
|
|
535
|
+
|
|
536
|
+
<div class="matrix-box">
|
|
537
|
+
<div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
|
|
538
|
+
<div class="matrix-row">
|
|
539
|
+
<div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
|
|
540
|
+
<div class="matrix-value">${responseMatrix.total2xx}</div>
|
|
541
|
+
</div>
|
|
542
|
+
<div class="matrix-row">
|
|
543
|
+
<div class="matrix-label">Redirects <span class="matrix-badge badge-3xx">3xx</span></div>
|
|
544
|
+
<div class="matrix-value">${responseMatrix.total3xx}</div>
|
|
545
|
+
</div>
|
|
546
|
+
<div class="matrix-row">
|
|
547
|
+
<div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
|
|
548
|
+
<div class="matrix-value">${responseMatrix.total4xx}</div>
|
|
549
|
+
</div>
|
|
550
|
+
<div class="matrix-row">
|
|
551
|
+
<div class="matrix-label">Server Error <span class="matrix-badge badge-5xx">5xx</span></div>
|
|
552
|
+
<div class="matrix-value">${responseMatrix.total5xx}</div>
|
|
553
|
+
</div>
|
|
461
554
|
</div>
|
|
462
555
|
</div>
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
556
|
+
|
|
557
|
+
<!-- \u{1F4C9} CORRELATION STREAM GRAPH CHART -->
|
|
558
|
+
<div class="graph-card">
|
|
559
|
+
<div class="graph-title">\u{1F504} Throughput, TTF Trend & Active Threads Concurrency Correlation</div>
|
|
560
|
+
<div style="height: 320px; position: relative;">
|
|
561
|
+
<canvas id="correlationChart"></canvas>
|
|
562
|
+
</div>
|
|
466
563
|
</div>
|
|
564
|
+
|
|
467
565
|
<div class="card">
|
|
468
566
|
<h3>Telemetry Matrix Logs</h3>
|
|
469
567
|
<table>
|
|
@@ -474,25 +572,19 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
474
572
|
<th>Avg Latency</th>
|
|
475
573
|
<th>P95 Latency</th>
|
|
476
574
|
<th>Error Rate</th>
|
|
477
|
-
<th>Apdex Score</th>
|
|
478
575
|
<th>Status</th>
|
|
479
576
|
</tr>
|
|
480
577
|
</thead>
|
|
481
578
|
<tbody>
|
|
482
579
|
${history.map((h) => {
|
|
483
580
|
const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
|
|
484
|
-
return
|
|
581
|
+
return `<tr>
|
|
485
582
|
<td><strong>${h.threads}</strong></td>
|
|
486
583
|
<td>${h.throughput.toFixed(0)} req/sec</td>
|
|
487
584
|
<td>${h.avgLatency.toFixed(1)} ms</td>
|
|
488
585
|
<td>${h.p95Latency.toFixed(1)} ms</td>
|
|
489
586
|
<td>${(h.errorRate * 100).toFixed(2)}%</td>
|
|
490
|
-
<td>${
|
|
491
|
-
<td>
|
|
492
|
-
<span class="badge ${isPassed ? "badge-success" : "badge-fail"}">
|
|
493
|
-
${isPassed ? "SLO PASS" : "SLO BREACH"}
|
|
494
|
-
</span>
|
|
495
|
-
</td>
|
|
587
|
+
<td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
|
|
496
588
|
</tr>`;
|
|
497
589
|
}).join("")}
|
|
498
590
|
</tbody>
|
|
@@ -500,49 +592,67 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
500
592
|
</div>
|
|
501
593
|
${logClustersHtml}
|
|
502
594
|
</div>
|
|
595
|
+
|
|
503
596
|
<script>
|
|
504
597
|
const labels = ${JSON.stringify(labels)};
|
|
505
|
-
|
|
506
|
-
type: '
|
|
507
|
-
data: {
|
|
508
|
-
labels: labels,
|
|
509
|
-
datasets: [{
|
|
510
|
-
label: 'Throughput (req/sec)',
|
|
511
|
-
data: ${JSON.stringify(throughputData)},
|
|
512
|
-
borderColor: '#38bdf8',
|
|
513
|
-
backgroundColor: 'rgba(56, 189, 248, 0.1)',
|
|
514
|
-
fill: true,
|
|
515
|
-
tension: 0.1
|
|
516
|
-
}]
|
|
517
|
-
},
|
|
518
|
-
options: { responsive: true, plugins: { legend: { labels: { color: '#f8fafc' } } } }
|
|
519
|
-
});
|
|
520
|
-
new Chart(document.getElementById('latencyChart'), {
|
|
521
|
-
type: 'line',
|
|
598
|
+
new Chart(document.getElementById('correlationChart'), {
|
|
599
|
+
type: 'bar',
|
|
522
600
|
data: {
|
|
523
601
|
labels: labels,
|
|
524
602
|
datasets: [
|
|
525
603
|
{
|
|
526
|
-
label: '
|
|
527
|
-
data: ${JSON.stringify(
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
604
|
+
label: 'Successful Requests',
|
|
605
|
+
data: ${JSON.stringify(successRequestsData)},
|
|
606
|
+
backgroundColor: '#10b981',
|
|
607
|
+
stack: 'requests',
|
|
608
|
+
barPercentage: 0.95,
|
|
609
|
+
categoryPercentage: 0.95
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
label: 'Failed Workloads',
|
|
613
|
+
data: ${JSON.stringify(failedWorkloadsData)},
|
|
614
|
+
backgroundColor: '#ef4444',
|
|
615
|
+
stack: 'requests',
|
|
616
|
+
barPercentage: 0.95,
|
|
617
|
+
categoryPercentage: 0.95
|
|
531
618
|
},
|
|
532
619
|
{
|
|
533
|
-
label: '
|
|
534
|
-
data: ${JSON.stringify(
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
620
|
+
label: 'Active VU Threads',
|
|
621
|
+
data: ${JSON.stringify(activeThreadsData)},
|
|
622
|
+
type: 'line',
|
|
623
|
+
borderColor: '#38bdf8',
|
|
624
|
+
borderWidth: 2,
|
|
625
|
+
pointRadius: 3,
|
|
626
|
+
fill: false,
|
|
627
|
+
yAxisID: 'yThreads'
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
label: 'Time-to-Failure (TTF) Trend',
|
|
631
|
+
data: ${JSON.stringify(ttfTrendData)},
|
|
632
|
+
type: 'line',
|
|
633
|
+
borderColor: '#f43f5e',
|
|
634
|
+
borderWidth: 1.5,
|
|
635
|
+
borderDash: [4, 4],
|
|
636
|
+
pointRadius: 0,
|
|
637
|
+
fill: false,
|
|
638
|
+
yAxisID: 'yTtf'
|
|
538
639
|
}
|
|
539
640
|
]
|
|
540
641
|
},
|
|
541
642
|
options: {
|
|
542
643
|
responsive: true,
|
|
644
|
+
maintainAspectRatio: false,
|
|
645
|
+
plugins: {
|
|
646
|
+
legend: {
|
|
647
|
+
position: 'top',
|
|
648
|
+
labels: { color: '#94a3b8', boxWidth: 12, font: { size: 11 } }
|
|
649
|
+
}
|
|
650
|
+
},
|
|
543
651
|
scales: {
|
|
544
|
-
|
|
545
|
-
|
|
652
|
+
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
653
|
+
y: { stacked: true, grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Requests Volume', color: '#64748b' } },
|
|
654
|
+
yThreads: { position: 'right', display: true, grid: { drawOnChartArea: false }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'Active Threads', color: '#38bdf8' } },
|
|
655
|
+
yTtf: { position: 'right', display: false, grid: { drawOnChartArea: false } }
|
|
546
656
|
}
|
|
547
657
|
}
|
|
548
658
|
});
|
|
@@ -556,18 +666,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
|
556
666
|
}
|
|
557
667
|
function printUsage() {
|
|
558
668
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
559
|
-
Usage:
|
|
560
|
-
blaze <target-script.ts> [options]
|
|
561
|
-
|
|
562
669
|
Options:
|
|
563
|
-
--threads <count>
|
|
564
|
-
--duration <seconds> Test time frame duration (Default: 10s)
|
|
565
|
-
--agentic Engages Intelligent Autonomous Adaptive Loop Testing
|
|
566
|
-
--cluster-logs Enables Semantic Log Clustering and Analysis
|
|
567
|
-
--target-apdex <score> Target threshold cutoff limit for Apdex (Default: 0.85)
|
|
568
|
-
--target-error <rate> Target cutoff threshold percentage for failures (Default: 0.01)
|
|
569
|
-
--max-threads <count> Safety bounding limit cap for Agent ramp-up (Default: 300)
|
|
570
|
-
--output <filename> Custom HTML dashboard file output path (Default: blaze-dashboard.html)
|
|
571
|
-
`);
|
|
670
|
+
--threads <count> | --duration <seconds> | --agentic | --cluster-logs`);
|
|
572
671
|
}
|
|
573
672
|
main().catch(console.error);
|
|
Binary file
|