claude-flow-multilang
Version:
Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture
592 lines (514 loc) • 18.3 kB
JavaScript
/**
* Real Performance Metrics Collection System
* Tracks actual system performance, agent activity, and resource usage
*/
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { performance } from 'perf_hooks';
// Metrics storage path
const METRICS_DIR = path.join(process.cwd(), '.claude-flow', 'metrics');
const PERFORMANCE_FILE = path.join(METRICS_DIR, 'performance.json');
const AGENT_METRICS_FILE = path.join(METRICS_DIR, 'agent-metrics.json');
const TASK_METRICS_FILE = path.join(METRICS_DIR, 'task-metrics.json');
const SYSTEM_METRICS_FILE = path.join(METRICS_DIR, 'system-metrics.json');
// In-memory metrics cache
let metricsCache = {
tasks: [],
agents: {},
system: [],
performance: {
startTime: Date.now(),
totalTasks: 0,
successfulTasks: 0,
failedTasks: 0,
totalAgents: 0,
activeAgents: 0,
neuralEvents: 0
}
};
// Store interval ID for cleanup
let systemMonitoringInterval = null;
// Initialize metrics system
export async function initializeMetrics(startMonitoring = true) {
try {
await fs.mkdir(METRICS_DIR, { recursive: true });
// Load existing metrics if available
await loadMetricsFromDisk();
// Start system monitoring only if requested
if (startMonitoring) {
startSystemMonitoring();
}
return true;
} catch (err) {
console.error('Failed to initialize metrics:', err);
return false;
}
}
// Load metrics from disk
async function loadMetricsFromDisk() {
try {
// Load performance metrics
if (await fileExists(PERFORMANCE_FILE)) {
const data = await fs.readFile(PERFORMANCE_FILE, 'utf8');
const saved = JSON.parse(data);
metricsCache.performance = { ...metricsCache.performance, ...saved };
}
// Load task metrics
if (await fileExists(TASK_METRICS_FILE)) {
const data = await fs.readFile(TASK_METRICS_FILE, 'utf8');
metricsCache.tasks = JSON.parse(data);
}
// Load agent metrics
if (await fileExists(AGENT_METRICS_FILE)) {
const data = await fs.readFile(AGENT_METRICS_FILE, 'utf8');
metricsCache.agents = JSON.parse(data);
}
} catch (err) {
// Ignore errors, start fresh
}
}
// Save metrics to disk
async function saveMetricsToDisk() {
try {
await fs.writeFile(PERFORMANCE_FILE, JSON.stringify(metricsCache.performance, null, 2));
await fs.writeFile(TASK_METRICS_FILE, JSON.stringify(metricsCache.tasks, null, 2));
await fs.writeFile(AGENT_METRICS_FILE, JSON.stringify(metricsCache.agents, null, 2));
} catch (err) {
// Ignore save errors
}
}
// Track task execution
export async function trackTaskExecution(taskId, taskType, success, duration, metadata = {}) {
const task = {
id: taskId,
type: taskType,
success,
duration,
timestamp: Date.now(),
metadata
};
metricsCache.tasks.push(task);
metricsCache.performance.totalTasks++;
if (success) {
metricsCache.performance.successfulTasks++;
} else {
metricsCache.performance.failedTasks++;
}
// Keep only last 1000 tasks
if (metricsCache.tasks.length > 1000) {
metricsCache.tasks = metricsCache.tasks.slice(-1000);
}
await saveMetricsToDisk();
}
// Track agent activity
export async function trackAgentActivity(agentId, agentType, action, duration, success = true) {
if (!metricsCache.agents[agentType]) {
metricsCache.agents[agentType] = {
total: 0,
successful: 0,
failed: 0,
totalDuration: 0,
actions: []
};
}
const agent = metricsCache.agents[agentType];
agent.total++;
agent.totalDuration += duration;
if (success) {
agent.successful++;
} else {
agent.failed++;
}
agent.actions.push({
id: agentId,
action,
duration,
success,
timestamp: Date.now()
});
// Keep only last 100 actions per agent type
if (agent.actions.length > 100) {
agent.actions = agent.actions.slice(-100);
}
metricsCache.performance.totalAgents = Object.keys(metricsCache.agents).length;
await saveMetricsToDisk();
}
// Track neural events
export async function trackNeuralEvent(eventType, metadata = {}) {
metricsCache.performance.neuralEvents++;
await saveMetricsToDisk();
}
// Get performance report data
export async function getPerformanceReport(timeframe = '24h') {
const now = Date.now();
const timeframeMs = parseTimeframe(timeframe);
const cutoff = now - timeframeMs;
// Filter tasks within timeframe
const recentTasks = metricsCache.tasks.filter(task => task.timestamp >= cutoff);
// Calculate metrics
const totalTasks = recentTasks.length;
const successfulTasks = recentTasks.filter(t => t.success).length;
const successRate = totalTasks > 0 ? (successfulTasks / totalTasks) * 100 : 0;
const avgDuration = totalTasks > 0
? recentTasks.reduce((sum, t) => sum + t.duration, 0) / totalTasks
: 0;
// Agent metrics
const agentMetrics = {};
Object.entries(metricsCache.agents).forEach(([type, data]) => {
const recentActions = data.actions.filter(a => a.timestamp >= cutoff);
if (recentActions.length > 0) {
const successCount = recentActions.filter(a => a.success).length;
const avgDur = recentActions.reduce((sum, a) => sum + a.duration, 0) / recentActions.length;
agentMetrics[type] = {
total: recentActions.length,
successRate: (successCount / recentActions.length) * 100,
avgDuration: avgDur
};
}
});
// System metrics
const systemMetrics = await getSystemMetrics();
// Calculate trends (compare to previous period)
const prevCutoff = cutoff - timeframeMs;
const prevTasks = metricsCache.tasks.filter(t => t.timestamp >= prevCutoff && t.timestamp < cutoff);
const prevSuccessRate = prevTasks.length > 0
? (prevTasks.filter(t => t.success).length / prevTasks.length) * 100
: 0;
const prevAvgDuration = prevTasks.length > 0
? prevTasks.reduce((sum, t) => sum + t.duration, 0) / prevTasks.length
: 0;
const trends = {
successRateChange: successRate - prevSuccessRate,
durationChange: avgDuration - prevAvgDuration,
taskVolumeChange: totalTasks - prevTasks.length
};
return {
timeframe,
summary: {
totalTasks,
successRate,
avgDuration: avgDuration / 1000, // Convert to seconds
agentsSpawned: Object.values(agentMetrics).reduce((sum, m) => sum + m.total, 0),
memoryEfficiency: systemMetrics.memoryEfficiency,
neuralEvents: metricsCache.performance.neuralEvents
},
agentMetrics,
systemMetrics,
trends,
tasks: recentTasks.slice(-20) // Last 20 tasks
};
}
// Get bottleneck analysis data
export async function getBottleneckAnalysis(scope = 'system', target = 'all') {
const bottlenecks = [];
const recommendations = [];
// Analyze task performance
if (scope === 'system' || scope === 'task') {
const slowTasks = metricsCache.tasks
.filter(t => t.duration > 10000) // Tasks taking more than 10s
.sort((a, b) => b.duration - a.duration)
.slice(0, 5);
if (slowTasks.length > 0) {
bottlenecks.push({
severity: 'warning',
component: 'Task execution',
metric: `${slowTasks.length} slow tasks (>10s)`,
details: slowTasks.map(t => ({
id: t.id,
type: t.type,
duration: t.duration / 1000
}))
});
recommendations.push('Optimize slow task types or break them into smaller subtasks');
}
}
// Analyze agent performance
if (scope === 'system' || scope === 'agent') {
Object.entries(metricsCache.agents).forEach(([type, data]) => {
const successRate = data.total > 0 ? (data.successful / data.total) * 100 : 100;
const avgDuration = data.total > 0 ? data.totalDuration / data.total : 0;
if (successRate < 80) {
bottlenecks.push({
severity: 'critical',
component: `${type} agents`,
metric: `${successRate.toFixed(1)}% success rate`,
target: type
});
recommendations.push(`Investigate ${type} agent failures and improve error handling`);
}
if (avgDuration > 15000) {
bottlenecks.push({
severity: 'warning',
component: `${type} agents`,
metric: `${(avgDuration / 1000).toFixed(1)}s avg duration`,
target: type
});
recommendations.push(`Optimize ${type} agent performance or increase parallelization`);
}
});
}
// Analyze system resources
if (scope === 'system' || scope === 'memory') {
const systemMetrics = await getSystemMetrics();
if (systemMetrics.memoryUsagePercent > 80) {
bottlenecks.push({
severity: 'critical',
component: 'Memory usage',
metric: `${systemMetrics.memoryUsagePercent}% utilization`
});
recommendations.push('Implement memory optimization or increase system resources');
}
if (systemMetrics.cpuLoad > 0.8) {
bottlenecks.push({
severity: 'warning',
component: 'CPU usage',
metric: `${(systemMetrics.cpuLoad * 100).toFixed(1)}% load`
});
recommendations.push('Consider horizontal scaling or CPU optimization');
}
}
// Add positive indicators
if (bottlenecks.length === 0) {
bottlenecks.push({
severity: 'good',
component: 'Overall system',
metric: 'No bottlenecks detected'
});
}
return {
scope,
target,
bottlenecks,
recommendations,
analysisDuration: performance.now(),
confidenceScore: 0.85,
issuesDetected: bottlenecks.filter(b => b.severity !== 'good').length
};
}
// System monitoring
function startSystemMonitoring() {
// Clear any existing interval
if (systemMonitoringInterval) {
clearInterval(systemMonitoringInterval);
}
// Collect system metrics every 30 seconds
systemMonitoringInterval = setInterval(async () => {
const metrics = await getSystemMetrics();
// Store system metrics
if (!metricsCache.system) {
metricsCache.system = [];
}
metricsCache.system.push({
timestamp: Date.now(),
...metrics
});
// Keep only last 24 hours of system metrics
const dayAgo = Date.now() - (24 * 60 * 60 * 1000);
metricsCache.system = metricsCache.system.filter(m => m.timestamp > dayAgo);
// Save to disk
try {
await fs.writeFile(SYSTEM_METRICS_FILE, JSON.stringify(metricsCache.system, null, 2));
} catch (err) {
// Ignore save errors
}
}, 30000);
// Allow process to exit even with active interval
systemMonitoringInterval.unref();
}
// Stop system monitoring
export function stopSystemMonitoring() {
if (systemMonitoringInterval) {
clearInterval(systemMonitoringInterval);
systemMonitoringInterval = null;
}
}
// Get current system metrics
async function getSystemMetrics() {
const totalMem = os.totalmem();
const freeMem = os.freemem();
const usedMem = totalMem - freeMem;
const memoryUsagePercent = (usedMem / totalMem) * 100;
const cpuLoad = os.loadavg()[0] / os.cpus().length; // 1-minute load average
return {
memoryTotal: totalMem,
memoryUsed: usedMem,
memoryFree: freeMem,
memoryUsagePercent,
memoryEfficiency: 100 - memoryUsagePercent,
cpuCount: os.cpus().length,
cpuLoad,
platform: os.platform(),
uptime: os.uptime()
};
}
// Parse timeframe string to milliseconds
function parseTimeframe(timeframe) {
const units = {
'h': 60 * 60 * 1000,
'd': 24 * 60 * 60 * 1000
};
const match = timeframe.match(/^(\d+)([hd])$/);
if (match) {
const value = parseInt(match[1]);
const unit = match[2];
return value * units[unit];
}
// Default to 24 hours
return 24 * 60 * 60 * 1000;
}
// Check if file exists
async function fileExists(filepath) {
try {
await fs.access(filepath);
return true;
} catch {
return false;
}
}
// Cleanup function for graceful shutdown
export function cleanup() {
stopSystemMonitoring();
}
// Export metrics for reporting
export async function exportMetrics(format = 'json') {
const timestamp = Date.now();
const reportsDir = path.join(process.cwd(), 'analysis-reports');
await fs.mkdir(reportsDir, { recursive: true });
if (format === 'json') {
const reportPath = path.join(reportsDir, `performance-${timestamp}.json`);
const data = {
timestamp: new Date().toISOString(),
performance: metricsCache.performance,
tasks: metricsCache.tasks.slice(-100), // Last 100 tasks
agents: metricsCache.agents,
system: metricsCache.system.slice(-50) // Last 50 system snapshots
};
await fs.writeFile(reportPath, JSON.stringify(data, null, 2));
return reportPath;
}
if (format === 'csv') {
const reportPath = path.join(reportsDir, `performance-${timestamp}.csv`);
let csv = 'Timestamp,Type,Metric,Value\n';
// Add performance metrics
Object.entries(metricsCache.performance).forEach(([key, value]) => {
csv += `${new Date().toISOString()},performance,${key},${value}\n`;
});
// Add agent metrics
Object.entries(metricsCache.agents).forEach(([type, data]) => {
csv += `${new Date().toISOString()},agent,${type}_total,${data.total}\n`;
csv += `${new Date().toISOString()},agent,${type}_success_rate,${data.total > 0 ? (data.successful / data.total) * 100 : 0}\n`;
csv += `${new Date().toISOString()},agent,${type}_avg_duration,${data.total > 0 ? data.totalDuration / data.total : 0}\n`;
});
await fs.writeFile(reportPath, csv);
return reportPath;
}
if (format === 'html') {
const reportPath = path.join(reportsDir, `performance-${timestamp}.html`);
const report = await getPerformanceReport('24h');
const html = generateHTMLReport(report);
await fs.writeFile(reportPath, html);
return reportPath;
}
throw new Error(`Unsupported format: ${format}`);
}
// Generate HTML report
function generateHTMLReport(report) {
return `<!DOCTYPE html>
<html>
<head>
<title>Claude Flow Performance Report - ${new Date().toISOString()}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h1, h2 { color: #333; }
.metric { display: inline-block; margin: 10px 20px 10px 0; }
.metric-label { color: #666; font-size: 14px; }
.metric-value { font-size: 24px; font-weight: bold; color: #2196F3; }
.trend { font-size: 14px; margin-left: 10px; }
.trend.positive { color: #4CAF50; }
.trend.negative { color: #F44336; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #f5f5f5; font-weight: bold; }
.chart { margin: 20px 0; height: 300px; background: #fafafa; border: 1px solid #ddd; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #999; }
</style>
</head>
<body>
<div class="container">
<h1>Claude Flow Performance Report</h1>
<p>Generated: ${new Date().toISOString()} | Timeframe: ${report.timeframe}</p>
<h2>Summary Metrics</h2>
<div class="metrics">
<div class="metric">
<div class="metric-label">Total Tasks</div>
<div class="metric-value">${report.summary.totalTasks}</div>
${report.trends.taskVolumeChange !== 0 ? `<span class="trend ${report.trends.taskVolumeChange > 0 ? 'positive' : 'negative'}">${report.trends.taskVolumeChange > 0 ? '+' : ''}${report.trends.taskVolumeChange}</span>` : ''}
</div>
<div class="metric">
<div class="metric-label">Success Rate</div>
<div class="metric-value">${report.summary.successRate.toFixed(1)}%</div>
${report.trends.successRateChange !== 0 ? `<span class="trend ${report.trends.successRateChange > 0 ? 'positive' : 'negative'}">${report.trends.successRateChange > 0 ? '+' : ''}${report.trends.successRateChange.toFixed(1)}%</span>` : ''}
</div>
<div class="metric">
<div class="metric-label">Avg Duration</div>
<div class="metric-value">${report.summary.avgDuration.toFixed(1)}s</div>
${report.trends.durationChange !== 0 ? `<span class="trend ${report.trends.durationChange < 0 ? 'positive' : 'negative'}">${report.trends.durationChange > 0 ? '+' : ''}${(report.trends.durationChange / 1000).toFixed(1)}s</span>` : ''}
</div>
<div class="metric">
<div class="metric-label">Memory Efficiency</div>
<div class="metric-value">${report.summary.memoryEfficiency.toFixed(0)}%</div>
</div>
</div>
<h2>Agent Performance</h2>
<table>
<thead>
<tr>
<th>Agent Type</th>
<th>Total Actions</th>
<th>Success Rate</th>
<th>Avg Duration</th>
</tr>
</thead>
<tbody>
${Object.entries(report.agentMetrics).map(([type, metrics]) => `
<tr>
<td>${type}</td>
<td>${metrics.total}</td>
<td>${metrics.successRate.toFixed(1)}%</td>
<td>${(metrics.avgDuration / 1000).toFixed(1)}s</td>
</tr>
`).join('')}
</tbody>
</table>
<h2>Performance Trends</h2>
<div class="chart">
<p>Interactive charts would be displayed here</p>
</div>
<h2>Recent Tasks</h2>
<table>
<thead>
<tr>
<th>Task ID</th>
<th>Type</th>
<th>Status</th>
<th>Duration</th>
<th>Timestamp</th>
</tr>
</thead>
<tbody>
${report.tasks.slice(-10).reverse().map(task => `
<tr>
<td>${task.id}</td>
<td>${task.type}</td>
<td>${task.success ? '✅ Success' : '❌ Failed'}</td>
<td>${(task.duration / 1000).toFixed(2)}s</td>
<td>${new Date(task.timestamp).toLocaleString()}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
</body>
</html>`;
}