snow-flow 1.3.25 → 1.3.28
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/.claude/config.json +33 -9
- package/.claude-flow/queen/queen-memory.db +0 -0
- package/.roo/README.md +56 -0
- package/.roo/workflows/basic-tdd.json +32 -0
- package/.roomodes +122 -0
- package/CLAUDE.md +140 -752
- package/claude-flow +30 -75
- package/dist/cli.js +250 -7
- package/dist/compliance/advanced-compliance-system.js +857 -0
- package/dist/compliance/index.js +8 -0
- package/dist/documentation/index.js +8 -0
- package/dist/documentation/self-documenting-system.js +1006 -0
- package/dist/healing/index.js +8 -0
- package/dist/healing/self-healing-system.js +1041 -0
- package/dist/intelligence/performance-recommendations-engine.js +479 -4
- package/dist/managers/scope-manager.js +5 -2
- package/dist/mcp/servicenow-deployment-mcp.js +113 -15
- package/dist/mcp/servicenow-flow-composer-mcp.js +4 -2
- package/dist/mcp/servicenow-intelligent-mcp.js +351 -0
- package/dist/mcp/servicenow-operations-mcp.js +2 -2
- package/dist/memory/memory-system.js +146 -0
- package/dist/monitoring/enhanced-monitoring-system.js +1085 -0
- package/dist/optimization/cost-optimization-engine.js +771 -0
- package/dist/optimization/flow-performance-optimizer.js +723 -0
- package/dist/optimization/index.js +10 -0
- package/dist/orchestration/flow-update-orchestrator.js +648 -0
- package/dist/rollback/smart-rollback-system.js +462 -0
- package/dist/templates/flow-template-system.js +625 -0
- package/dist/testing/flow-testing-automation.js +641 -0
- package/dist/testing/integration-test-suite.js +890 -0
- package/dist/utils/flow-structure-builder.js +1 -1
- package/dist/utils/servicenow-client.js +50 -2
- package/dist/utils/snow-oauth.js +66 -8
- package/dist/utils/xml-first-flow-generator.js +6 -0
- package/dist/version.js +13 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1085 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 🚀 PERFORMANCE FIX: Enhanced Monitoring System
|
|
4
|
+
* Advanced real-time monitoring with predictive analytics, alerting, and dashboards
|
|
5
|
+
*
|
|
6
|
+
* Addresses beta testing feedback on performance monitoring gaps
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EnhancedMonitoringSystem = void 0;
|
|
13
|
+
const events_1 = require("events");
|
|
14
|
+
const logger_1 = require("../utils/logger");
|
|
15
|
+
const ws_1 = __importDefault(require("ws"));
|
|
16
|
+
const http_1 = __importDefault(require("http"));
|
|
17
|
+
class EnhancedMonitoringSystem extends events_1.EventEmitter {
|
|
18
|
+
constructor(performanceTracker, memory, config) {
|
|
19
|
+
super();
|
|
20
|
+
this.activeAlerts = new Map();
|
|
21
|
+
this.metricHistory = new Map();
|
|
22
|
+
this.connectedClients = new Set();
|
|
23
|
+
this.performanceTracker = performanceTracker;
|
|
24
|
+
this.memory = memory;
|
|
25
|
+
this.logger = new logger_1.Logger('EnhancedMonitoringSystem');
|
|
26
|
+
this.config = {
|
|
27
|
+
realTimeEnabled: true,
|
|
28
|
+
predictiveAnalyticsEnabled: true,
|
|
29
|
+
alertingEnabled: true,
|
|
30
|
+
dashboardEnabled: true,
|
|
31
|
+
websocketPort: 8090,
|
|
32
|
+
alertThresholds: {
|
|
33
|
+
cpuUsage: 80,
|
|
34
|
+
memoryUsage: 85,
|
|
35
|
+
diskUsage: 90,
|
|
36
|
+
responseTime: 5000,
|
|
37
|
+
errorRate: 10,
|
|
38
|
+
cacheHitRate: 50,
|
|
39
|
+
queueDepth: 100
|
|
40
|
+
},
|
|
41
|
+
retentionPeriods: {
|
|
42
|
+
realTimeMetrics: 3600000, // 1 hour
|
|
43
|
+
hourlyAggregates: 2592000000, // 30 days
|
|
44
|
+
dailyAggregates: 7776000000, // 90 days
|
|
45
|
+
weeklyAggregates: 31536000000 // 1 year
|
|
46
|
+
},
|
|
47
|
+
...config
|
|
48
|
+
};
|
|
49
|
+
this.systemHealth = this.initializeSystemHealth();
|
|
50
|
+
this.logger.info('🚀 Enhanced Monitoring System initialized', {
|
|
51
|
+
realTimeEnabled: this.config.realTimeEnabled,
|
|
52
|
+
predictiveAnalyticsEnabled: this.config.predictiveAnalyticsEnabled,
|
|
53
|
+
alertingEnabled: this.config.alertingEnabled,
|
|
54
|
+
dashboardEnabled: this.config.dashboardEnabled
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Start the enhanced monitoring system
|
|
59
|
+
*/
|
|
60
|
+
async start() {
|
|
61
|
+
this.logger.info('🚀 Starting Enhanced Monitoring System...');
|
|
62
|
+
// Initialize monitoring tables
|
|
63
|
+
await this.createMonitoringTables();
|
|
64
|
+
// Start real-time monitoring
|
|
65
|
+
if (this.config.realTimeEnabled) {
|
|
66
|
+
this.startRealTimeMonitoring();
|
|
67
|
+
}
|
|
68
|
+
// Start alerting system
|
|
69
|
+
if (this.config.alertingEnabled) {
|
|
70
|
+
this.startAlertingSystem();
|
|
71
|
+
}
|
|
72
|
+
// Start predictive analytics
|
|
73
|
+
if (this.config.predictiveAnalyticsEnabled) {
|
|
74
|
+
this.startPredictiveAnalytics();
|
|
75
|
+
}
|
|
76
|
+
// Start dashboard server
|
|
77
|
+
if (this.config.dashboardEnabled) {
|
|
78
|
+
await this.startDashboardServer();
|
|
79
|
+
}
|
|
80
|
+
// Set up performance tracker integration
|
|
81
|
+
this.setupPerformanceTrackerIntegration();
|
|
82
|
+
this.logger.info('✅ Enhanced Monitoring System started successfully');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get current system health status
|
|
86
|
+
*/
|
|
87
|
+
async getSystemHealth() {
|
|
88
|
+
await this.updateSystemHealth();
|
|
89
|
+
return { ...this.systemHealth };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get performance trends for specified periods
|
|
93
|
+
*/
|
|
94
|
+
async getPerformanceTrends(periods = ['hour', 'day', 'week']) {
|
|
95
|
+
const trends = [];
|
|
96
|
+
const metrics = [
|
|
97
|
+
'response_time',
|
|
98
|
+
'throughput',
|
|
99
|
+
'error_rate',
|
|
100
|
+
'cpu_usage',
|
|
101
|
+
'memory_usage',
|
|
102
|
+
'cache_hit_rate'
|
|
103
|
+
];
|
|
104
|
+
for (const metric of metrics) {
|
|
105
|
+
for (const period of periods) {
|
|
106
|
+
const trend = await this.calculateTrend(metric, period);
|
|
107
|
+
if (trend) {
|
|
108
|
+
trends.push(trend);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return trends.sort((a, b) => {
|
|
113
|
+
// Sort by severity: degrading trends first, then by impact
|
|
114
|
+
if (a.trend === 'degrading' && b.trend !== 'degrading')
|
|
115
|
+
return -1;
|
|
116
|
+
if (b.trend === 'degrading' && a.trend !== 'degrading')
|
|
117
|
+
return 1;
|
|
118
|
+
return Math.abs(b.percentageChange) - Math.abs(a.percentageChange);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get resource usage forecasts
|
|
123
|
+
*/
|
|
124
|
+
async getResourceForecasts(timeHorizonHours = 24) {
|
|
125
|
+
const forecasts = [];
|
|
126
|
+
const resources = ['cpu', 'memory', 'disk', 'network', 'database_connections'];
|
|
127
|
+
for (const resource of resources) {
|
|
128
|
+
const forecast = await this.generateResourceForecast(resource, timeHorizonHours);
|
|
129
|
+
if (forecast) {
|
|
130
|
+
forecasts.push(forecast);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return forecasts.sort((a, b) => {
|
|
134
|
+
// Sort by urgency: resources approaching exhaustion first
|
|
135
|
+
if (a.exhaustionDate && b.exhaustionDate) {
|
|
136
|
+
return a.exhaustionDate.getTime() - b.exhaustionDate.getTime();
|
|
137
|
+
}
|
|
138
|
+
if (a.exhaustionDate && !b.exhaustionDate)
|
|
139
|
+
return -1;
|
|
140
|
+
if (!a.exhaustionDate && b.exhaustionDate)
|
|
141
|
+
return 1;
|
|
142
|
+
return b.predictedUsage - a.predictedUsage;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Generate comprehensive monitoring report
|
|
147
|
+
*/
|
|
148
|
+
async generateMonitoringReport(period = 'day') {
|
|
149
|
+
this.logger.info('📊 Generating comprehensive monitoring report', { period });
|
|
150
|
+
const [systemHealth, performanceReport, trends, forecasts, alerts, recommendations] = await Promise.all([
|
|
151
|
+
this.getSystemHealth(),
|
|
152
|
+
this.performanceTracker.generateReport(period),
|
|
153
|
+
this.getPerformanceTrends([period]),
|
|
154
|
+
this.getResourceForecasts(period === 'hour' ? 24 : 168), // 24h or 1 week
|
|
155
|
+
this.getActiveAlerts(),
|
|
156
|
+
this.generateSmartRecommendations()
|
|
157
|
+
]);
|
|
158
|
+
const report = {
|
|
159
|
+
timestamp: new Date(),
|
|
160
|
+
period,
|
|
161
|
+
summary: {
|
|
162
|
+
overallHealth: systemHealth.overall,
|
|
163
|
+
totalAlerts: alerts.length,
|
|
164
|
+
criticalIssues: alerts.filter(a => a.severity === 'critical').length,
|
|
165
|
+
performanceScore: this.calculatePerformanceScore(performanceReport, systemHealth),
|
|
166
|
+
trendsCount: trends.length,
|
|
167
|
+
forecastsCount: forecasts.length
|
|
168
|
+
},
|
|
169
|
+
systemHealth,
|
|
170
|
+
performance: performanceReport,
|
|
171
|
+
trends: trends.slice(0, 10), // Top 10 trends
|
|
172
|
+
forecasts: forecasts.slice(0, 5), // Top 5 forecasts
|
|
173
|
+
alerts: alerts.slice(0, 20), // Recent 20 alerts
|
|
174
|
+
recommendations: recommendations.slice(0, 10), // Top 10 recommendations
|
|
175
|
+
metadata: {
|
|
176
|
+
monitoringSystemVersion: '2.0.0',
|
|
177
|
+
reportGenerationTime: Date.now() - Date.now(), // Will be updated at end
|
|
178
|
+
includedComponents: Object.keys(systemHealth.components),
|
|
179
|
+
dataPoints: await this.getDataPointCount(period)
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
// Update generation time
|
|
183
|
+
report.metadata.reportGenerationTime = Date.now() - report.timestamp.getTime();
|
|
184
|
+
// Store report
|
|
185
|
+
await this.memory.store(`monitoring_report_${period}_${Date.now()}`, report, this.config.retentionPeriods.dailyAggregates);
|
|
186
|
+
this.logger.info('✅ Monitoring report generated', {
|
|
187
|
+
period,
|
|
188
|
+
overallHealth: systemHealth.overall,
|
|
189
|
+
alertCount: alerts.length,
|
|
190
|
+
generationTime: report.metadata.reportGenerationTime
|
|
191
|
+
});
|
|
192
|
+
return report;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Acknowledge an alert
|
|
196
|
+
*/
|
|
197
|
+
async acknowledgeAlert(alertId, acknowledgedBy) {
|
|
198
|
+
const alert = this.activeAlerts.get(alertId);
|
|
199
|
+
if (!alert) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
alert.acknowledged = true;
|
|
203
|
+
// Store acknowledgment
|
|
204
|
+
await this.memory.store(`alert_ack_${alertId}`, {
|
|
205
|
+
alertId,
|
|
206
|
+
acknowledgedBy,
|
|
207
|
+
acknowledgedAt: new Date(),
|
|
208
|
+
alert: { ...alert }
|
|
209
|
+
});
|
|
210
|
+
this.logger.info('🔔 Alert acknowledged', { alertId, acknowledgedBy });
|
|
211
|
+
// Broadcast to connected clients
|
|
212
|
+
this.broadcastToClients({
|
|
213
|
+
type: 'alert_acknowledged',
|
|
214
|
+
data: { alertId, acknowledgedBy, timestamp: new Date() }
|
|
215
|
+
});
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get active alerts
|
|
220
|
+
*/
|
|
221
|
+
async getActiveAlerts() {
|
|
222
|
+
return Array.from(this.activeAlerts.values())
|
|
223
|
+
.sort((a, b) => {
|
|
224
|
+
// Sort by severity, then by timestamp
|
|
225
|
+
const severityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
226
|
+
const severityDiff = severityOrder[b.severity] - severityOrder[a.severity];
|
|
227
|
+
if (severityDiff !== 0)
|
|
228
|
+
return severityDiff;
|
|
229
|
+
return b.timestamp.getTime() - a.timestamp.getTime();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Private Methods
|
|
234
|
+
*/
|
|
235
|
+
initializeSystemHealth() {
|
|
236
|
+
return {
|
|
237
|
+
overall: 'unknown',
|
|
238
|
+
components: {
|
|
239
|
+
cpu: { status: 'unknown', value: 0, threshold: 80, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' },
|
|
240
|
+
memory: { status: 'unknown', value: 0, threshold: 85, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' },
|
|
241
|
+
disk: { status: 'unknown', value: 0, threshold: 90, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' },
|
|
242
|
+
network: { status: 'unknown', value: 0, threshold: 80, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' },
|
|
243
|
+
database: { status: 'unknown', value: 0, threshold: 75, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' },
|
|
244
|
+
cache: { status: 'unknown', value: 0, threshold: 50, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' },
|
|
245
|
+
api: { status: 'unknown', value: 0, threshold: 5000, trend: 'stable', lastUpdated: new Date(), details: 'Initializing...' }
|
|
246
|
+
},
|
|
247
|
+
alerts: [],
|
|
248
|
+
recommendations: [],
|
|
249
|
+
predictedIssues: []
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async createMonitoringTables() {
|
|
253
|
+
await this.memory.execute(`
|
|
254
|
+
CREATE TABLE IF NOT EXISTS monitoring_metrics (
|
|
255
|
+
id TEXT PRIMARY KEY,
|
|
256
|
+
metric_name TEXT NOT NULL,
|
|
257
|
+
metric_value REAL NOT NULL,
|
|
258
|
+
component TEXT NOT NULL,
|
|
259
|
+
timestamp INTEGER NOT NULL,
|
|
260
|
+
metadata TEXT,
|
|
261
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
CREATE INDEX IF NOT EXISTS idx_monitoring_metrics_name ON monitoring_metrics(metric_name);
|
|
265
|
+
CREATE INDEX IF NOT EXISTS idx_monitoring_metrics_timestamp ON monitoring_metrics(timestamp);
|
|
266
|
+
CREATE INDEX IF NOT EXISTS idx_monitoring_metrics_component ON monitoring_metrics(component);
|
|
267
|
+
|
|
268
|
+
CREATE TABLE IF NOT EXISTS monitoring_alerts (
|
|
269
|
+
id TEXT PRIMARY KEY,
|
|
270
|
+
severity TEXT NOT NULL,
|
|
271
|
+
component TEXT NOT NULL,
|
|
272
|
+
message TEXT NOT NULL,
|
|
273
|
+
timestamp INTEGER NOT NULL,
|
|
274
|
+
acknowledged BOOLEAN DEFAULT FALSE,
|
|
275
|
+
acknowledged_by TEXT,
|
|
276
|
+
acknowledged_at INTEGER,
|
|
277
|
+
count INTEGER DEFAULT 1,
|
|
278
|
+
first_seen INTEGER NOT NULL,
|
|
279
|
+
last_seen INTEGER NOT NULL,
|
|
280
|
+
metadata TEXT,
|
|
281
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
CREATE INDEX IF NOT EXISTS idx_monitoring_alerts_severity ON monitoring_alerts(severity);
|
|
285
|
+
CREATE INDEX IF NOT EXISTS idx_monitoring_alerts_component ON monitoring_alerts(component);
|
|
286
|
+
CREATE INDEX IF NOT EXISTS idx_monitoring_alerts_timestamp ON monitoring_alerts(timestamp);
|
|
287
|
+
|
|
288
|
+
CREATE TABLE IF NOT EXISTS monitoring_predictions (
|
|
289
|
+
id TEXT PRIMARY KEY,
|
|
290
|
+
prediction_type TEXT NOT NULL,
|
|
291
|
+
component TEXT NOT NULL,
|
|
292
|
+
description TEXT NOT NULL,
|
|
293
|
+
probability REAL NOT NULL,
|
|
294
|
+
estimated_time INTEGER,
|
|
295
|
+
impact TEXT NOT NULL,
|
|
296
|
+
recommendations TEXT,
|
|
297
|
+
confidence REAL NOT NULL,
|
|
298
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
299
|
+
);
|
|
300
|
+
`);
|
|
301
|
+
}
|
|
302
|
+
startRealTimeMonitoring() {
|
|
303
|
+
this.logger.info('🔄 Starting real-time monitoring...');
|
|
304
|
+
this.monitoringInterval = setInterval(async () => {
|
|
305
|
+
try {
|
|
306
|
+
await this.collectSystemMetrics();
|
|
307
|
+
await this.updateSystemHealth();
|
|
308
|
+
// Broadcast to connected clients
|
|
309
|
+
if (this.connectedClients.size > 0) {
|
|
310
|
+
this.broadcastToClients({
|
|
311
|
+
type: 'system_health_update',
|
|
312
|
+
data: this.systemHealth
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
this.logger.error('❌ Error in real-time monitoring:', error);
|
|
318
|
+
}
|
|
319
|
+
}, 5000); // Every 5 seconds
|
|
320
|
+
}
|
|
321
|
+
startAlertingSystem() {
|
|
322
|
+
this.logger.info('🔔 Starting alerting system...');
|
|
323
|
+
this.alertingInterval = setInterval(async () => {
|
|
324
|
+
try {
|
|
325
|
+
await this.checkAlertConditions();
|
|
326
|
+
await this.cleanupOldAlerts();
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
this.logger.error('❌ Error in alerting system:', error);
|
|
330
|
+
}
|
|
331
|
+
}, 10000); // Every 10 seconds
|
|
332
|
+
}
|
|
333
|
+
startPredictiveAnalytics() {
|
|
334
|
+
this.logger.info('🔮 Starting predictive analytics...');
|
|
335
|
+
this.predictionInterval = setInterval(async () => {
|
|
336
|
+
try {
|
|
337
|
+
await this.runPredictiveAnalysis();
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
this.logger.error('❌ Error in predictive analytics:', error);
|
|
341
|
+
}
|
|
342
|
+
}, 300000); // Every 5 minutes
|
|
343
|
+
}
|
|
344
|
+
async startDashboardServer() {
|
|
345
|
+
this.httpServer = http_1.default.createServer((req, res) => {
|
|
346
|
+
this.handleHttpRequest(req, res);
|
|
347
|
+
});
|
|
348
|
+
this.websocketServer = new ws_1.default.Server({
|
|
349
|
+
server: this.httpServer,
|
|
350
|
+
path: '/monitoring-ws'
|
|
351
|
+
});
|
|
352
|
+
this.websocketServer.on('connection', (ws) => {
|
|
353
|
+
this.handleWebSocketConnection(ws);
|
|
354
|
+
});
|
|
355
|
+
return new Promise((resolve) => {
|
|
356
|
+
this.httpServer.listen(this.config.websocketPort, () => {
|
|
357
|
+
this.logger.info(`🌐 Dashboard server started on port ${this.config.websocketPort}`);
|
|
358
|
+
resolve();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
setupPerformanceTrackerIntegration() {
|
|
363
|
+
// Listen to performance tracker events
|
|
364
|
+
this.performanceTracker.on('performance:slow', (data) => {
|
|
365
|
+
this.createAlert('high', 'api', `Slow operation detected: ${data.operation} (${data.duration}ms)`, data);
|
|
366
|
+
});
|
|
367
|
+
this.performanceTracker.on('performance:failure', (data) => {
|
|
368
|
+
this.createAlert('medium', 'api', `Operation failed: ${data.operation} - ${data.error}`, data);
|
|
369
|
+
});
|
|
370
|
+
this.performanceTracker.on('performance:high-memory', (data) => {
|
|
371
|
+
this.createAlert('medium', 'memory', `High memory usage: ${data.operation} (${data.memoryUsage}MB)`, data);
|
|
372
|
+
});
|
|
373
|
+
this.performanceTracker.on('resource:warning', (data) => {
|
|
374
|
+
this.createAlert('high', data.type, `Resource warning: ${data.type} usage at ${(data.usage * 100).toFixed(1)}%`, data);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
async collectSystemMetrics() {
|
|
378
|
+
const timestamp = Date.now();
|
|
379
|
+
// Collect Node.js process metrics
|
|
380
|
+
const memUsage = process.memoryUsage();
|
|
381
|
+
const cpuUsage = process.cpuUsage();
|
|
382
|
+
const metrics = [
|
|
383
|
+
{
|
|
384
|
+
name: 'process_memory_heap_used',
|
|
385
|
+
value: memUsage.heapUsed / 1024 / 1024, // MB
|
|
386
|
+
component: 'memory'
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'process_memory_heap_total',
|
|
390
|
+
value: memUsage.heapTotal / 1024 / 1024, // MB
|
|
391
|
+
component: 'memory'
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'process_memory_rss',
|
|
395
|
+
value: memUsage.rss / 1024 / 1024, // MB
|
|
396
|
+
component: 'memory'
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: 'process_cpu_user',
|
|
400
|
+
value: cpuUsage.user / 1000, // milliseconds
|
|
401
|
+
component: 'cpu'
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: 'process_cpu_system',
|
|
405
|
+
value: cpuUsage.system / 1000, // milliseconds
|
|
406
|
+
component: 'cpu'
|
|
407
|
+
}
|
|
408
|
+
];
|
|
409
|
+
// Collect memory system metrics
|
|
410
|
+
const memoryStats = await this.memory.getDatabaseStats();
|
|
411
|
+
metrics.push({
|
|
412
|
+
name: 'database_size',
|
|
413
|
+
value: memoryStats.size / 1024 / 1024, // MB
|
|
414
|
+
component: 'database'
|
|
415
|
+
});
|
|
416
|
+
// Collect cache metrics if available
|
|
417
|
+
try {
|
|
418
|
+
const cacheStats = await this.memory.getCacheStats();
|
|
419
|
+
metrics.push({
|
|
420
|
+
name: 'cache_hit_rate',
|
|
421
|
+
value: cacheStats.hits / (cacheStats.hits + cacheStats.misses) * 100,
|
|
422
|
+
component: 'cache'
|
|
423
|
+
}, {
|
|
424
|
+
name: 'cache_size',
|
|
425
|
+
value: cacheStats.size,
|
|
426
|
+
component: 'cache'
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
// Cache stats may not be available
|
|
431
|
+
}
|
|
432
|
+
// Store metrics
|
|
433
|
+
for (const metric of metrics) {
|
|
434
|
+
await this.storeMetric(metric.name, metric.value, metric.component, timestamp);
|
|
435
|
+
// Update history for trend analysis
|
|
436
|
+
const historyKey = `${metric.component}_${metric.name}`;
|
|
437
|
+
if (!this.metricHistory.has(historyKey)) {
|
|
438
|
+
this.metricHistory.set(historyKey, []);
|
|
439
|
+
}
|
|
440
|
+
const history = this.metricHistory.get(historyKey);
|
|
441
|
+
history.push(metric.value);
|
|
442
|
+
// Keep only last 100 values
|
|
443
|
+
if (history.length > 100) {
|
|
444
|
+
history.shift();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async storeMetric(name, value, component, timestamp) {
|
|
449
|
+
const id = `${component}_${name}_${timestamp}`;
|
|
450
|
+
await this.memory.insert('monitoring_metrics', {
|
|
451
|
+
id,
|
|
452
|
+
metric_name: name,
|
|
453
|
+
metric_value: value,
|
|
454
|
+
component,
|
|
455
|
+
timestamp,
|
|
456
|
+
metadata: JSON.stringify({
|
|
457
|
+
node_version: process.version,
|
|
458
|
+
platform: process.platform,
|
|
459
|
+
arch: process.arch
|
|
460
|
+
})
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
async updateSystemHealth() {
|
|
464
|
+
const now = new Date();
|
|
465
|
+
// Update CPU health
|
|
466
|
+
const cpuHistory = this.metricHistory.get('cpu_process_cpu_user') || [];
|
|
467
|
+
if (cpuHistory.length > 0) {
|
|
468
|
+
const currentCpu = cpuHistory[cpuHistory.length - 1] || 0;
|
|
469
|
+
this.systemHealth.components.cpu = {
|
|
470
|
+
status: currentCpu > this.config.alertThresholds.cpuUsage ? 'critical' :
|
|
471
|
+
currentCpu > this.config.alertThresholds.cpuUsage * 0.8 ? 'warning' : 'healthy',
|
|
472
|
+
value: currentCpu,
|
|
473
|
+
threshold: this.config.alertThresholds.cpuUsage,
|
|
474
|
+
trend: this.calculateTrend('cpu', cpuHistory),
|
|
475
|
+
lastUpdated: now,
|
|
476
|
+
details: `CPU usage: ${currentCpu.toFixed(1)}%`
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
// Update Memory health
|
|
480
|
+
const memHistory = this.metricHistory.get('memory_process_memory_heap_used') || [];
|
|
481
|
+
if (memHistory.length > 0) {
|
|
482
|
+
const currentMem = memHistory[memHistory.length - 1] || 0;
|
|
483
|
+
this.systemHealth.components.memory = {
|
|
484
|
+
status: currentMem > 500 ? 'critical' : currentMem > 300 ? 'warning' : 'healthy',
|
|
485
|
+
value: currentMem,
|
|
486
|
+
threshold: 500,
|
|
487
|
+
trend: this.calculateTrend('memory', memHistory),
|
|
488
|
+
lastUpdated: now,
|
|
489
|
+
details: `Heap usage: ${currentMem.toFixed(1)}MB`
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
// Update Database health
|
|
493
|
+
const dbHistory = this.metricHistory.get('database_database_size') || [];
|
|
494
|
+
if (dbHistory.length > 0) {
|
|
495
|
+
const currentDb = dbHistory[dbHistory.length - 1] || 0;
|
|
496
|
+
this.systemHealth.components.database = {
|
|
497
|
+
status: currentDb > 1000 ? 'warning' : 'healthy',
|
|
498
|
+
value: currentDb,
|
|
499
|
+
threshold: 1000,
|
|
500
|
+
trend: this.calculateTrend('database', dbHistory),
|
|
501
|
+
lastUpdated: now,
|
|
502
|
+
details: `Database size: ${currentDb.toFixed(1)}MB`
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// Update Cache health
|
|
506
|
+
const cacheHitHistory = this.metricHistory.get('cache_cache_hit_rate') || [];
|
|
507
|
+
if (cacheHitHistory.length > 0) {
|
|
508
|
+
const currentHitRate = cacheHitHistory[cacheHitHistory.length - 1] || 0;
|
|
509
|
+
this.systemHealth.components.cache = {
|
|
510
|
+
status: currentHitRate < this.config.alertThresholds.cacheHitRate ? 'warning' : 'healthy',
|
|
511
|
+
value: currentHitRate,
|
|
512
|
+
threshold: this.config.alertThresholds.cacheHitRate,
|
|
513
|
+
trend: this.calculateTrend('cache', cacheHitHistory),
|
|
514
|
+
lastUpdated: now,
|
|
515
|
+
details: `Cache hit rate: ${currentHitRate.toFixed(1)}%`
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
// Calculate overall health
|
|
519
|
+
const componentStatuses = Object.values(this.systemHealth.components).map(c => c.status);
|
|
520
|
+
if (componentStatuses.includes('critical')) {
|
|
521
|
+
this.systemHealth.overall = 'critical';
|
|
522
|
+
}
|
|
523
|
+
else if (componentStatuses.includes('warning')) {
|
|
524
|
+
this.systemHealth.overall = 'warning';
|
|
525
|
+
}
|
|
526
|
+
else if (componentStatuses.every(s => s === 'healthy')) {
|
|
527
|
+
this.systemHealth.overall = 'healthy';
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
this.systemHealth.overall = 'unknown';
|
|
531
|
+
}
|
|
532
|
+
// Update alerts list
|
|
533
|
+
this.systemHealth.alerts = await this.getActiveAlerts();
|
|
534
|
+
}
|
|
535
|
+
calculateTrend(component, history) {
|
|
536
|
+
if (history.length < 10)
|
|
537
|
+
return 'stable';
|
|
538
|
+
const recent = history.slice(-5);
|
|
539
|
+
const older = history.slice(-10, -5);
|
|
540
|
+
const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length;
|
|
541
|
+
const olderAvg = older.reduce((a, b) => a + b, 0) / older.length;
|
|
542
|
+
const changePercent = ((recentAvg - olderAvg) / olderAvg) * 100;
|
|
543
|
+
// For CPU, memory, response time: increasing is degrading
|
|
544
|
+
// For cache hit rate, throughput: increasing is improving
|
|
545
|
+
const isInverseMetric = component.includes('cache') || component.includes('throughput');
|
|
546
|
+
if (Math.abs(changePercent) < 5)
|
|
547
|
+
return 'stable';
|
|
548
|
+
if (isInverseMetric) {
|
|
549
|
+
return changePercent > 0 ? 'improving' : 'degrading';
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
return changePercent > 0 ? 'degrading' : 'improving';
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
async checkAlertConditions() {
|
|
556
|
+
const now = Date.now();
|
|
557
|
+
// Check each component health
|
|
558
|
+
for (const [componentName, component] of Object.entries(this.systemHealth.components)) {
|
|
559
|
+
if (component.status === 'critical' || component.status === 'warning') {
|
|
560
|
+
const severity = component.status === 'critical' ? 'critical' : 'high';
|
|
561
|
+
const alertId = `${componentName}_${component.status}`;
|
|
562
|
+
if (this.activeAlerts.has(alertId)) {
|
|
563
|
+
// Update existing alert
|
|
564
|
+
const alert = this.activeAlerts.get(alertId);
|
|
565
|
+
alert.count++;
|
|
566
|
+
alert.lastSeen = new Date();
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
// Create new alert
|
|
570
|
+
await this.createAlert(severity, componentName, component.details, {
|
|
571
|
+
threshold: component.threshold,
|
|
572
|
+
currentValue: component.value,
|
|
573
|
+
trend: component.trend
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
async createAlert(severity, component, message, metadata) {
|
|
580
|
+
const alertId = `${component}_${severity}_${Date.now()}`;
|
|
581
|
+
const now = new Date();
|
|
582
|
+
const alert = {
|
|
583
|
+
id: alertId,
|
|
584
|
+
severity,
|
|
585
|
+
component,
|
|
586
|
+
message,
|
|
587
|
+
timestamp: now,
|
|
588
|
+
acknowledged: false,
|
|
589
|
+
count: 1,
|
|
590
|
+
firstSeen: now
|
|
591
|
+
};
|
|
592
|
+
this.activeAlerts.set(alertId, alert);
|
|
593
|
+
// Store in database
|
|
594
|
+
await this.memory.insert('monitoring_alerts', {
|
|
595
|
+
id: alertId,
|
|
596
|
+
severity,
|
|
597
|
+
component,
|
|
598
|
+
message,
|
|
599
|
+
timestamp: now.getTime(),
|
|
600
|
+
acknowledged: false,
|
|
601
|
+
count: 1,
|
|
602
|
+
first_seen: now.getTime(),
|
|
603
|
+
last_seen: now.getTime(),
|
|
604
|
+
metadata: JSON.stringify(metadata || {})
|
|
605
|
+
});
|
|
606
|
+
this.logger.warn(`🚨 Alert created: ${severity.toUpperCase()} - ${component} - ${message}`, { alertId });
|
|
607
|
+
// Emit alert event
|
|
608
|
+
this.emit('alert', alert);
|
|
609
|
+
// Broadcast to connected clients
|
|
610
|
+
this.broadcastToClients({
|
|
611
|
+
type: 'new_alert',
|
|
612
|
+
data: alert
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
async cleanupOldAlerts() {
|
|
616
|
+
const cutoff = Date.now() - (24 * 60 * 60 * 1000); // 24 hours
|
|
617
|
+
for (const [alertId, alert] of this.activeAlerts) {
|
|
618
|
+
if (alert.timestamp.getTime() < cutoff && alert.acknowledged) {
|
|
619
|
+
this.activeAlerts.delete(alertId);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async runPredictiveAnalysis() {
|
|
624
|
+
this.logger.debug('🔮 Running predictive analysis...');
|
|
625
|
+
const predictions = [];
|
|
626
|
+
// Analyze memory growth
|
|
627
|
+
const memHistory = this.metricHistory.get('memory_process_memory_heap_used') || [];
|
|
628
|
+
if (memHistory.length > 20) {
|
|
629
|
+
const memoryPrediction = this.predictResourceExhaustion('memory', memHistory, 1000); // 1GB limit
|
|
630
|
+
if (memoryPrediction) {
|
|
631
|
+
predictions.push(memoryPrediction);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
// Analyze database growth
|
|
635
|
+
const dbHistory = this.metricHistory.get('database_database_size') || [];
|
|
636
|
+
if (dbHistory.length > 20) {
|
|
637
|
+
const dbPrediction = this.predictResourceExhaustion('database', dbHistory, 5000); // 5GB limit
|
|
638
|
+
if (dbPrediction) {
|
|
639
|
+
predictions.push(dbPrediction);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
// Store predictions
|
|
643
|
+
for (const prediction of predictions) {
|
|
644
|
+
await this.memory.insert('monitoring_predictions', {
|
|
645
|
+
id: `prediction_${prediction.type}_${prediction.component}_${Date.now()}`,
|
|
646
|
+
prediction_type: prediction.type,
|
|
647
|
+
component: prediction.component,
|
|
648
|
+
description: prediction.description,
|
|
649
|
+
probability: prediction.probability,
|
|
650
|
+
estimated_time: prediction.estimatedTime?.getTime(),
|
|
651
|
+
impact: prediction.impact,
|
|
652
|
+
recommendations: JSON.stringify(prediction.recommendations),
|
|
653
|
+
confidence: prediction.probability
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
// Update system health with predictions
|
|
657
|
+
this.systemHealth.predictedIssues = predictions;
|
|
658
|
+
}
|
|
659
|
+
predictResourceExhaustion(resource, history, limit) {
|
|
660
|
+
if (history.length < 10)
|
|
661
|
+
return null;
|
|
662
|
+
// Simple linear regression to predict growth
|
|
663
|
+
const recentHistory = history.slice(-20);
|
|
664
|
+
const n = recentHistory.length;
|
|
665
|
+
const x = Array.from({ length: n }, (_, i) => i);
|
|
666
|
+
const y = recentHistory;
|
|
667
|
+
const sumX = x.reduce((a, b) => a + b, 0);
|
|
668
|
+
const sumY = y.reduce((a, b) => a + b, 0);
|
|
669
|
+
const sumXY = x.reduce((sum, xi, i) => sum + xi * y[i], 0);
|
|
670
|
+
const sumXX = x.reduce((sum, xi) => sum + xi * xi, 0);
|
|
671
|
+
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
|
|
672
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
673
|
+
// Predict when resource will reach limit
|
|
674
|
+
if (slope > 0) {
|
|
675
|
+
const currentValue = y[y.length - 1];
|
|
676
|
+
const timeToLimit = (limit - currentValue) / slope;
|
|
677
|
+
if (timeToLimit > 0 && timeToLimit < 1000) { // Within 1000 time units
|
|
678
|
+
const probability = Math.min(0.9, Math.max(0.1, 1 - (timeToLimit / 1000)));
|
|
679
|
+
const estimatedTime = new Date(Date.now() + (timeToLimit * 300000)); // 5-minute intervals
|
|
680
|
+
return {
|
|
681
|
+
type: 'resource_exhaustion',
|
|
682
|
+
component: resource,
|
|
683
|
+
description: `${resource} usage is growing and may reach capacity limits`,
|
|
684
|
+
probability,
|
|
685
|
+
estimatedTime,
|
|
686
|
+
impact: timeToLimit < 100 ? 'critical' : timeToLimit < 300 ? 'high' : 'medium',
|
|
687
|
+
recommendations: [
|
|
688
|
+
`Monitor ${resource} usage closely`,
|
|
689
|
+
`Consider optimizing ${resource} consumption`,
|
|
690
|
+
'Implement cleanup/archival processes',
|
|
691
|
+
'Scale resources if necessary'
|
|
692
|
+
]
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
async calculateTrend(metric, period) {
|
|
699
|
+
const timeframe = {
|
|
700
|
+
hour: 3600000,
|
|
701
|
+
day: 86400000,
|
|
702
|
+
week: 604800000
|
|
703
|
+
}[period];
|
|
704
|
+
const since = Date.now() - timeframe;
|
|
705
|
+
const metrics = await this.memory.query(`
|
|
706
|
+
SELECT metric_value, timestamp FROM monitoring_metrics
|
|
707
|
+
WHERE metric_name = ? AND timestamp > ?
|
|
708
|
+
ORDER BY timestamp
|
|
709
|
+
`, [metric, since]);
|
|
710
|
+
if (metrics.length < 10)
|
|
711
|
+
return null;
|
|
712
|
+
const values = metrics.map(m => m.metric_value);
|
|
713
|
+
const recent = values.slice(-Math.floor(values.length / 3));
|
|
714
|
+
const older = values.slice(0, Math.floor(values.length / 3));
|
|
715
|
+
const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length;
|
|
716
|
+
const olderAvg = older.reduce((a, b) => a + b, 0) / older.length;
|
|
717
|
+
const overallAvg = values.reduce((a, b) => a + b, 0) / values.length;
|
|
718
|
+
const percentageChange = ((recentAvg - olderAvg) / olderAvg) * 100;
|
|
719
|
+
const variance = values.reduce((sum, val) => sum + Math.pow(val - overallAvg, 2), 0) / values.length;
|
|
720
|
+
const volatility = Math.sqrt(variance) / overallAvg;
|
|
721
|
+
return {
|
|
722
|
+
metric,
|
|
723
|
+
period,
|
|
724
|
+
trend: Math.abs(percentageChange) < 5 ? 'stable' :
|
|
725
|
+
percentageChange > 0 ? 'degrading' : 'improving',
|
|
726
|
+
percentageChange,
|
|
727
|
+
currentValue: recentAvg,
|
|
728
|
+
historicalAverage: overallAvg,
|
|
729
|
+
volatility
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
async generateResourceForecast(resource, timeHorizonHours) {
|
|
733
|
+
const history = this.metricHistory.get(`${resource}_${resource}_usage`) ||
|
|
734
|
+
this.metricHistory.get(`${resource}_process_memory_heap_used`) ||
|
|
735
|
+
this.metricHistory.get(`${resource}_database_size`) || [];
|
|
736
|
+
if (history.length < 10)
|
|
737
|
+
return null;
|
|
738
|
+
const currentUsage = history[history.length - 1] || 0;
|
|
739
|
+
const trend = this.calculateTrend(resource, history);
|
|
740
|
+
// Simple prediction based on trend
|
|
741
|
+
let predictedUsage = currentUsage;
|
|
742
|
+
let confidence = 0.5;
|
|
743
|
+
if (trend === 'degrading') {
|
|
744
|
+
const growthRate = this.calculateGrowthRate(history);
|
|
745
|
+
predictedUsage = currentUsage * (1 + (growthRate * timeHorizonHours / 24));
|
|
746
|
+
confidence = 0.7;
|
|
747
|
+
}
|
|
748
|
+
else if (trend === 'improving') {
|
|
749
|
+
const reductionRate = this.calculateGrowthRate(history);
|
|
750
|
+
predictedUsage = currentUsage * (1 + (reductionRate * timeHorizonHours / 24));
|
|
751
|
+
confidence = 0.6;
|
|
752
|
+
}
|
|
753
|
+
const recommendations = [];
|
|
754
|
+
let exhaustionDate;
|
|
755
|
+
if (predictedUsage > currentUsage * 1.5) {
|
|
756
|
+
recommendations.push(`${resource} usage expected to increase significantly`);
|
|
757
|
+
recommendations.push('Consider implementing resource optimization');
|
|
758
|
+
}
|
|
759
|
+
if (predictedUsage > 1000) { // Arbitrary threshold
|
|
760
|
+
exhaustionDate = new Date(Date.now() + (timeHorizonHours * 3600000));
|
|
761
|
+
recommendations.push('Resource exhaustion predicted - immediate action required');
|
|
762
|
+
}
|
|
763
|
+
return {
|
|
764
|
+
resource,
|
|
765
|
+
currentUsage,
|
|
766
|
+
predictedUsage,
|
|
767
|
+
timeHorizon: timeHorizonHours,
|
|
768
|
+
confidence,
|
|
769
|
+
exhaustionDate,
|
|
770
|
+
recommendations
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
calculateGrowthRate(history) {
|
|
774
|
+
if (history.length < 5)
|
|
775
|
+
return 0;
|
|
776
|
+
const recent = history.slice(-5);
|
|
777
|
+
const older = history.slice(-10, -5);
|
|
778
|
+
const recentAvg = recent.reduce((a, b) => a + b) / recent.length;
|
|
779
|
+
const olderAvg = older.reduce((a, b) => a + b) / older.length;
|
|
780
|
+
return (recentAvg - olderAvg) / olderAvg;
|
|
781
|
+
}
|
|
782
|
+
calculatePerformanceScore(performanceReport, systemHealth) {
|
|
783
|
+
let score = 100;
|
|
784
|
+
// Deduct points for errors
|
|
785
|
+
score -= performanceReport.summary.errorRate * 2;
|
|
786
|
+
// Deduct points for slow response times
|
|
787
|
+
if (performanceReport.summary.averageResponseTime > 2000) {
|
|
788
|
+
score -= (performanceReport.summary.averageResponseTime - 2000) / 100;
|
|
789
|
+
}
|
|
790
|
+
// Deduct points for unhealthy components
|
|
791
|
+
const unhealthyComponents = Object.values(systemHealth.components)
|
|
792
|
+
.filter(c => c.status !== 'healthy').length;
|
|
793
|
+
score -= unhealthyComponents * 10;
|
|
794
|
+
// Deduct points for critical alerts
|
|
795
|
+
const criticalAlerts = systemHealth.alerts.filter(a => a.severity === 'critical').length;
|
|
796
|
+
score -= criticalAlerts * 15;
|
|
797
|
+
return Math.max(0, Math.min(100, score));
|
|
798
|
+
}
|
|
799
|
+
async generateSmartRecommendations() {
|
|
800
|
+
const recommendations = [];
|
|
801
|
+
// Analyze system health and generate recommendations
|
|
802
|
+
for (const [componentName, component] of Object.entries(this.systemHealth.components)) {
|
|
803
|
+
if (component.status !== 'healthy') {
|
|
804
|
+
if (componentName === 'memory' && component.trend === 'degrading') {
|
|
805
|
+
recommendations.push('Consider implementing memory optimization strategies');
|
|
806
|
+
recommendations.push('Review memory leaks in long-running processes');
|
|
807
|
+
}
|
|
808
|
+
if (componentName === 'cpu' && component.value > 80) {
|
|
809
|
+
recommendations.push('CPU usage is high - consider scaling or optimization');
|
|
810
|
+
}
|
|
811
|
+
if (componentName === 'cache' && component.value < 50) {
|
|
812
|
+
recommendations.push('Cache hit rate is low - review caching strategy');
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// Add predictive recommendations
|
|
817
|
+
for (const prediction of this.systemHealth.predictedIssues) {
|
|
818
|
+
recommendations.push(...prediction.recommendations);
|
|
819
|
+
}
|
|
820
|
+
return [...new Set(recommendations)]; // Remove duplicates
|
|
821
|
+
}
|
|
822
|
+
async getDataPointCount(period) {
|
|
823
|
+
const timeframe = {
|
|
824
|
+
hour: 3600000,
|
|
825
|
+
day: 86400000,
|
|
826
|
+
week: 604800000,
|
|
827
|
+
month: 2592000000
|
|
828
|
+
}[period] || 86400000;
|
|
829
|
+
const since = Date.now() - timeframe;
|
|
830
|
+
const result = await this.memory.query(`
|
|
831
|
+
SELECT COUNT(*) as count FROM monitoring_metrics
|
|
832
|
+
WHERE timestamp > ?
|
|
833
|
+
`, [since]);
|
|
834
|
+
return result[0]?.count || 0;
|
|
835
|
+
}
|
|
836
|
+
handleWebSocketConnection(ws) {
|
|
837
|
+
this.connectedClients.add(ws);
|
|
838
|
+
this.logger.info('📱 WebSocket client connected', { totalClients: this.connectedClients.size });
|
|
839
|
+
// Send initial system health
|
|
840
|
+
ws.send(JSON.stringify({
|
|
841
|
+
type: 'initial_data',
|
|
842
|
+
data: {
|
|
843
|
+
systemHealth: this.systemHealth,
|
|
844
|
+
timestamp: new Date()
|
|
845
|
+
}
|
|
846
|
+
}));
|
|
847
|
+
ws.on('close', () => {
|
|
848
|
+
this.connectedClients.delete(ws);
|
|
849
|
+
this.logger.info('📱 WebSocket client disconnected', { totalClients: this.connectedClients.size });
|
|
850
|
+
});
|
|
851
|
+
ws.on('message', async (message) => {
|
|
852
|
+
try {
|
|
853
|
+
const data = JSON.parse(message.toString());
|
|
854
|
+
await this.handleWebSocketMessage(ws, data);
|
|
855
|
+
}
|
|
856
|
+
catch (error) {
|
|
857
|
+
this.logger.error('❌ Error handling WebSocket message:', error);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
async handleWebSocketMessage(ws, message) {
|
|
862
|
+
switch (message.type) {
|
|
863
|
+
case 'acknowledge_alert':
|
|
864
|
+
if (message.alertId && message.user) {
|
|
865
|
+
const success = await this.acknowledgeAlert(message.alertId, message.user);
|
|
866
|
+
ws.send(JSON.stringify({
|
|
867
|
+
type: 'alert_acknowledged',
|
|
868
|
+
success,
|
|
869
|
+
alertId: message.alertId
|
|
870
|
+
}));
|
|
871
|
+
}
|
|
872
|
+
break;
|
|
873
|
+
case 'get_performance_trends':
|
|
874
|
+
const trends = await this.getPerformanceTrends(message.periods || ['day']);
|
|
875
|
+
ws.send(JSON.stringify({
|
|
876
|
+
type: 'performance_trends',
|
|
877
|
+
data: trends
|
|
878
|
+
}));
|
|
879
|
+
break;
|
|
880
|
+
case 'get_resource_forecasts':
|
|
881
|
+
const forecasts = await this.getResourceForecasts(message.timeHorizon || 24);
|
|
882
|
+
ws.send(JSON.stringify({
|
|
883
|
+
type: 'resource_forecasts',
|
|
884
|
+
data: forecasts
|
|
885
|
+
}));
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
handleHttpRequest(req, res) {
|
|
890
|
+
// Serve basic monitoring dashboard
|
|
891
|
+
const dashboardHtml = this.generateDashboardHtml();
|
|
892
|
+
res.writeHead(200, {
|
|
893
|
+
'Content-Type': 'text/html',
|
|
894
|
+
'Content-Length': Buffer.byteLength(dashboardHtml)
|
|
895
|
+
});
|
|
896
|
+
res.end(dashboardHtml);
|
|
897
|
+
}
|
|
898
|
+
generateDashboardHtml() {
|
|
899
|
+
return `
|
|
900
|
+
<!DOCTYPE html>
|
|
901
|
+
<html>
|
|
902
|
+
<head>
|
|
903
|
+
<title>Snow-Flow Enhanced Monitoring Dashboard</title>
|
|
904
|
+
<style>
|
|
905
|
+
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
|
|
906
|
+
.header { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
|
|
907
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
|
|
908
|
+
.card { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
|
|
909
|
+
.status-healthy { color: #27ae60; }
|
|
910
|
+
.status-warning { color: #f39c12; }
|
|
911
|
+
.status-critical { color: #e74c3c; }
|
|
912
|
+
.status-unknown { color: #95a5a6; }
|
|
913
|
+
.metric { display: flex; justify-content: space-between; margin: 10px 0; }
|
|
914
|
+
.alert { padding: 10px; margin: 5px 0; border-radius: 3px; }
|
|
915
|
+
.alert-critical { background: #ffebee; border-left: 4px solid #e74c3c; }
|
|
916
|
+
.alert-high { background: #fff3e0; border-left: 4px solid #ff9800; }
|
|
917
|
+
.alert-medium { background: #f3e5f5; border-left: 4px solid #9c27b0; }
|
|
918
|
+
.alert-low { background: #e8f5e8; border-left: 4px solid #4caf50; }
|
|
919
|
+
#connectionStatus { position: fixed; top: 10px; right: 10px; padding: 5px 10px; border-radius: 3px; }
|
|
920
|
+
.connected { background: #4caf50; color: white; }
|
|
921
|
+
.disconnected { background: #f44336; color: white; }
|
|
922
|
+
</style>
|
|
923
|
+
</head>
|
|
924
|
+
<body>
|
|
925
|
+
<div class="header">
|
|
926
|
+
<h1>🚀 Snow-Flow Enhanced Monitoring Dashboard</h1>
|
|
927
|
+
<p>Real-time system health and performance monitoring</p>
|
|
928
|
+
<div id="connectionStatus" class="disconnected">Connecting...</div>
|
|
929
|
+
</div>
|
|
930
|
+
|
|
931
|
+
<div class="grid">
|
|
932
|
+
<div class="card">
|
|
933
|
+
<h3>System Health Overview</h3>
|
|
934
|
+
<div id="overallHealth">Loading...</div>
|
|
935
|
+
<div id="componentHealth"></div>
|
|
936
|
+
</div>
|
|
937
|
+
|
|
938
|
+
<div class="card">
|
|
939
|
+
<h3>Active Alerts</h3>
|
|
940
|
+
<div id="alerts">Loading...</div>
|
|
941
|
+
</div>
|
|
942
|
+
|
|
943
|
+
<div class="card">
|
|
944
|
+
<h3>Performance Metrics</h3>
|
|
945
|
+
<div id="performanceMetrics">Loading...</div>
|
|
946
|
+
</div>
|
|
947
|
+
|
|
948
|
+
<div class="card">
|
|
949
|
+
<h3>Resource Usage</h3>
|
|
950
|
+
<div id="resourceUsage">Loading...</div>
|
|
951
|
+
</div>
|
|
952
|
+
</div>
|
|
953
|
+
|
|
954
|
+
<script>
|
|
955
|
+
const ws = new WebSocket('ws://localhost:${this.config.websocketPort}/monitoring-ws');
|
|
956
|
+
const connectionStatus = document.getElementById('connectionStatus');
|
|
957
|
+
|
|
958
|
+
ws.onopen = function() {
|
|
959
|
+
connectionStatus.textContent = 'Connected';
|
|
960
|
+
connectionStatus.className = 'connected';
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
ws.onclose = function() {
|
|
964
|
+
connectionStatus.textContent = 'Disconnected';
|
|
965
|
+
connectionStatus.className = 'disconnected';
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
ws.onmessage = function(event) {
|
|
969
|
+
const message = JSON.parse(event.data);
|
|
970
|
+
|
|
971
|
+
switch(message.type) {
|
|
972
|
+
case 'initial_data':
|
|
973
|
+
case 'system_health_update':
|
|
974
|
+
updateSystemHealth(message.data.systemHealth || message.data);
|
|
975
|
+
break;
|
|
976
|
+
case 'new_alert':
|
|
977
|
+
addAlert(message.data);
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
function updateSystemHealth(health) {
|
|
983
|
+
// Update overall health
|
|
984
|
+
const overallHealth = document.getElementById('overallHealth');
|
|
985
|
+
overallHealth.innerHTML = \`
|
|
986
|
+
<div class="metric">
|
|
987
|
+
<span>Overall Status:</span>
|
|
988
|
+
<span class="status-\${health.overall}">\${health.overall.toUpperCase()}</span>
|
|
989
|
+
</div>
|
|
990
|
+
\`;
|
|
991
|
+
|
|
992
|
+
// Update component health
|
|
993
|
+
const componentHealth = document.getElementById('componentHealth');
|
|
994
|
+
let componentsHtml = '';
|
|
995
|
+
|
|
996
|
+
for (const [name, component] of Object.entries(health.components)) {
|
|
997
|
+
componentsHtml += \`
|
|
998
|
+
<div class="metric">
|
|
999
|
+
<span>\${name.charAt(0).toUpperCase() + name.slice(1)}:</span>
|
|
1000
|
+
<span class="status-\${component.status}">
|
|
1001
|
+
\${component.value.toFixed(1)} (\${component.status})
|
|
1002
|
+
</span>
|
|
1003
|
+
</div>
|
|
1004
|
+
\`;
|
|
1005
|
+
}
|
|
1006
|
+
componentHealth.innerHTML = componentsHtml;
|
|
1007
|
+
|
|
1008
|
+
// Update alerts
|
|
1009
|
+
updateAlerts(health.alerts || []);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function updateAlerts(alerts) {
|
|
1013
|
+
const alertsDiv = document.getElementById('alerts');
|
|
1014
|
+
|
|
1015
|
+
if (alerts.length === 0) {
|
|
1016
|
+
alertsDiv.innerHTML = '<p>No active alerts</p>';
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
let alertsHtml = '';
|
|
1021
|
+
alerts.slice(0, 10).forEach(alert => {
|
|
1022
|
+
alertsHtml += \`
|
|
1023
|
+
<div class="alert alert-\${alert.severity}">
|
|
1024
|
+
<strong>\${alert.component.toUpperCase()}</strong>: \${alert.message}
|
|
1025
|
+
<br><small>\${new Date(alert.timestamp).toLocaleString()}</small>
|
|
1026
|
+
</div>
|
|
1027
|
+
\`;
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
alertsDiv.innerHTML = alertsHtml;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function addAlert(alert) {
|
|
1034
|
+
// Add new alert to the top of the list
|
|
1035
|
+
const alertsDiv = document.getElementById('alerts');
|
|
1036
|
+
const newAlertHtml = \`
|
|
1037
|
+
<div class="alert alert-\${alert.severity}">
|
|
1038
|
+
<strong>\${alert.component.toUpperCase()}</strong>: \${alert.message}
|
|
1039
|
+
<br><small>\${new Date(alert.timestamp).toLocaleString()}</small>
|
|
1040
|
+
</div>
|
|
1041
|
+
\`;
|
|
1042
|
+
|
|
1043
|
+
alertsDiv.innerHTML = newAlertHtml + alertsDiv.innerHTML;
|
|
1044
|
+
}
|
|
1045
|
+
</script>
|
|
1046
|
+
</body>
|
|
1047
|
+
</html>
|
|
1048
|
+
`;
|
|
1049
|
+
}
|
|
1050
|
+
broadcastToClients(message) {
|
|
1051
|
+
const messageStr = JSON.stringify(message);
|
|
1052
|
+
this.connectedClients.forEach(client => {
|
|
1053
|
+
if (client.readyState === ws_1.default.OPEN) {
|
|
1054
|
+
client.send(messageStr);
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Shutdown the monitoring system
|
|
1060
|
+
*/
|
|
1061
|
+
async shutdown() {
|
|
1062
|
+
this.logger.info('🛑 Shutting down Enhanced Monitoring System...');
|
|
1063
|
+
// Clear intervals
|
|
1064
|
+
if (this.monitoringInterval)
|
|
1065
|
+
clearInterval(this.monitoringInterval);
|
|
1066
|
+
if (this.alertingInterval)
|
|
1067
|
+
clearInterval(this.alertingInterval);
|
|
1068
|
+
if (this.predictionInterval)
|
|
1069
|
+
clearInterval(this.predictionInterval);
|
|
1070
|
+
// Close WebSocket connections
|
|
1071
|
+
this.connectedClients.forEach(client => {
|
|
1072
|
+
client.close();
|
|
1073
|
+
});
|
|
1074
|
+
this.connectedClients.clear();
|
|
1075
|
+
// Close servers
|
|
1076
|
+
if (this.websocketServer) {
|
|
1077
|
+
this.websocketServer.close();
|
|
1078
|
+
}
|
|
1079
|
+
if (this.httpServer) {
|
|
1080
|
+
this.httpServer.close();
|
|
1081
|
+
}
|
|
1082
|
+
this.logger.info('✅ Enhanced Monitoring System shut down');
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
exports.EnhancedMonitoringSystem = EnhancedMonitoringSystem;
|