UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

431 lines (430 loc) 15.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resourceAnalytics = exports.ResourceAnalytics = void 0; /** * Resource usage reporting and analytics */ const events_1 = require("events"); const fs_1 = require("fs"); const path_1 = require("path"); const os_1 = require("os"); class ResourceAnalytics extends events_1.EventEmitter { constructor() { super(); this.snapshots = []; this.alerts = []; this.monitoring = false; this.lastCpuUsage = process.cpuUsage(); // Thresholds this.MEMORY_WARNING = 100; // MB this.MEMORY_CRITICAL = 200; // MB this.CPU_WARNING = 80; // % this.CPU_CRITICAL = 95; // % const analyticsDir = (0, path_1.join)((0, os_1.homedir)(), '.re-shell', 'analytics'); if (!(0, fs_1.existsSync)(analyticsDir)) { (0, fs_1.mkdirSync)(analyticsDir, { recursive: true }); } this.reportPath = (0, path_1.join)(analyticsDir, 'resource-analytics.json'); this.loadPersistedData(); } static getInstance() { if (!ResourceAnalytics.instance) { ResourceAnalytics.instance = new ResourceAnalytics(); } return ResourceAnalytics.instance; } /** * Start resource monitoring */ start(intervalMs = 5000) { if (this.monitoring) return; this.monitoring = true; this.interval = setInterval(() => { this.captureSnapshot(); }, intervalMs); // Capture initial snapshot this.captureSnapshot(); this.emit('monitoringStarted', { interval: intervalMs }); } /** * Stop resource monitoring */ stop() { if (!this.monitoring) return; this.monitoring = false; if (this.interval) { clearInterval(this.interval); this.interval = undefined; } this.persistData(); this.emit('monitoringStopped'); } /** * Add custom metric to current snapshot */ addCustomMetric(name, value) { if (this.snapshots.length > 0) { const latest = this.snapshots[this.snapshots.length - 1]; if (!latest.custom) { latest.custom = {}; } latest.custom[name] = value; } } /** * Generate comprehensive resource report */ generateReport(periodHours = 24) { const now = Date.now(); const periodStart = now - (periodHours * 60 * 60 * 1000); const periodSnapshots = this.snapshots.filter(s => s.timestamp >= periodStart); if (periodSnapshots.length === 0) { throw new Error('No data available for the specified period'); } const summary = this.calculateSummary(periodSnapshots); const trends = this.calculateTrends(periodSnapshots); const periodAlerts = this.alerts.filter(a => a.timestamp >= periodStart); const recommendations = this.generateRecommendations(summary, trends, periodAlerts); return { period: { start: periodStart, end: now, duration: now - periodStart }, summary, trends, alerts: periodAlerts, recommendations }; } /** * Get resource trends */ getTrends(metric, samples = 20) { if (this.snapshots.length < samples) { return null; } const recentSnapshots = this.snapshots.slice(-samples); const values = recentSnapshots.map(s => this.getMetricValue(s, metric)); return this.calculateTrend(metric, values, recentSnapshots); } /** * Get current resource usage */ getCurrentUsage() { return this.snapshots.length > 0 ? this.snapshots[this.snapshots.length - 1] : null; } /** * Get resource usage history */ getHistory(hours = 1) { const cutoff = Date.now() - (hours * 60 * 60 * 1000); return this.snapshots.filter(s => s.timestamp >= cutoff); } /** * Export analytics data */ exportData(format = 'json') { if (format === 'csv') { return this.exportToCsv(); } else { return JSON.stringify({ snapshots: this.snapshots, alerts: this.alerts, exported: Date.now() }, null, 2); } } /** * Clear old data */ cleanup(retentionHours = 168) { const cutoff = Date.now() - (retentionHours * 60 * 60 * 1000); this.snapshots = this.snapshots.filter(s => s.timestamp >= cutoff); this.alerts = this.alerts.filter(a => a.timestamp >= cutoff); this.persistData(); this.emit('dataCleanup', { cutoff, retained: this.snapshots.length }); } /** * Capture current resource snapshot */ captureSnapshot() { const memory = process.memoryUsage(); const currentCpu = process.cpuUsage(this.lastCpuUsage); this.lastCpuUsage = process.cpuUsage(); // Convert to percentages (approximate) const cpuUser = (currentCpu.user / 1000000) * 100; // microseconds to percent const cpuSystem = (currentCpu.system / 1000000) * 100; const snapshot = { timestamp: Date.now(), memory: { rss: memory.rss / 1024 / 1024, // MB heapTotal: memory.heapTotal / 1024 / 1024, heapUsed: memory.heapUsed / 1024 / 1024, external: memory.external / 1024 / 1024, arrayBuffers: memory.arrayBuffers / 1024 / 1024 }, cpu: { user: cpuUser, system: cpuSystem }, operations: { total: 0, // Will be updated by operation managers pending: 0, running: 0, completed: 0, failed: 0 } }; this.snapshots.push(snapshot); // Keep last 1000 snapshots if (this.snapshots.length > 1000) { this.snapshots = this.snapshots.slice(-500); } // Check for alerts this.checkAlerts(snapshot); this.emit('snapshotCaptured', snapshot); } /** * Check for resource alerts */ checkAlerts(snapshot) { const now = Date.now(); // Memory alerts if (snapshot.memory.heapUsed > this.MEMORY_CRITICAL) { this.createAlert('memory', 'critical', `Critical memory usage: ${snapshot.memory.heapUsed.toFixed(1)}MB`, snapshot.memory.heapUsed, this.MEMORY_CRITICAL, now); } else if (snapshot.memory.heapUsed > this.MEMORY_WARNING) { this.createAlert('memory', 'warning', `High memory usage: ${snapshot.memory.heapUsed.toFixed(1)}MB`, snapshot.memory.heapUsed, this.MEMORY_WARNING, now); } // CPU alerts const totalCpu = snapshot.cpu.user + snapshot.cpu.system; if (totalCpu > this.CPU_CRITICAL) { this.createAlert('cpu', 'critical', `Critical CPU usage: ${totalCpu.toFixed(1)}%`, totalCpu, this.CPU_CRITICAL, now); } else if (totalCpu > this.CPU_WARNING) { this.createAlert('cpu', 'warning', `High CPU usage: ${totalCpu.toFixed(1)}%`, totalCpu, this.CPU_WARNING, now); } } /** * Create and emit resource alert */ createAlert(type, severity, message, value, threshold, timestamp) { // Don't spam alerts - check if similar alert was created recently const recentAlerts = this.alerts.filter(a => a.type === type && a.severity === severity && timestamp - a.timestamp < 60000 // 1 minute ); if (recentAlerts.length > 0) return; const alert = { type, severity, message, value, threshold, timestamp }; this.alerts.push(alert); this.emit('resourceAlert', alert); } /** * Calculate summary statistics */ calculateSummary(snapshots) { const memoryValues = snapshots.map(s => s.memory.heapUsed); const cpuValues = snapshots.map(s => s.cpu.user + s.cpu.system); const operationsTotal = snapshots.reduce((sum, s) => sum + s.operations.total, 0); return { memory: { peak: Math.max(...memoryValues), average: memoryValues.reduce((sum, v) => sum + v, 0) / memoryValues.length, current: memoryValues[memoryValues.length - 1] || 0 }, cpu: { peak: Math.max(...cpuValues), average: cpuValues.reduce((sum, v) => sum + v, 0) / cpuValues.length, current: cpuValues[cpuValues.length - 1] || 0 }, operations: { total: operationsTotal, throughput: operationsTotal / (snapshots.length * 5 / 60), // per minute successRate: 0 // Would need to be calculated based on actual operation data } }; } /** * Calculate trends for metrics */ calculateTrends(snapshots) { const trends = []; const metrics = ['memory.heapUsed', 'cpu.total', 'operations.total']; for (const metric of metrics) { const values = snapshots.map(s => this.getMetricValue(s, metric)); const trend = this.calculateTrend(metric, values, snapshots); if (trend) { trends.push(trend); } } return trends; } /** * Calculate trend for a specific metric */ calculateTrend(metric, values, snapshots) { if (values.length < 2) return null; // Simple linear regression const n = values.length; const x = Array.from({ length: n }, (_, i) => i); const y = values; const sumX = x.reduce((sum, val) => sum + val, 0); const sumY = y.reduce((sum, val) => sum + val, 0); const sumXY = x.reduce((sum, val, i) => sum + val * y[i], 0); const sumXX = x.reduce((sum, val) => sum + val * val, 0); const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX); const intercept = (sumY - slope * sumX) / n; // Calculate R-squared for confidence const yMean = sumY / n; const ssTotal = y.reduce((sum, val) => sum + Math.pow(val - yMean, 2), 0); const ssRes = y.reduce((sum, val, i) => { const predicted = slope * x[i] + intercept; return sum + Math.pow(val - predicted, 2); }, 0); const rSquared = 1 - (ssRes / ssTotal); // Convert slope to rate per minute const timespan = snapshots[snapshots.length - 1].timestamp - snapshots[0].timestamp; const ratePerMinute = slope * (60000 / (timespan / n)); let direction; if (Math.abs(ratePerMinute) < 0.01) { direction = 'stable'; } else if (ratePerMinute > 0) { direction = 'increasing'; } else { direction = 'decreasing'; } return { metric, direction, rate: Math.abs(ratePerMinute), confidence: Math.max(0, Math.min(1, rSquared)) }; } /** * Get metric value from snapshot */ getMetricValue(snapshot, metric) { const parts = metric.split('.'); switch (parts[0]) { case 'memory': return snapshot.memory[parts[1]] || 0; case 'cpu': if (parts[1] === 'total') { return snapshot.cpu.user + snapshot.cpu.system; } return snapshot.cpu[parts[1]] || 0; case 'operations': return snapshot.operations[parts[1]] || 0; default: return snapshot.custom?.[metric] || 0; } } /** * Generate recommendations based on analysis */ generateRecommendations(summary, trends, alerts) { const recommendations = []; // Memory recommendations if (summary.memory.peak > this.MEMORY_WARNING) { recommendations.push('Consider optimizing memory usage or increasing available memory'); } const memoryTrend = trends.find(t => t.metric === 'memory.heapUsed'); if (memoryTrend?.direction === 'increasing' && memoryTrend.confidence > 0.7) { recommendations.push('Memory usage is consistently increasing - check for memory leaks'); } // CPU recommendations if (summary.cpu.peak > this.CPU_WARNING) { recommendations.push('High CPU usage detected - consider optimizing compute-intensive operations'); } // Alert-based recommendations const criticalAlerts = alerts.filter(a => a.severity === 'critical'); if (criticalAlerts.length > 0) { recommendations.push('Critical resource alerts detected - immediate attention required'); } return recommendations; } /** * Export data to CSV format */ exportToCsv() { const headers = [ 'timestamp', 'memory_rss', 'memory_heap_used', 'memory_heap_total', 'cpu_user', 'cpu_system', 'operations_total', 'operations_pending', 'operations_running' ]; const rows = this.snapshots.map(s => [ s.timestamp, s.memory.rss, s.memory.heapUsed, s.memory.heapTotal, s.cpu.user, s.cpu.system, s.operations.total, s.operations.pending, s.operations.running ]); return [headers.join(','), ...rows.map(row => row.join(','))].join('\n'); } /** * Load persisted analytics data */ loadPersistedData() { if (!(0, fs_1.existsSync)(this.reportPath)) return; try { const data = JSON.parse((0, fs_1.readFileSync)(this.reportPath, 'utf-8')); // Load last 100 snapshots if (data.snapshots && Array.isArray(data.snapshots)) { this.snapshots = data.snapshots.slice(-100); } // Load recent alerts (last 24 hours) if (data.alerts && Array.isArray(data.alerts)) { const cutoff = Date.now() - (24 * 60 * 60 * 1000); this.alerts = data.alerts.filter((a) => a.timestamp >= cutoff); } } catch (error) { // Ignore errors loading persisted data } } /** * Persist analytics data */ persistData() { try { const data = { snapshots: this.snapshots.slice(-100), // Keep last 100 alerts: this.alerts.slice(-50), // Keep last 50 lastUpdated: Date.now() }; (0, fs_1.writeFileSync)(this.reportPath, JSON.stringify(data, null, 2)); } catch (error) { // Ignore errors persisting data } } } exports.ResourceAnalytics = ResourceAnalytics; // Export singleton instance exports.resourceAnalytics = ResourceAnalytics.getInstance();