blaze-performance-tester 3.0.5 → 3.0.18
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 +183 -53
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,63 @@ import { createRequire } from "module";
|
|
|
5
5
|
import * as path from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import * as fs from "fs";
|
|
8
|
+
|
|
9
|
+
// src/LogAnalyzer.ts
|
|
10
|
+
var LogAnalyzer = class {
|
|
11
|
+
/**
|
|
12
|
+
* Generates a structural semantic fingerprint by masking dynamic values
|
|
13
|
+
*/
|
|
14
|
+
extractLogTemplate(rawLog) {
|
|
15
|
+
return rawLog.trim().replace(/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}(\.\d{3})?Z?/, "<TIMESTAMP>").replace(/0x[a-fA-F0-9]+/g, "<HEX_ID>").replace(/\b\d+\b/g, "<NUM>").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<UUID>");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Categorizes the structural log signature template based on runtime taxonomy rules
|
|
19
|
+
*/
|
|
20
|
+
classifyTemplate(template) {
|
|
21
|
+
const LowercaseTpl = template.toLowerCase();
|
|
22
|
+
if (LowercaseTpl.includes("timeout") || LowercaseTpl.includes("econnrefused") || LowercaseTpl.includes("socket") || LowercaseTpl.includes("network") || LowercaseTpl.includes("hang up") || LowercaseTpl.includes("502") || LowercaseTpl.includes("504")) {
|
|
23
|
+
return { category: "Environment_Infrastructure_Error", severity: "CRITICAL" };
|
|
24
|
+
}
|
|
25
|
+
if (LowercaseTpl.includes("401") || LowercaseTpl.includes("403") || LowercaseTpl.includes("unauthorized") || LowercaseTpl.includes("forbidden") || LowercaseTpl.includes("jwt") || LowercaseTpl.includes("token expired")) {
|
|
26
|
+
return { category: "Authentication_Error", severity: "CRITICAL" };
|
|
27
|
+
}
|
|
28
|
+
return { category: "Product_Error", severity: "WARNING" };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Processes a stream of raw textual error logs into deduplicated clustered fingerprints
|
|
32
|
+
*/
|
|
33
|
+
process(rawLogs) {
|
|
34
|
+
const clusterMap = {};
|
|
35
|
+
let totalCapturedErrors = 0;
|
|
36
|
+
for (const log of rawLogs) {
|
|
37
|
+
if (!log || log.trim() === "") continue;
|
|
38
|
+
totalCapturedErrors++;
|
|
39
|
+
const template = this.extractLogTemplate(log);
|
|
40
|
+
if (!clusterMap[template]) {
|
|
41
|
+
const rules = this.classifyTemplate(template);
|
|
42
|
+
clusterMap[template] = {
|
|
43
|
+
template,
|
|
44
|
+
count: 0,
|
|
45
|
+
category: rules.category,
|
|
46
|
+
severity: rules.severity,
|
|
47
|
+
examples: []
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
clusterMap[template].count++;
|
|
51
|
+
if (clusterMap[template].examples.length < 3 && !clusterMap[template].examples.includes(log)) {
|
|
52
|
+
clusterMap[template].examples.push(log);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const sortedClusters = Object.values(clusterMap).sort((a, b) => b.count - a.count);
|
|
56
|
+
return {
|
|
57
|
+
uniqueClustersCount: sortedClusters.length,
|
|
58
|
+
totalCapturedErrors,
|
|
59
|
+
clusters: sortedClusters
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// cli.ts
|
|
8
65
|
var __filename = fileURLToPath(import.meta.url);
|
|
9
66
|
var __dirname = path.dirname(__filename);
|
|
10
67
|
var require2 = createRequire(import.meta.url);
|
|
@@ -44,13 +101,15 @@ async function main() {
|
|
|
44
101
|
}
|
|
45
102
|
}
|
|
46
103
|
function parseArguments(args) {
|
|
47
|
-
const targetScript = path.resolve(args[0]);
|
|
104
|
+
const targetScript = path.resolve(args[0] || ".");
|
|
48
105
|
const isAgentic = args.includes("--agentic");
|
|
106
|
+
const clusterLogs = args.includes("--cluster-logs");
|
|
49
107
|
let threads = 10;
|
|
50
108
|
let durationSec = 10;
|
|
51
109
|
let targetApdex = 0.85;
|
|
52
110
|
let targetErrorRate = 0.01;
|
|
53
111
|
let maxThreads = 300;
|
|
112
|
+
let maxAllowedLatencyMs = 800;
|
|
54
113
|
let outputHtml = "blaze-dashboard.html";
|
|
55
114
|
const threadsIdx = args.indexOf("--threads");
|
|
56
115
|
if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
|
|
@@ -64,16 +123,31 @@ function parseArguments(args) {
|
|
|
64
123
|
if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
|
|
65
124
|
const outputIdx = args.indexOf("--output");
|
|
66
125
|
if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
|
|
126
|
+
if (isAgentic) {
|
|
127
|
+
const agenticIdx = args.indexOf("--agentic");
|
|
128
|
+
const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
|
|
129
|
+
if (trailingArgs[0]) maxThreads = parseInt(trailingArgs[0], 10);
|
|
130
|
+
if (trailingArgs[1]) {
|
|
131
|
+
let rawLatency = parseFloat(trailingArgs[1]);
|
|
132
|
+
if (rawLatency < 50) {
|
|
133
|
+
maxAllowedLatencyMs = rawLatency * 1e3;
|
|
134
|
+
} else {
|
|
135
|
+
maxAllowedLatencyMs = rawLatency;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (trailingArgs[2]) targetErrorRate = parseFloat(trailingArgs[2]) / 100;
|
|
139
|
+
}
|
|
67
140
|
return {
|
|
68
141
|
targetScript,
|
|
69
142
|
isAgentic,
|
|
143
|
+
clusterLogs,
|
|
70
144
|
threads,
|
|
71
145
|
durationSec,
|
|
72
146
|
targetApdex,
|
|
73
147
|
targetErrorRate,
|
|
74
148
|
maxThreads,
|
|
75
149
|
stepSize: 15,
|
|
76
|
-
maxAllowedLatencyMs
|
|
150
|
+
maxAllowedLatencyMs,
|
|
77
151
|
outputHtml
|
|
78
152
|
};
|
|
79
153
|
}
|
|
@@ -88,24 +162,29 @@ async function runBlazeCoreEngine(config) {
|
|
|
88
162
|
} catch (err) {
|
|
89
163
|
}
|
|
90
164
|
if (!rawRequests || rawRequests.length === 0) {
|
|
91
|
-
rawRequests = generateSimulationData(config.threads, config.durationSec);
|
|
165
|
+
rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
|
|
92
166
|
}
|
|
93
|
-
|
|
167
|
+
const metrics = processMetricsTelemetry(rawRequests, config.durationSec);
|
|
168
|
+
const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
|
|
169
|
+
resolve2({ metrics, rawErrors });
|
|
94
170
|
}, 1e3);
|
|
95
171
|
});
|
|
96
172
|
}
|
|
97
173
|
async function runIntelligentAgenticStressTest(config) {
|
|
98
174
|
console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
|
|
99
|
-
console.log(`\u{1F3AF} Target Constraints: Apdex >= ${config.targetApdex} | Error Rate <= ${config.targetErrorRate * 100}
|
|
175
|
+
console.log(`\u{1F3AF} Target Constraints: Apdex >= ${config.targetApdex} | Error Rate <= ${(config.targetErrorRate * 100).toFixed(1)}% | SLA Target <= ${config.maxAllowedLatencyMs}ms`);
|
|
100
176
|
const history = [];
|
|
101
|
-
let
|
|
177
|
+
let aggregateErrorLogs = [];
|
|
178
|
+
let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
|
|
102
179
|
let baseStep = config.stepSize;
|
|
103
180
|
let isStable = true;
|
|
104
181
|
let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
|
|
105
182
|
while (currentThreads <= config.maxThreads && isStable) {
|
|
106
|
-
console.log(
|
|
107
|
-
|
|
108
|
-
|
|
183
|
+
console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
|
|
184
|
+
const { metrics, rawErrors } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
|
|
185
|
+
if (config.clusterLogs) {
|
|
186
|
+
aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
|
|
187
|
+
}
|
|
109
188
|
const currentState = {
|
|
110
189
|
threads: currentThreads,
|
|
111
190
|
avgLatency: metrics.avgLatencyMs,
|
|
@@ -124,7 +203,6 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
124
203
|
if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
|
|
125
204
|
verdictReason = "The primary breakdown point was identified due to infrastructure saturation where throughput flattened while latency spiked rapidly.";
|
|
126
205
|
console.log(`\u26A0\uFE0F [Agent Alert]: ${verdictReason} Backing off.`);
|
|
127
|
-
currentThreads = Math.floor(currentThreads * 0.8);
|
|
128
206
|
isStable = false;
|
|
129
207
|
break;
|
|
130
208
|
}
|
|
@@ -134,9 +212,9 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
134
212
|
baseStep = Math.max(2, Math.floor(baseStep * 0.35));
|
|
135
213
|
}
|
|
136
214
|
}
|
|
137
|
-
if (currentState.apdex < config.targetApdex) {
|
|
215
|
+
if (currentState.p95Latency > config.maxAllowedLatencyMs || currentState.apdex < config.targetApdex) {
|
|
138
216
|
verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
|
|
139
|
-
console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Apdex: ${currentState.apdex.toFixed(2)}`);
|
|
217
|
+
console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. P95 Latency: ${currentState.p95Latency.toFixed(1)}ms | Apdex: ${currentState.apdex.toFixed(2)}`);
|
|
140
218
|
isStable = false;
|
|
141
219
|
break;
|
|
142
220
|
}
|
|
@@ -146,18 +224,32 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
146
224
|
isStable = false;
|
|
147
225
|
break;
|
|
148
226
|
}
|
|
227
|
+
if (currentThreads === config.maxThreads) {
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
149
230
|
const apdexMargin = (currentState.apdex - config.targetApdex) / (1 - config.targetApdex);
|
|
150
|
-
const adaptiveMultiplier = Math.max(0.15, Math.min(2, apdexMargin));
|
|
151
|
-
|
|
231
|
+
const adaptiveMultiplier = Math.max(0.15, Math.min(2, isNaN(apdexMargin) ? 1 : apdexMargin));
|
|
232
|
+
let nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
|
|
233
|
+
if (currentThreads + nextStep > config.maxThreads) {
|
|
234
|
+
nextStep = config.maxThreads - currentThreads;
|
|
235
|
+
}
|
|
152
236
|
console.log(`\u{1F4C8} [Step Adjustment]: Scaling factor ${adaptiveMultiplier.toFixed(2)}x. Next jump: +${nextStep} threads.`);
|
|
153
237
|
currentThreads += nextStep;
|
|
154
238
|
}
|
|
239
|
+
let semanticReport = null;
|
|
240
|
+
if (config.clusterLogs && aggregateErrorLogs.length > 0) {
|
|
241
|
+
console.log(`
|
|
242
|
+
\u{1F9E0} [Semantic Clustering]: Executing classification analyzer across ${aggregateErrorLogs.length} error entries...`);
|
|
243
|
+
const analyzer = new LogAnalyzer();
|
|
244
|
+
semanticReport = analyzer.process(aggregateErrorLogs);
|
|
245
|
+
console.log(`\u2705 Analysis Complete. Identified ${semanticReport.uniqueClustersCount} distinct signature blueprints.`);
|
|
246
|
+
}
|
|
155
247
|
generateFinalAgentReport(history);
|
|
156
|
-
exportHtmlDashboard(history, config, verdictReason);
|
|
248
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport);
|
|
157
249
|
}
|
|
158
250
|
async function runStandardStressTest(config) {
|
|
159
251
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
160
|
-
const metrics = await runBlazeCoreEngine(config);
|
|
252
|
+
const { metrics, rawErrors } = await runBlazeCoreEngine(config);
|
|
161
253
|
printMatrixDashboard(metrics, config.threads);
|
|
162
254
|
const history = [{
|
|
163
255
|
threads: config.threads,
|
|
@@ -167,25 +259,44 @@ async function runStandardStressTest(config) {
|
|
|
167
259
|
apdex: metrics.apdexScore,
|
|
168
260
|
throughput: metrics.requestsPerSecond
|
|
169
261
|
}];
|
|
262
|
+
let semanticReport = null;
|
|
263
|
+
if (config.clusterLogs && rawErrors.length > 0) {
|
|
264
|
+
const analyzer = new LogAnalyzer();
|
|
265
|
+
semanticReport = analyzer.process(rawErrors);
|
|
266
|
+
}
|
|
170
267
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
171
268
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
172
|
-
exportHtmlDashboard(history, config, verdictReason);
|
|
269
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport);
|
|
173
270
|
}
|
|
174
|
-
function generateSimulationData(threads, durationSec) {
|
|
271
|
+
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
175
272
|
const data = [];
|
|
176
|
-
const totalRequests = threads * durationSec *
|
|
273
|
+
const totalRequests = Math.max(15, threads * durationSec * 30);
|
|
177
274
|
const startTime = Date.now();
|
|
178
|
-
const
|
|
275
|
+
const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
|
|
276
|
+
const isBreached = threads > targetThresholdBreak;
|
|
277
|
+
const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
|
|
278
|
+
const errorPool = [
|
|
279
|
+
`Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16)`,
|
|
280
|
+
`HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms`,
|
|
281
|
+
`TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (${path.sep}app${path.sep}dist${path.sep}handler.js:42:19)`
|
|
282
|
+
];
|
|
179
283
|
for (let i = 0; i < totalRequests; i++) {
|
|
180
|
-
const isError = Math.random() < (
|
|
284
|
+
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
181
285
|
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
286
|
+
let errorMessage;
|
|
287
|
+
let statusCode = 200;
|
|
288
|
+
if (isError) {
|
|
289
|
+
statusCode = 500;
|
|
290
|
+
errorMessage = `[${(/* @__PURE__ */ new Date()).toISOString()}] ` + errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
291
|
+
}
|
|
182
292
|
data.push({
|
|
183
293
|
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
184
294
|
timestampMs: startTime + Math.random() * (durationSec * 1e3),
|
|
185
295
|
dnsTimeMs: 1 + Math.random() * 4,
|
|
186
296
|
tcpTimeMs: 5 + Math.random() * 10,
|
|
187
297
|
tlsTimeMs: 10 + Math.random() * 12,
|
|
188
|
-
statusCode
|
|
298
|
+
statusCode,
|
|
299
|
+
errorMessage
|
|
189
300
|
});
|
|
190
301
|
}
|
|
191
302
|
return data.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
@@ -197,7 +308,7 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
197
308
|
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
198
309
|
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
199
310
|
const errorCount = requests.filter((r) => r.statusCode >= 400).length;
|
|
200
|
-
const errorRate = errorCount / totalRequests;
|
|
311
|
+
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
201
312
|
const T = 100;
|
|
202
313
|
const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
|
|
203
314
|
const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
|
|
@@ -217,9 +328,7 @@ function printMatrixDashboard(m, threads) {
|
|
|
217
328
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
218
329
|
console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
|
|
219
330
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
220
|
-
console.log(
|
|
221
|
-
`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`
|
|
222
|
-
);
|
|
331
|
+
console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
|
|
223
332
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
224
333
|
}
|
|
225
334
|
function generateFinalAgentReport(history) {
|
|
@@ -235,7 +344,7 @@ function generateFinalAgentReport(history) {
|
|
|
235
344
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
236
345
|
console.log("=======================================================\n");
|
|
237
346
|
}
|
|
238
|
-
function exportHtmlDashboard(history, config, verdictReason) {
|
|
347
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport) {
|
|
239
348
|
const labels = history.map((h) => `${h.threads} Threads`);
|
|
240
349
|
const throughputData = history.map((h) => h.throughput.toFixed(0));
|
|
241
350
|
const latencyData = history.map((h) => h.avgLatency.toFixed(1));
|
|
@@ -243,9 +352,45 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
243
352
|
const waveListItems = history.map((h) => {
|
|
244
353
|
return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
|
|
245
354
|
}).join("");
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
355
|
+
let logClustersHtml = "";
|
|
356
|
+
if (semanticReport) {
|
|
357
|
+
logClustersHtml = `
|
|
358
|
+
<div class="card" style="margin-top: 2rem;">
|
|
359
|
+
<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
|
+
<table>
|
|
362
|
+
<thead>
|
|
363
|
+
<tr>
|
|
364
|
+
<th style="width: 10%;">Count</th>
|
|
365
|
+
<th style="width: 25%;">Classification Category</th>
|
|
366
|
+
<th style="width: 10%;">Severity</th>
|
|
367
|
+
<th style="width: 55%;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
|
|
368
|
+
</tr>
|
|
369
|
+
</thead>
|
|
370
|
+
<tbody>
|
|
371
|
+
${semanticReport.clusters.map((c) => {
|
|
372
|
+
let catColor = "#f87171";
|
|
373
|
+
if (c.category === "Environment_Infrastructure_Error") catColor = "#fb923c";
|
|
374
|
+
if (c.category === "Authentication_Error") catColor = "#c084fc";
|
|
375
|
+
let sevColor = c.severity === "CRITICAL" ? "#dc2626" : "#eab308";
|
|
376
|
+
return `
|
|
377
|
+
<tr>
|
|
378
|
+
<td><span style="font-size: 1.1rem; font-weight: bold; color: #f8fafc;">${c.count}</span></td>
|
|
379
|
+
<td><span style="color: ${catColor}; font-weight: 600;">${c.category}</span></td>
|
|
380
|
+
<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>
|
|
387
|
+
</tr>`;
|
|
388
|
+
}).join("")}
|
|
389
|
+
</tbody>
|
|
390
|
+
</table>
|
|
391
|
+
</div>`;
|
|
392
|
+
}
|
|
393
|
+
const htmlContent = `<!DOCTYPE html><html lang="en"><head>
|
|
249
394
|
<meta charset="UTF-8">
|
|
250
395
|
<title>Blaze Core Performance Report</title>
|
|
251
396
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
@@ -255,8 +400,6 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
255
400
|
.header { border-bottom: 1px solid #334155; padding-bottom: 1rem; margin-bottom: 1.5rem; }
|
|
256
401
|
h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
|
|
257
402
|
.meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
|
|
258
|
-
|
|
259
|
-
/* \u{1F916} AI VERDICT COMPONENT STYLING */
|
|
260
403
|
.verdict-box {
|
|
261
404
|
background: #111827;
|
|
262
405
|
border: 1px dashed #ea580c;
|
|
@@ -291,27 +434,22 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
291
434
|
.wave-item {
|
|
292
435
|
margin: 0.25rem 0;
|
|
293
436
|
}
|
|
294
|
-
|
|
295
437
|
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 2rem; margin-bottom: 2rem; }
|
|
296
438
|
.card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 1.5rem; }
|
|
297
439
|
h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #334155; padding-bottom: 0.5rem; }
|
|
298
440
|
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
|
|
299
|
-
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #334155; }
|
|
441
|
+
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #334155; vertical-align: top; }
|
|
300
442
|
th { color: #94a3b8; font-weight: 600; }
|
|
301
443
|
tr:hover { background: #273549; }
|
|
302
444
|
.badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
|
|
303
445
|
.badge-success { background: #16a34a; color: #f0fdf4; }
|
|
304
446
|
.badge-fail { background: #dc2626; color: #fef2f2; }
|
|
305
|
-
</style>
|
|
306
|
-
</head>
|
|
307
|
-
<body>
|
|
447
|
+
</style></head><body>
|
|
308
448
|
<div class="container">
|
|
309
449
|
<div class="header">
|
|
310
450
|
<h1>\u{1F525} Blaze Core Performance Analysis</h1>
|
|
311
451
|
<div class="meta">Target Script: <code>${config.targetScript}</code> | Mode: ${config.isAgentic ? "\u{1F9E0} Intelligent Agentic" : "\u{1F3CB}\uFE0F Fixed Workload"}</div>
|
|
312
452
|
</div>
|
|
313
|
-
|
|
314
|
-
<!-- \u{1F916} AUTOMATED VERDICT CONTAINER -->
|
|
315
453
|
<div class="verdict-box">
|
|
316
454
|
<div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
|
|
317
455
|
<div class="verdict-text">
|
|
@@ -322,12 +460,10 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
322
460
|
${waveListItems}
|
|
323
461
|
</div>
|
|
324
462
|
</div>
|
|
325
|
-
|
|
326
463
|
<div class="grid">
|
|
327
464
|
<div class="card"><canvas id="throughputChart"></canvas></div>
|
|
328
465
|
<div class="card"><canvas id="latencyChart"></canvas></div>
|
|
329
466
|
</div>
|
|
330
|
-
|
|
331
467
|
<div class="card">
|
|
332
468
|
<h3>Telemetry Matrix Logs</h3>
|
|
333
469
|
<table>
|
|
@@ -345,8 +481,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
345
481
|
<tbody>
|
|
346
482
|
${history.map((h) => {
|
|
347
483
|
const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
|
|
348
|
-
return `
|
|
349
|
-
<tr>
|
|
484
|
+
return ` <tr>
|
|
350
485
|
<td><strong>${h.threads}</strong></td>
|
|
351
486
|
<td>${h.throughput.toFixed(0)} req/sec</td>
|
|
352
487
|
<td>${h.avgLatency.toFixed(1)} ms</td>
|
|
@@ -363,12 +498,11 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
363
498
|
</tbody>
|
|
364
499
|
</table>
|
|
365
500
|
</div>
|
|
501
|
+
${logClustersHtml}
|
|
366
502
|
</div>
|
|
367
|
-
|
|
368
503
|
<script>
|
|
369
504
|
const labels = ${JSON.stringify(labels)};
|
|
370
|
-
|
|
371
|
-
new Chart(document.getElementById('throughputChart'), {
|
|
505
|
+
new Chart(document.getElementById('throughputChart'), {
|
|
372
506
|
type: 'line',
|
|
373
507
|
data: {
|
|
374
508
|
labels: labels,
|
|
@@ -383,7 +517,6 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
383
517
|
},
|
|
384
518
|
options: { responsive: true, plugins: { legend: { labels: { color: '#f8fafc' } } } }
|
|
385
519
|
});
|
|
386
|
-
|
|
387
520
|
new Chart(document.getElementById('latencyChart'), {
|
|
388
521
|
type: 'line',
|
|
389
522
|
data: {
|
|
@@ -413,9 +546,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
413
546
|
}
|
|
414
547
|
}
|
|
415
548
|
});
|
|
416
|
-
</script
|
|
417
|
-
</body>
|
|
418
|
-
</html>`;
|
|
549
|
+
</script></body></html>`;
|
|
419
550
|
try {
|
|
420
551
|
fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
|
|
421
552
|
console.log(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
|
|
@@ -424,9 +555,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
|
|
|
424
555
|
}
|
|
425
556
|
}
|
|
426
557
|
function printUsage() {
|
|
427
|
-
console.log(`
|
|
428
|
-
Blaze Core Performance Test CLI Engine
|
|
429
|
-
|
|
558
|
+
console.log(`Blaze Core Performance Test CLI Engine
|
|
430
559
|
Usage:
|
|
431
560
|
blaze <target-script.ts> [options]
|
|
432
561
|
|
|
@@ -434,6 +563,7 @@ Options:
|
|
|
434
563
|
--threads <count> Number of working execution thread pools (Default: 10)
|
|
435
564
|
--duration <seconds> Test time frame duration (Default: 10s)
|
|
436
565
|
--agentic Engages Intelligent Autonomous Adaptive Loop Testing
|
|
566
|
+
--cluster-logs Enables Semantic Log Clustering and Analysis
|
|
437
567
|
--target-apdex <score> Target threshold cutoff limit for Apdex (Default: 0.85)
|
|
438
568
|
--target-error <rate> Target cutoff threshold percentage for failures (Default: 0.01)
|
|
439
569
|
--max-threads <count> Safety bounding limit cap for Agent ramp-up (Default: 300)
|
|
Binary file
|