blaze-performance-tester 3.1.27 → 3.1.29
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 +136 -12
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -87,6 +87,45 @@ var mathUtils = {
|
|
|
87
87
|
return sorted[Math.max(0, index)];
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
+
async function fetchRcaInsights(semanticReport, apiKey) {
|
|
91
|
+
if (!semanticReport || !semanticReport.clusters || semanticReport.clusters.length === 0) {
|
|
92
|
+
return `<ul><li><strong>Nominal Operational Bounds:</strong> No cluster-level anomalies or processing faults were registered during this execution frame.</li></ul>`;
|
|
93
|
+
}
|
|
94
|
+
const errorSummary = semanticReport.clusters.map((c) => `[Count: ${c.count}] Category: ${c.category} -> Pattern: ${c.template}`).join("\n");
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
"Authorization": `Bearer ${apiKey}`
|
|
101
|
+
},
|
|
102
|
+
body: JSON.stringify({
|
|
103
|
+
model: "gpt-4o-mini",
|
|
104
|
+
messages: [
|
|
105
|
+
{
|
|
106
|
+
role: "system",
|
|
107
|
+
content: "You are a principal systems architect and site reliability engineer. Return clean, direct HTML list items (\u7528 <li> tags, no raw markdown blocks) summarizing diagnostics."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
role: "user",
|
|
111
|
+
content: `Analyze these performance test errors and provide 3 bullet points detailing the likely backend bottleneck (e.g., database connection pool exhaustion) and practical configuration fixes.
|
|
112
|
+
|
|
113
|
+
Error Log Patterns:
|
|
114
|
+
${errorSummary}`
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
temperature: 0.2
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
return `<span style="color: #ef4444;">AI Completion API Error: ${response.status} ${response.statusText}</span>`;
|
|
122
|
+
}
|
|
123
|
+
const data = await response.json();
|
|
124
|
+
return data.choices?.[0]?.message?.content || "<li>Failed to aggregate autonomous analysis parameters.</li>";
|
|
125
|
+
} catch (error) {
|
|
126
|
+
return `<span style="color: #ef4444;">Failed to resolve external cloud model: ${error.message}</span>`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
90
129
|
async function executeTestPipeline(config) {
|
|
91
130
|
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
92
131
|
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
@@ -148,6 +187,7 @@ function parseArguments(args) {
|
|
|
148
187
|
let maxThreads = 300;
|
|
149
188
|
let maxAllowedLatencyMs = 800;
|
|
150
189
|
let outputHtml = "blaze-dashboard.html";
|
|
190
|
+
let aiKey = "";
|
|
151
191
|
const threadsIdx = args.indexOf("--threads");
|
|
152
192
|
if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
|
|
153
193
|
const durationIdx = args.indexOf("--duration");
|
|
@@ -160,6 +200,8 @@ function parseArguments(args) {
|
|
|
160
200
|
if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
|
|
161
201
|
const outputIdx = args.indexOf("--output");
|
|
162
202
|
if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
|
|
203
|
+
const aiKeyIdx = args.indexOf("--ai-key");
|
|
204
|
+
if (aiKeyIdx !== -1) aiKey = args[aiKeyIdx + 1];
|
|
163
205
|
const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
|
|
164
206
|
if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
|
|
165
207
|
if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
|
|
@@ -191,7 +233,8 @@ function parseArguments(args) {
|
|
|
191
233
|
maxThreads,
|
|
192
234
|
stepSize: 15,
|
|
193
235
|
maxAllowedLatencyMs,
|
|
194
|
-
outputHtml
|
|
236
|
+
outputHtml,
|
|
237
|
+
aiKey
|
|
195
238
|
};
|
|
196
239
|
}
|
|
197
240
|
async function runBlazeCoreEngine(config) {
|
|
@@ -327,12 +370,17 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
327
370
|
currentThreads += nextStep;
|
|
328
371
|
}
|
|
329
372
|
let semanticReport = null;
|
|
373
|
+
let rcaInsights = "";
|
|
330
374
|
if (config.clusterLogs) {
|
|
331
375
|
if (aggregateErrorLogs.length === 0) {
|
|
332
376
|
aggregateErrorLogs = getFallbackClusterLogs();
|
|
333
377
|
}
|
|
334
378
|
const analyzer = new LogAnalyzer();
|
|
335
379
|
semanticReport = analyzer.process(aggregateErrorLogs);
|
|
380
|
+
if (config.aiKey) {
|
|
381
|
+
console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
|
|
382
|
+
rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
|
|
383
|
+
}
|
|
336
384
|
}
|
|
337
385
|
const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
|
|
338
386
|
const healthScore = computeHealthScoreValue(finalState.apdex, finalState.errorRate, finalState.p95Latency, config.maxAllowedLatencyMs);
|
|
@@ -358,7 +406,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
358
406
|
Object.keys(globalCodeCounts).forEach((code) => {
|
|
359
407
|
breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
|
|
360
408
|
});
|
|
361
|
-
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests);
|
|
409
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, rcaInsights);
|
|
362
410
|
}
|
|
363
411
|
async function runStandardStressTest(config) {
|
|
364
412
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
@@ -377,11 +425,16 @@ async function runStandardStressTest(config) {
|
|
|
377
425
|
failedRequests: metrics.c4xx + metrics.c5xx
|
|
378
426
|
}];
|
|
379
427
|
let semanticReport = null;
|
|
428
|
+
let rcaInsights = "";
|
|
380
429
|
if (config.clusterLogs) {
|
|
381
430
|
let errsToProcess = rawErrors;
|
|
382
431
|
if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
|
|
383
432
|
const analyzer = new LogAnalyzer();
|
|
384
433
|
semanticReport = analyzer.process(errsToProcess);
|
|
434
|
+
if (config.aiKey) {
|
|
435
|
+
console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
|
|
436
|
+
rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
|
|
437
|
+
}
|
|
385
438
|
}
|
|
386
439
|
const finalSummaryCards = {
|
|
387
440
|
apdex: metrics.apdexScore,
|
|
@@ -412,7 +465,7 @@ async function runStandardStressTest(config) {
|
|
|
412
465
|
total4xx: metrics.c4xx,
|
|
413
466
|
total5xx: metrics.c5xx,
|
|
414
467
|
breakdownRates
|
|
415
|
-
}, finalSummaryCards, rawRequests);
|
|
468
|
+
}, finalSummaryCards, rawRequests, rcaInsights);
|
|
416
469
|
}
|
|
417
470
|
function getFallbackClusterLogs() {
|
|
418
471
|
const logs = [];
|
|
@@ -467,7 +520,7 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
467
520
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
468
521
|
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
469
522
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
470
|
-
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500:
|
|
523
|
+
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
471
524
|
requests.forEach((r) => {
|
|
472
525
|
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
473
526
|
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
@@ -528,7 +581,7 @@ function generateFinalAgentReport(history, score) {
|
|
|
528
581
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
529
582
|
console.log("=======================================================\n");
|
|
530
583
|
}
|
|
531
|
-
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = []) {
|
|
584
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "") {
|
|
532
585
|
const labels = history.map((h, i) => `${i + 1}s`);
|
|
533
586
|
const successRequestsData = history.map((h) => h.successRequests);
|
|
534
587
|
const failedWorkloadsData = history.map((h) => h.failedRequests);
|
|
@@ -545,6 +598,26 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
545
598
|
x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
|
|
546
599
|
y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
|
|
547
600
|
}));
|
|
601
|
+
let rcaAccordionHtml = "";
|
|
602
|
+
if (config.clusterLogs) {
|
|
603
|
+
const content = rcaInsights ? rcaInsights : `
|
|
604
|
+
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px dashed #475569; color: #94a3b8;">
|
|
605
|
+
\u{1F4A1} <strong>Autonomous RCA Available:</strong> Pass the <code>--ai-key <your-key></code> flag during pipeline execution to automatically turn structural failure logs into actionable configuration mitigations here.
|
|
606
|
+
</div>`;
|
|
607
|
+
rcaAccordionHtml = `
|
|
608
|
+
<details class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #8b5cf6; padding: 0; overflow: hidden;" open>
|
|
609
|
+
<summary style="font-size: 1.2rem; font-weight: bold; color: #c084fc; padding: 1.25rem; display: flex; align-items: center; justify-content: space-between; cursor: pointer; outline: none; user-select: none; list-style: none;">
|
|
610
|
+
<span style="display: flex; align-items: center; gap: 0.6rem;">\u{1F9E0} Autonomous Root-Cause Analysis (RCA) Insights</span>
|
|
611
|
+
<span style="font-size: 0.8rem; color: #64748b; font-weight: normal; font-family: monospace;">[CLICK TO COLLAPSE/EXPAND]</span>
|
|
612
|
+
</summary>
|
|
613
|
+
<div style="padding: 0 1.25rem 1.25rem 1.25rem; border-top: 1px solid #1e293b; color: #cbd5e1; font-size: 0.95rem; line-height: 1.6;">
|
|
614
|
+
<div style="margin-top: 1rem; margin-bottom: 0.5rem; color: #a78bfa; font-weight: 600;">System Diagnostic Summary:</div>
|
|
615
|
+
<div style="padding-left: 0.5rem;">
|
|
616
|
+
${content}
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</details>`;
|
|
620
|
+
}
|
|
548
621
|
let logClustersHtml = "";
|
|
549
622
|
if (semanticReport) {
|
|
550
623
|
const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
|
|
@@ -552,7 +625,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
552
625
|
logClustersHtml = `
|
|
553
626
|
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
554
627
|
<h3 style="border-bottom: none; padding-bottom: 0rem; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; color: #ffffff; font-size: 1.2rem;">
|
|
555
|
-
\u{
|
|
628
|
+
\u{1F4CA} Semantic Log Clustering & Error Classification
|
|
556
629
|
</h3>
|
|
557
630
|
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
558
631
|
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
|
|
@@ -741,6 +814,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
741
814
|
.badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
|
|
742
815
|
.badge-success { background: #16a34a; color: #f0fdf4; }
|
|
743
816
|
.badge-fail { background: #dc2626; color: #fef2f2; }
|
|
817
|
+
|
|
818
|
+
summary::-webkit-details-marker { display: none; }
|
|
744
819
|
</style></head><body>
|
|
745
820
|
<div class="container">
|
|
746
821
|
<div class="header">
|
|
@@ -790,7 +865,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
790
865
|
<div class="verdict-waves">${waveListItems}</div>
|
|
791
866
|
</div>
|
|
792
867
|
|
|
793
|
-
<!-- IMPLEMENTED CARD FEATURE 76: Executive Health Gauges Panel -->
|
|
794
868
|
<div class="gauge-container-box">
|
|
795
869
|
<div class="gauge-title">Blaze Run Health Score</div>
|
|
796
870
|
<div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
|
|
@@ -851,7 +925,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
851
925
|
</div>
|
|
852
926
|
|
|
853
927
|
<div class="graph-card">
|
|
854
|
-
<div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend</div>
|
|
928
|
+
<div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast</div>
|
|
855
929
|
<div style="height: 280px; position: relative;">
|
|
856
930
|
<canvas id="concurrencyChart"></canvas>
|
|
857
931
|
</div>
|
|
@@ -903,6 +977,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
903
977
|
${seoImpactSectionHtml}
|
|
904
978
|
${liveAdjusterHtml}
|
|
905
979
|
${rightSizingHtml}
|
|
980
|
+
${rcaAccordionHtml}
|
|
906
981
|
${logClustersHtml}
|
|
907
982
|
</div>
|
|
908
983
|
|
|
@@ -960,14 +1035,52 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
960
1035
|
}
|
|
961
1036
|
});
|
|
962
1037
|
|
|
1038
|
+
const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
|
|
1039
|
+
const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
|
|
1040
|
+
const historicalCount = baseTtfTrend.length;
|
|
1041
|
+
|
|
1042
|
+
let extendedLabels = [...labels];
|
|
1043
|
+
let extendedThreads = [...baseActiveThreads];
|
|
1044
|
+
let extendedTtf = [...baseTtfTrend];
|
|
1045
|
+
let aiForecastData = [];
|
|
1046
|
+
|
|
1047
|
+
if (historicalCount >= 2) {
|
|
1048
|
+
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
|
|
1049
|
+
for (let i = 0; i < historicalCount; i++) {
|
|
1050
|
+
sumX += i;
|
|
1051
|
+
sumY += baseTtfTrend[i];
|
|
1052
|
+
sumXY += i * baseTtfTrend[i];
|
|
1053
|
+
sumXX += i * i;
|
|
1054
|
+
}
|
|
1055
|
+
const denominator = (historicalCount * sumXX - sumX * sumX);
|
|
1056
|
+
const slope = denominator === 0 ? 0 : (historicalCount * sumXY - sumX * sumY) / denominator;
|
|
1057
|
+
const intercept = (sumY - slope * sumX) / historicalCount;
|
|
1058
|
+
|
|
1059
|
+
for (let i = 0; i < historicalCount; i++) {
|
|
1060
|
+
aiForecastData.push(Number((slope * i + intercept).toFixed(1)));
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const lastThreads = baseActiveThreads[historicalCount - 1] || 10;
|
|
1064
|
+
const threadStep = ${config.stepSize || 15};
|
|
1065
|
+
|
|
1066
|
+
for (let i = 1; i <= 3; i++) {
|
|
1067
|
+
extendedLabels.push(\`+\${i * 5}s (AI Forecast)\`);
|
|
1068
|
+
extendedThreads.push(lastThreads + (threadStep * i));
|
|
1069
|
+
extendedTtf.push(null);
|
|
1070
|
+
aiForecastData.push(Number((slope * (historicalCount + i - 1) + intercept).toFixed(1)));
|
|
1071
|
+
}
|
|
1072
|
+
} else {
|
|
1073
|
+
aiForecastData = [...baseTtfTrend];
|
|
1074
|
+
}
|
|
1075
|
+
|
|
963
1076
|
new Chart(document.getElementById('concurrencyChart'), {
|
|
964
1077
|
type: 'line',
|
|
965
1078
|
data: {
|
|
966
|
-
labels:
|
|
1079
|
+
labels: extendedLabels,
|
|
967
1080
|
datasets: [
|
|
968
1081
|
{
|
|
969
1082
|
label: 'Active VU Threads',
|
|
970
|
-
data:
|
|
1083
|
+
data: extendedThreads,
|
|
971
1084
|
borderColor: '#38bdf8',
|
|
972
1085
|
backgroundColor: 'rgba(56, 189, 248, 0.1)',
|
|
973
1086
|
borderWidth: 2.5,
|
|
@@ -977,13 +1090,24 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
977
1090
|
},
|
|
978
1091
|
{
|
|
979
1092
|
label: 'Time-to-Failure (TTF) Trend',
|
|
980
|
-
data:
|
|
1093
|
+
data: extendedTtf,
|
|
981
1094
|
borderColor: '#f43f5e',
|
|
982
1095
|
borderWidth: 2,
|
|
983
1096
|
borderDash: [5, 5],
|
|
984
1097
|
pointRadius: 3,
|
|
985
1098
|
fill: false,
|
|
986
1099
|
yAxisID: 'yTtf'
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
label: '\u{1F52E} AI Capacity Forecast Line',
|
|
1103
|
+
data: aiForecastData,
|
|
1104
|
+
borderColor: '#a855f7',
|
|
1105
|
+
borderWidth: 2,
|
|
1106
|
+
borderDash: [3, 3],
|
|
1107
|
+
pointRadius: 4,
|
|
1108
|
+
pointBackgroundColor: '#a855f7',
|
|
1109
|
+
fill: false,
|
|
1110
|
+
yAxisID: 'yTtf'
|
|
987
1111
|
}
|
|
988
1112
|
]
|
|
989
1113
|
},
|
|
@@ -1056,6 +1180,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1056
1180
|
function printUsage() {
|
|
1057
1181
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
1058
1182
|
Options:
|
|
1059
|
-
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --seo
|
|
1183
|
+
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --seo | --ai-key <api-key>`);
|
|
1060
1184
|
}
|
|
1061
1185
|
main().catch(console.error);
|
|
Binary file
|