blaze-performance-tester 3.1.35 → 3.1.37
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 +81 -10
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -87,6 +87,60 @@ var mathUtils = {
|
|
|
87
87
|
return sorted[Math.max(0, index)];
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
+
async function generateGeminiRCA(history, finalSummary, errorLogs) {
|
|
91
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
92
|
+
if (!apiKey) {
|
|
93
|
+
return `<span style="color: #ef4444;">\u26A0\uFE0F Gemini API Key missing. Export GEMINI_API_KEY to enable automated 2.5 Flash RCA analysis.</span>`;
|
|
94
|
+
}
|
|
95
|
+
console.log("\u{1F9E0} Querying Gemini 2.5 Flash for Advanced Root Cause Analysis...");
|
|
96
|
+
const formattedHistory = history.map(
|
|
97
|
+
(h) => `Threads: ${h.threads} VUs | Throughput: ${h.throughput.toFixed(1)} req/s | Avg Latency: ${h.avgLatency.toFixed(1)}ms | Errors: ${(h.errorRate * 100).toFixed(2)}% | Apdex: ${h.apdex.toFixed(2)}`
|
|
98
|
+
).join("\n");
|
|
99
|
+
const uniqueLogs = Array.from(new Set(errorLogs)).slice(0, 10).join("\n");
|
|
100
|
+
const prompt = `
|
|
101
|
+
You are an expert Performance & Systems Reliability Engineer. Analyze the following load test execution telemetry and logs:
|
|
102
|
+
|
|
103
|
+
[LOAD SUMMARY METRICS]
|
|
104
|
+
- Unified Blaze Health Score: ${finalSummary.healthScore}/100
|
|
105
|
+
- Saturation Knee Point: ${finalSummary.saturationKneePoint} VUs
|
|
106
|
+
- Peak Stability Variance (Std Dev): ${finalSummary.stdDev.toFixed(2)}ms
|
|
107
|
+
- Network Bandwidth: ${finalSummary.bandwidth.toFixed(2)} MB/s
|
|
108
|
+
|
|
109
|
+
[EXECUTION STEP HISTORY]
|
|
110
|
+
${formattedHistory}
|
|
111
|
+
|
|
112
|
+
[UNIQUE LOG SAMPLES EXPLORATION]
|
|
113
|
+
${uniqueLogs || "No unique structural exceptions encountered."}
|
|
114
|
+
|
|
115
|
+
Provide a punchy, highly technical 3-paragraph Root Cause Analysis (RCA) report.
|
|
116
|
+
Paragraph 1: Diagnose the exact bottleneck point (CPU saturation, socket pool exhaustion, database lock contention, etc.) indicated by how latency changes with concurrency.
|
|
117
|
+
Paragraph 2: Correlate unique error patterns directly to performance drops.
|
|
118
|
+
Paragraph 3: Give concrete, infrastructure or architectural sizing recommendations.
|
|
119
|
+
|
|
120
|
+
CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). If you use bold text, use standard HTML <strong> tags. Wrap lines cleanly.
|
|
121
|
+
`;
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: { "Content-Type": "application/json" },
|
|
126
|
+
body: JSON.stringify({
|
|
127
|
+
contents: [{ parts: [{ text: prompt }] }],
|
|
128
|
+
generationConfig: {
|
|
129
|
+
temperature: 0.2,
|
|
130
|
+
maxOutputTokens: 2048
|
|
131
|
+
// Expanded token safety margin to prevent mid-sentence cutting
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
});
|
|
135
|
+
const data = await response.json();
|
|
136
|
+
const candidateText = data?.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
137
|
+
if (!candidateText) throw new Error("Malformed API structural response layout");
|
|
138
|
+
return candidateText.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br>");
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error("\u274C Gemini API processing breakdown failure:", error);
|
|
141
|
+
return `<span style="color: #f87171;">Failed to generate AI diagnosis framework: ${error.message}</span>`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
90
144
|
async function executeTestPipeline(config) {
|
|
91
145
|
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
92
146
|
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
@@ -334,7 +388,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
334
388
|
const analyzer = new LogAnalyzer();
|
|
335
389
|
semanticReport = analyzer.process(aggregateErrorLogs);
|
|
336
390
|
}
|
|
337
|
-
const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0
|
|
391
|
+
const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
|
|
338
392
|
const healthScore = computeHealthScoreValue(finalState.apdex, finalState.errorRate, finalState.p95Latency, config.maxAllowedLatencyMs);
|
|
339
393
|
const finalSummaryCards = {
|
|
340
394
|
apdex: finalState.apdex,
|
|
@@ -349,17 +403,17 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
349
403
|
healthScore
|
|
350
404
|
};
|
|
351
405
|
generateFinalAgentReport(history, healthScore);
|
|
406
|
+
const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, aggregateErrorLogs);
|
|
352
407
|
if (config.isSeo) {
|
|
353
408
|
const finalLat = finalState.avgLatency || finalSummaryCards.ttfb;
|
|
354
409
|
const seo = calculateSeoImpactMetrics(finalLat);
|
|
355
410
|
console.log(`\u{1F50E} [SEO Impact Report]: Target latency under stress (${finalLat.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
|
|
356
411
|
}
|
|
357
412
|
const breakdownRates = {};
|
|
358
|
-
Object.keys(globalCodeCounts).forEach((
|
|
359
|
-
const code = parseInt(codeStr, 10);
|
|
413
|
+
Object.keys(globalCodeCounts).forEach((code) => {
|
|
360
414
|
breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
|
|
361
415
|
});
|
|
362
|
-
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests);
|
|
416
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml);
|
|
363
417
|
}
|
|
364
418
|
async function runStandardStressTest(config) {
|
|
365
419
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
@@ -398,13 +452,13 @@ async function runStandardStressTest(config) {
|
|
|
398
452
|
};
|
|
399
453
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
400
454
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
455
|
+
const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
|
|
401
456
|
if (config.isSeo) {
|
|
402
457
|
const seo = calculateSeoImpactMetrics(metrics.avgLatencyMs);
|
|
403
458
|
console.log(`\u{1F50E} [SEO Impact Report]: Target latency (${metrics.avgLatencyMs.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
|
|
404
459
|
}
|
|
405
460
|
const breakdownRates = {};
|
|
406
|
-
Object.keys(metrics.codeCounts).forEach((
|
|
407
|
-
const code = parseInt(codeStr, 10);
|
|
461
|
+
Object.keys(metrics.codeCounts).forEach((code) => {
|
|
408
462
|
breakdownRates[code] = metrics.totalRequests === 0 ? 0 : metrics.codeCounts[code] / metrics.totalRequests * 100;
|
|
409
463
|
});
|
|
410
464
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, {
|
|
@@ -414,7 +468,7 @@ async function runStandardStressTest(config) {
|
|
|
414
468
|
total4xx: metrics.c4xx,
|
|
415
469
|
total5xx: metrics.c5xx,
|
|
416
470
|
breakdownRates
|
|
417
|
-
}, finalSummaryCards, rawRequests);
|
|
471
|
+
}, finalSummaryCards, rawRequests, geminiAnalysisHtml);
|
|
418
472
|
}
|
|
419
473
|
function getFallbackClusterLogs() {
|
|
420
474
|
const logs = [];
|
|
@@ -530,7 +584,7 @@ function generateFinalAgentReport(history, score) {
|
|
|
530
584
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
531
585
|
console.log("=======================================================\n");
|
|
532
586
|
}
|
|
533
|
-
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = []) {
|
|
587
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "") {
|
|
534
588
|
const labels = history.map((h, i) => `${i + 1}s`);
|
|
535
589
|
const successRequestsData = history.map((h) => h.successRequests);
|
|
536
590
|
const failedWorkloadsData = history.map((h) => h.failedRequests);
|
|
@@ -843,6 +897,16 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
843
897
|
</div>
|
|
844
898
|
</div>
|
|
845
899
|
|
|
900
|
+
<!-- Live Gemini 2.5 Flash Autonomous RCA Module -->
|
|
901
|
+
<div class="card" style="margin-bottom: 2rem; background: #0c172b; border: 1px solid #7c3aed;">
|
|
902
|
+
<h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
|
|
903
|
+
\u26A1 Gemini 2.5 Flash Autonomous Root Cause Analysis (RCA)
|
|
904
|
+
</h3>
|
|
905
|
+
<div style="font-size: 0.95rem; line-height: 1.6; color: #cbd5e1; padding: 0.5rem 0; white-space: pre-wrap; word-break: break-word;">
|
|
906
|
+
${geminiAnalysisHtml}
|
|
907
|
+
</div>
|
|
908
|
+
</div>
|
|
909
|
+
|
|
846
910
|
<div class="charts-grid">
|
|
847
911
|
<div class="graph-card">
|
|
848
912
|
<div class="graph-title">\u{1F4CA} Throughput Velocity & Workload Volume</div>
|
|
@@ -865,6 +929,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
865
929
|
</div>
|
|
866
930
|
</div>
|
|
867
931
|
|
|
932
|
+
<div class="graph-card">
|
|
933
|
+
<div class="graph-title">\u{1F369} HTTP Status Code Proportions</div>
|
|
934
|
+
<div style="height: 280px; position: relative;">
|
|
935
|
+
<canvas id="statusDonutChart"></canvas>
|
|
936
|
+
</div>
|
|
937
|
+
</div>
|
|
938
|
+
|
|
868
939
|
<div class="graph-card">
|
|
869
940
|
<div class="graph-title">\u{1F4C8} Time-to-First-Byte Scatter Plot</div>
|
|
870
941
|
<div style="height: 280px; position: relative;">
|
|
@@ -1047,8 +1118,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1047
1118
|
},
|
|
1048
1119
|
scales: {
|
|
1049
1120
|
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
1050
|
-
|
|
1051
|
-
|
|
1121
|
+
y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
|
|
1122
|
+
y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
|
|
1052
1123
|
}
|
|
1053
1124
|
}
|
|
1054
1125
|
});
|
|
Binary file
|