claw-dashboard 1.8.4 → 2.0.0
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/.c8rc.json +38 -0
- package/.dockerignore +68 -0
- package/.github/dependabot.yml +45 -0
- package/.github/pull_request_template.md +39 -0
- package/.github/workflows/ci.yml +147 -0
- package/.github/workflows/release.yml +40 -0
- package/.github/workflows/security.yml +84 -0
- package/.husky/pre-commit +1 -0
- package/.planning/codebase/ARCHITECTURE.md +206 -0
- package/.planning/codebase/INTEGRATIONS.md +150 -0
- package/.planning/codebase/STACK.md +122 -0
- package/.planning/codebase/STRUCTURE.md +201 -0
- package/CHANGELOG.md +293 -0
- package/CONTRIBUTING.md +378 -0
- package/Dockerfile +54 -0
- package/FEATURES.md +195 -21
- package/README.md +83 -6
- package/TODO.md +28 -0
- package/build-cjs.js +127 -0
- package/cjs-shim.js +13 -0
- package/dist/clawdash +1729 -0
- package/dist/clawdash.meta.json +2236 -0
- package/dist/widgets.cjs +6511 -0
- package/docker-compose.yml +67 -0
- package/docs/API.md +1050 -0
- package/docs/PLUGINS.md +1504 -0
- package/esbuild.config.js +158 -0
- package/eslint.config.js +56 -0
- package/examples/plugins/README.md +122 -0
- package/examples/plugins/api-status/index.js +294 -0
- package/examples/plugins/api-status/plugin.json +19 -0
- package/examples/plugins/hello-world/index.js +104 -0
- package/examples/plugins/hello-world/plugin.json +15 -0
- package/examples/plugins/system-metrics-chart/index.js +339 -0
- package/examples/plugins/system-metrics-chart/plugin.json +18 -0
- package/examples/plugins/weather-widget/index.js +136 -0
- package/examples/plugins/weather-widget/plugin.json +19 -0
- package/index.cjs +23005 -0
- package/index.js +5331 -512
- package/jest.config.js +11 -0
- package/man/clawdash.1 +276 -0
- package/package.json +54 -5
- package/schemas/plugin-manifest.json +128 -0
- package/scripts/release.js +595 -0
- package/src/alerts.js +693 -0
- package/src/auto-save.js +584 -0
- package/src/cache.js +390 -0
- package/src/checksum.js +146 -0
- package/src/cli/args.js +110 -0
- package/src/cli/export-schedule.js +423 -0
- package/src/cli/export-snapshot.js +138 -0
- package/src/cli/help.js +69 -0
- package/src/cli/import-snapshot.js +230 -0
- package/src/cli/index.js +14 -0
- package/src/cli/list-templates.js +69 -0
- package/src/cli/validate-config.js +76 -0
- package/src/cli/validate-plugin.js +187 -0
- package/src/cli/version.js +15 -0
- package/src/config-validator.js +586 -0
- package/src/config-watcher.js +401 -0
- package/src/config.js +684 -0
- package/src/container-detector.js +499 -0
- package/src/database.js +734 -0
- package/src/differential-render.js +327 -0
- package/src/errors.js +169 -0
- package/src/export-scheduler.js +581 -0
- package/src/gateway-manager.js +685 -0
- package/src/hints.js +371 -0
- package/src/loading-states.js +509 -0
- package/src/logger.js +185 -0
- package/src/memory-pressure.js +467 -0
- package/src/performance-monitor.js +374 -0
- package/src/plugin-errors.js +586 -0
- package/src/plugin-manifest-validator.js +219 -0
- package/src/plugin-reload.js +481 -0
- package/src/plugin-scaffold.js +1941 -0
- package/src/plugin-validator.js +284 -0
- package/src/retry.js +218 -0
- package/src/security.js +860 -0
- package/src/snapshot.js +372 -0
- package/src/splash.js +115 -0
- package/src/theme-selector.js +411 -0
- package/src/themes.js +874 -0
- package/src/transitions.js +534 -0
- package/src/types.d.ts +174 -0
- package/src/validation.js +926 -0
- package/src/web-server.js +885 -0
- package/src/widgets/builtin-widgets.js +1057 -0
- package/src/widgets/config-processor.js +401 -0
- package/src/widgets/dependency-resolver.js +596 -0
- package/src/widgets/index.js +79 -0
- package/src/widgets/plugin-api.js +845 -0
- package/src/widgets/widget-error-boundary.js +773 -0
- package/src/widgets/widget-error-isolation.js +551 -0
- package/src/widgets/widget-loader.js +1437 -0
- package/src/workers/system-worker.js +130 -0
- package/src/workers/worker-pool.js +633 -0
- package/tests/alerts.test.js +437 -0
- package/tests/auto-save.test.js +529 -0
- package/tests/cache.test.js +317 -0
- package/tests/cli.test.js +351 -0
- package/tests/command-palette.test.js +320 -0
- package/tests/config-processor.test.js +452 -0
- package/tests/config-validator.test.js +710 -0
- package/tests/config-watcher.test.js +594 -0
- package/tests/config.test.js +755 -0
- package/tests/database.test.js +438 -0
- package/tests/errors.test.js +189 -0
- package/tests/example-plugins.test.js +624 -0
- package/tests/integration.test.js +1321 -0
- package/tests/loading-states.test.js +300 -0
- package/tests/manifest-validation-on-load.test.js +671 -0
- package/tests/performance-monitor.test.js +190 -0
- package/tests/plugin-api-rate-limit.test.js +302 -0
- package/tests/plugin-errors.test.js +311 -0
- package/tests/plugin-lifecycle-e2e.test.js +1036 -0
- package/tests/plugin-reload.test.js +764 -0
- package/tests/rate-limiter.test.js +539 -0
- package/tests/retry.test.js +308 -0
- package/tests/security.test.js +411 -0
- package/tests/settings-modal.test.js +226 -0
- package/tests/theme-selector.test.js +57 -0
- package/tests/utils.js +242 -0
- package/tests/utils.test.js +317 -0
- package/tests/validate-plugin-cli.test.js +359 -0
- package/tests/validation.test.js +837 -0
- package/tests/web-server.test.js +646 -0
- package/tests/widget-arrange-mode.test.js +183 -0
- package/tests/widget-config-hot-reload.test.js +465 -0
- package/tests/widget-dependency.test.js +591 -0
- package/tests/widget-error-boundary.test.js +749 -0
- package/tests/widget-error-isolation.test.js +469 -0
- package/tests/widget-integration.test.js +1147 -0
- package/tests/widget-refresh-intervals.test.js +284 -0
- package/tests/worker-pool.test.js +522 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Monitor Module
|
|
3
|
+
* Tracks dashboard performance metrics including refresh rates, memory usage, and worker pool stats
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import si from 'systeminformation';
|
|
8
|
+
import logger from './logger.js';
|
|
9
|
+
import memoryPressure, { MemoryPressureDetector } from './memory-pressure.js';
|
|
10
|
+
|
|
11
|
+
// Worker pool reference (set via setWorkerPool)
|
|
12
|
+
let workerPoolRef = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set the worker pool reference for metrics tracking
|
|
16
|
+
* @param {Object} pool - Worker pool instance
|
|
17
|
+
*/
|
|
18
|
+
export function setWorkerPool(pool) {
|
|
19
|
+
workerPoolRef = pool;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get worker pool metrics
|
|
24
|
+
* @returns {Object|null} Worker pool status or null if not available
|
|
25
|
+
*/
|
|
26
|
+
export function getWorkerPoolMetrics() {
|
|
27
|
+
if (!workerPoolRef) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return workerPoolRef.getStatus();
|
|
32
|
+
} catch (error) {
|
|
33
|
+
logger.debug('Failed to get worker pool metrics:', error.message);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Performance metrics snapshot
|
|
40
|
+
* @typedef {Object} PerformanceSnapshot
|
|
41
|
+
* @property {number} timestamp - Unix timestamp
|
|
42
|
+
* @property {number} refreshRate - Current refresh rate in ms
|
|
43
|
+
* @property {number} memoryUsed - Heap used in MB
|
|
44
|
+
* @property {number} memoryTotal - Heap total in MB
|
|
45
|
+
* @property {number} memoryPercent - Memory usage percentage
|
|
46
|
+
* @property {number} cpuPercent - CPU usage percentage (process-specific)
|
|
47
|
+
* @property {number} eventLoopLag - Event loop lag in ms
|
|
48
|
+
* @property {number} uptime - Process uptime in seconds
|
|
49
|
+
* @property {Object} operationTimings - Timing details for each operation
|
|
50
|
+
* @property {string|null} slowOperations - Summary of slow operations
|
|
51
|
+
* @property {number} totalRefreshTime - Total refresh cycle time in ms
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
class PerformanceMonitor {
|
|
55
|
+
constructor() {
|
|
56
|
+
/** @type {PerformanceSnapshot[]} */
|
|
57
|
+
this.history = [];
|
|
58
|
+
this.maxHistory = 60; // 2 minutes at 2s refresh
|
|
59
|
+
this.lastCheck = Date.now();
|
|
60
|
+
this.lastCPUUsage = process.cpuUsage();
|
|
61
|
+
this.isTracking = false;
|
|
62
|
+
|
|
63
|
+
// Memory pressure detector
|
|
64
|
+
this.memoryPressure = memoryPressure;
|
|
65
|
+
this.enableMemoryPressure = true;
|
|
66
|
+
|
|
67
|
+
// Metrics
|
|
68
|
+
this.metrics = {
|
|
69
|
+
avgRefreshRate: 0,
|
|
70
|
+
avgMemoryUsed: 0,
|
|
71
|
+
peakMemoryUsed: 0,
|
|
72
|
+
avgHeapUsed: 0,
|
|
73
|
+
peakHeapUsed: 0,
|
|
74
|
+
avgCpuPercent: 0,
|
|
75
|
+
avgEventLoopLag: 0,
|
|
76
|
+
slowOperations: null,
|
|
77
|
+
totalRefreshTime: 0,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Start performance tracking
|
|
83
|
+
*/
|
|
84
|
+
start() {
|
|
85
|
+
this.isTracking = true;
|
|
86
|
+
this.lastCheck = Date.now();
|
|
87
|
+
this.lastCPUUsage = process.cpuUsage();
|
|
88
|
+
memoryPressure.start();
|
|
89
|
+
logger.debug('Performance monitoring started');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Stop performance tracking
|
|
94
|
+
*/
|
|
95
|
+
stop() {
|
|
96
|
+
this.isTracking = false;
|
|
97
|
+
memoryPressure.stop();
|
|
98
|
+
logger.debug('Performance monitoring stopped');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Record a performance snapshot
|
|
103
|
+
* @param {number} refreshRate - Current refresh interval in ms
|
|
104
|
+
* @param {Object} timingDetails - Optional timing details from refresh operations
|
|
105
|
+
* @returns {PerformanceSnapshot}
|
|
106
|
+
*/
|
|
107
|
+
async record(refreshRate = 2000, timingDetails = {}) {
|
|
108
|
+
if (!this.isTracking) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
const memoryUsage = process.memoryUsage();
|
|
114
|
+
const cpuUsage = process.cpuUsage(this.lastCPUUsage);
|
|
115
|
+
|
|
116
|
+
// Get system memory for comparison
|
|
117
|
+
let systemMem = null;
|
|
118
|
+
try {
|
|
119
|
+
systemMem = await si.mem();
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// Fallback to process memory only
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Calculate CPU percentage (user + system time / elapsed time)
|
|
125
|
+
const elapsedMs = now - this.lastCheck;
|
|
126
|
+
const cpuPercent = elapsedMs > 0
|
|
127
|
+
? Math.min(100, ((cpuUsage.user + cpuUsage.system) / 1000) / elapsedMs * 100)
|
|
128
|
+
: 0;
|
|
129
|
+
|
|
130
|
+
// Calculate event loop lag (how much behind schedule we are)
|
|
131
|
+
const eventLoopLag = Math.max(0, now - this.lastCheck - refreshRate);
|
|
132
|
+
|
|
133
|
+
// Calculate system memory (excluding cache like Activity Monitor)
|
|
134
|
+
const systemUsed = systemMem
|
|
135
|
+
? (systemMem.available ? systemMem.total - systemMem.available : systemMem.used)
|
|
136
|
+
: memoryUsage.heapUsed;
|
|
137
|
+
const systemTotal = systemMem ? systemMem.total : memoryUsage.heapTotal;
|
|
138
|
+
const systemPercent = systemMem && systemMem.total > 0
|
|
139
|
+
? Math.round((systemUsed / systemMem.total) * 100)
|
|
140
|
+
: 0;
|
|
141
|
+
|
|
142
|
+
const snapshot = {
|
|
143
|
+
timestamp: now,
|
|
144
|
+
refreshRate,
|
|
145
|
+
// Node.js process memory (heap)
|
|
146
|
+
heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024),
|
|
147
|
+
heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024),
|
|
148
|
+
heapPercent: memoryUsage.heapTotal > 0
|
|
149
|
+
? Math.round((memoryUsage.heapUsed / memoryUsage.heapTotal) * 100)
|
|
150
|
+
: 0,
|
|
151
|
+
// System memory (what Activity Monitor shows)
|
|
152
|
+
memoryUsed: Math.round(systemUsed / 1024 / 1024),
|
|
153
|
+
memoryTotal: Math.round(systemTotal / 1024 / 1024),
|
|
154
|
+
memoryPercent: systemPercent,
|
|
155
|
+
cpuPercent: Math.round(cpuPercent * 10) / 10,
|
|
156
|
+
eventLoopLag: Math.round(eventLoopLag),
|
|
157
|
+
uptime: Math.floor(process.uptime()),
|
|
158
|
+
operationTimings: timingDetails.operationTimings || {},
|
|
159
|
+
slowOperations: timingDetails.slowOperations || null,
|
|
160
|
+
totalRefreshTime: timingDetails.totalRefreshTime || 0,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Add to history
|
|
164
|
+
this.history.push(snapshot);
|
|
165
|
+
if (this.history.length > this.maxHistory) {
|
|
166
|
+
this.history.shift();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Update cached metrics
|
|
170
|
+
this._updateMetrics();
|
|
171
|
+
|
|
172
|
+
// Update for next iteration
|
|
173
|
+
this.lastCheck = now;
|
|
174
|
+
this.lastCPUUsage = process.cpuUsage();
|
|
175
|
+
|
|
176
|
+
return snapshot;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Update cached aggregate metrics
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
_updateMetrics() {
|
|
184
|
+
if (this.history.length === 0) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const count = this.history.length;
|
|
189
|
+
const sum = (arr, key) => arr.reduce((a, b) => a + (b[key] || 0), 0);
|
|
190
|
+
|
|
191
|
+
// Get latest for slow operations
|
|
192
|
+
const latest = this.history[this.history.length - 1];
|
|
193
|
+
|
|
194
|
+
this.metrics = {
|
|
195
|
+
avgRefreshRate: Math.round(sum(this.history, 'refreshRate') / count),
|
|
196
|
+
// System memory (Activity Monitor style)
|
|
197
|
+
avgMemoryUsed: Math.round(sum(this.history, 'memoryUsed') / count),
|
|
198
|
+
peakMemoryUsed: Math.max(...this.history.map(h => h.memoryUsed)),
|
|
199
|
+
// Heap memory (Node.js process)
|
|
200
|
+
avgHeapUsed: Math.round(sum(this.history, 'heapUsed') / count),
|
|
201
|
+
peakHeapUsed: Math.max(...this.history.map(h => h.heapUsed)),
|
|
202
|
+
avgCpuPercent: Math.round((sum(this.history, 'cpuPercent') / count) * 10) / 10,
|
|
203
|
+
avgEventLoopLag: Math.round(sum(this.history, 'eventLoopLag') / count),
|
|
204
|
+
slowOperations: latest.slowOperations || null,
|
|
205
|
+
totalRefreshTime: latest.totalRefreshTime || 0,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get current metrics summary
|
|
211
|
+
* @returns {Object}
|
|
212
|
+
*/
|
|
213
|
+
getMetrics() {
|
|
214
|
+
const latest = this.history[this.history.length - 1] || null;
|
|
215
|
+
return {
|
|
216
|
+
current: latest,
|
|
217
|
+
history: [...this.history],
|
|
218
|
+
aggregates: { ...this.metrics },
|
|
219
|
+
isTracking: this.isTracking,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get formatted status string for display
|
|
225
|
+
* @param {boolean} detailed - Include detailed metrics
|
|
226
|
+
* @returns {string}
|
|
227
|
+
*/
|
|
228
|
+
getStatusString(detailed = false) {
|
|
229
|
+
const latest = this.history[this.history.length - 1];
|
|
230
|
+
if (!latest) {
|
|
231
|
+
return 'Performance monitoring inactive';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const memoryColor = latest.memoryPercent >= 80 ? 'red-fg' :
|
|
235
|
+
latest.memoryPercent >= 60 ? 'yellow-fg' : 'green-fg';
|
|
236
|
+
const cpuColor = latest.cpuPercent >= 80 ? 'red-fg' :
|
|
237
|
+
latest.cpuPercent >= 50 ? 'yellow-fg' : 'green-fg';
|
|
238
|
+
|
|
239
|
+
// Show system memory (not heap) in footer
|
|
240
|
+
let status = `{${memoryColor}}MEM: ${latest.memoryUsed}MB (${latest.memoryPercent}%){/${memoryColor}`;
|
|
241
|
+
|
|
242
|
+
// Add memory pressure indicator if elevated
|
|
243
|
+
if (this.enableMemoryPressure) {
|
|
244
|
+
const pressureStatus = memoryPressure.getStatusString();
|
|
245
|
+
if (memoryPressure.isElevated()) {
|
|
246
|
+
status += ` | ${pressureStatus}`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
status += ` | {${cpuColor}}CPU: ${latest.cpuPercent}%{/${cpuColor}}`;
|
|
251
|
+
status += ` | Refresh: ${latest.refreshRate}ms`;
|
|
252
|
+
|
|
253
|
+
if (detailed && this.metrics.avgEventLoopLag > 0) {
|
|
254
|
+
const lagColor = this.metrics.avgEventLoopLag > 100 ? 'red-fg' :
|
|
255
|
+
this.metrics.avgEventLoopLag > 50 ? 'yellow-fg' : 'gray-fg';
|
|
256
|
+
status += ` | {${lagColor}}Lag: ${this.metrics.avgEventLoopLag}ms{/${lagColor}}`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Add worker pool metrics when available
|
|
260
|
+
const workerMetrics = getWorkerPoolMetrics();
|
|
261
|
+
if (workerMetrics) {
|
|
262
|
+
const workerColor = workerMetrics.pendingTasks > 0 ? 'yellow-fg' : 'green-fg';
|
|
263
|
+
const busyCount = workerMetrics.busyWorkers || 0;
|
|
264
|
+
const totalCount = workerMetrics.totalWorkers || 0;
|
|
265
|
+
const pendingCount = workerMetrics.pendingTasks || 0;
|
|
266
|
+
status += ` | {${workerColor}}Workers: ${busyCount}/${totalCount}{/${workerColor}}`;
|
|
267
|
+
if (pendingCount > 0) {
|
|
268
|
+
status += ` ({yellow-fg}${pendingCount} pending{/${yellow-fg}})`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Add memory pressure info when detailed and pressure is elevated
|
|
273
|
+
if (detailed && this.enableMemoryPressure) {
|
|
274
|
+
const pressureStatus = memoryPressure.getStatus();
|
|
275
|
+
if (pressureStatus.currentLevel !== 'none') {
|
|
276
|
+
const pressureColor = pressureStatus.currentLevel === 'emergency' ? 'red-fg' :
|
|
277
|
+
pressureStatus.currentLevel === 'critical' ? 'red-fg' :
|
|
278
|
+
pressureStatus.currentLevel === 'warning' ? 'yellow-fg' : 'cyan-fg';
|
|
279
|
+
status += ` | {${pressureColor}}Pressure: ${pressureStatus.currentLevel}{/${pressureColor}}`;
|
|
280
|
+
if (pressureStatus.trend?.direction === 'growing') {
|
|
281
|
+
status += ` {yellow-fg}↑${pressureStatus.trend.rateMBPerMin.toFixed(0)}MB/min{/}`;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return status;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get memory usage sparkline data
|
|
291
|
+
* @param {number} points - Number of data points
|
|
292
|
+
* @returns {number[]}
|
|
293
|
+
*/
|
|
294
|
+
getMemorySparkline(points = 30) {
|
|
295
|
+
const data = this.history.slice(-points).map(h => h.memoryUsed);
|
|
296
|
+
return data.length > 0 ? data : [0];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get CPU usage sparkline data
|
|
301
|
+
* @param {number} points - Number of data points
|
|
302
|
+
* @returns {number[]}
|
|
303
|
+
*/
|
|
304
|
+
getCpuSparkline(points = 30) {
|
|
305
|
+
const data = this.history.slice(-points).map(h => h.cpuPercent);
|
|
306
|
+
return data.length > 0 ? data : [0];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Check if performance is degraded
|
|
311
|
+
* @returns {{degraded: boolean, reasons: string[]}}
|
|
312
|
+
*/
|
|
313
|
+
checkHealth() {
|
|
314
|
+
const reasons = [];
|
|
315
|
+
const latest = this.history[this.history.length - 1];
|
|
316
|
+
|
|
317
|
+
if (!latest) {
|
|
318
|
+
return { degraded: false, reasons: [] };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (latest.memoryPercent >= 85) {
|
|
322
|
+
reasons.push(`High memory usage: ${latest.memoryPercent}%`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (latest.cpuPercent >= 80) {
|
|
326
|
+
reasons.push(`High CPU usage: ${latest.cpuPercent}%`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (this.metrics.avgEventLoopLag > 100) {
|
|
330
|
+
reasons.push(`Event loop lag: ${this.metrics.avgEventLoopLag}ms`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Check memory pressure
|
|
334
|
+
const pressureState = memoryPressure.check();
|
|
335
|
+
if (pressureState.level !== 'none' && pressureState.level !== 'elevated') {
|
|
336
|
+
reasons.push(`Memory pressure: ${pressureState.level} (${pressureState.heapUsedMB}MB)`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
degraded: reasons.length > 0,
|
|
341
|
+
reasons,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Check memory pressure state
|
|
347
|
+
* @returns {import('./memory-pressure.js').PressureState}
|
|
348
|
+
*/
|
|
349
|
+
checkMemoryPressure() {
|
|
350
|
+
return memoryPressure.check();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Reset all metrics and history
|
|
355
|
+
*/
|
|
356
|
+
reset() {
|
|
357
|
+
this.history = [];
|
|
358
|
+
this.lastCheck = Date.now();
|
|
359
|
+
this.lastCPUUsage = process.cpuUsage();
|
|
360
|
+
this.metrics = {
|
|
361
|
+
avgRefreshRate: 0,
|
|
362
|
+
avgMemoryUsed: 0,
|
|
363
|
+
peakMemoryUsed: 0,
|
|
364
|
+
avgCpuPercent: 0,
|
|
365
|
+
avgEventLoopLag: 0,
|
|
366
|
+
};
|
|
367
|
+
memoryPressure.reset();
|
|
368
|
+
logger.debug('Performance metrics reset');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Export singleton instance
|
|
373
|
+
export default new PerformanceMonitor();
|
|
374
|
+
export { PerformanceMonitor };
|