chrome-cdp-cli 1.5.0 → 1.6.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/README.md +12 -0
- package/dist/cli/CLIApplication.js +21 -0
- package/dist/cli/CLIInterface.js +65 -1
- package/dist/cli/CommandRouter.js +1 -0
- package/dist/client/ProxyClient.js +254 -0
- package/dist/client/index.js +1 -0
- package/dist/handlers/EvaluateScriptHandler.js +126 -1
- package/dist/handlers/GetConsoleMessageHandler.js +80 -10
- package/dist/handlers/GetNetworkRequestHandler.js +68 -10
- package/dist/handlers/ListConsoleMessagesHandler.js +83 -12
- package/dist/handlers/ListNetworkRequestsHandler.js +67 -11
- package/dist/monitors/ConsoleMonitor.js +47 -0
- package/dist/proxy/ProxyManager.js +267 -0
- package/dist/proxy/index.js +60 -0
- package/dist/proxy/server/CDPEventMonitor.js +263 -0
- package/dist/proxy/server/CDPProxyServer.js +436 -0
- package/dist/proxy/server/ConnectionPool.js +430 -0
- package/dist/proxy/server/FileSystemSecurity.js +358 -0
- package/dist/proxy/server/HealthMonitor.js +242 -0
- package/dist/proxy/server/MessageStore.js +360 -0
- package/dist/proxy/server/PerformanceMonitor.js +277 -0
- package/dist/proxy/server/ProxyAPIServer.js +909 -0
- package/dist/proxy/server/SecurityManager.js +337 -0
- package/dist/proxy/server/WSProxy.js +456 -0
- package/dist/proxy/types/ProxyTypes.js +2 -0
- package/dist/utils/logger.js +256 -18
- package/package.json +7 -1
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MessageStore = void 0;
|
|
4
|
+
const logger_1 = require("../../utils/logger");
|
|
5
|
+
class MessageStore {
|
|
6
|
+
constructor(maxConsoleMessages = 1000, maxNetworkRequests = 500) {
|
|
7
|
+
this.consoleMessages = new Map();
|
|
8
|
+
this.networkRequests = new Map();
|
|
9
|
+
this.maxConsoleMessages = maxConsoleMessages;
|
|
10
|
+
this.maxNetworkRequests = maxNetworkRequests;
|
|
11
|
+
this.logger = (0, logger_1.createLogger)({ component: 'MessageStore' });
|
|
12
|
+
}
|
|
13
|
+
addConsoleMessage(connectionId, message) {
|
|
14
|
+
this.logger.info(`[DEBUG] addConsoleMessage called for connection ${connectionId}, message type: ${message.type}, text: ${message.text.substring(0, 100)}`);
|
|
15
|
+
if (!this.consoleMessages.has(connectionId)) {
|
|
16
|
+
this.consoleMessages.set(connectionId, []);
|
|
17
|
+
this.logger.info(`[DEBUG] Created new message array for connection ${connectionId}`);
|
|
18
|
+
}
|
|
19
|
+
const messages = this.consoleMessages.get(connectionId);
|
|
20
|
+
messages.push(message);
|
|
21
|
+
this.logger.debug(`Added console message for connection ${connectionId}`, {
|
|
22
|
+
connectionId,
|
|
23
|
+
messageType: message.type,
|
|
24
|
+
messageText: message.text.substring(0, 100),
|
|
25
|
+
totalMessages: messages.length
|
|
26
|
+
});
|
|
27
|
+
if (messages.length > this.maxConsoleMessages) {
|
|
28
|
+
const removed = messages.splice(0, messages.length - this.maxConsoleMessages);
|
|
29
|
+
this.logger.logMemoryEvent('limit-reached', `Console message limit reached for connection ${connectionId}`, {
|
|
30
|
+
connectionCount: 1,
|
|
31
|
+
messageCount: messages.length,
|
|
32
|
+
messagesRemoved: removed.length,
|
|
33
|
+
maxLimit: this.maxConsoleMessages
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
this.logger.debug(`Console message stored successfully`, {
|
|
37
|
+
connectionId,
|
|
38
|
+
messageType: message.type,
|
|
39
|
+
source: message.source
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
getConsoleMessages(connectionId, filter) {
|
|
43
|
+
const messages = this.consoleMessages.get(connectionId) || [];
|
|
44
|
+
if (!filter) {
|
|
45
|
+
return [...messages];
|
|
46
|
+
}
|
|
47
|
+
let filteredMessages = [...messages];
|
|
48
|
+
if (filter.types && filter.types.length > 0) {
|
|
49
|
+
filteredMessages = filteredMessages.filter(msg => filter.types.includes(msg.type));
|
|
50
|
+
}
|
|
51
|
+
if (filter.textPattern) {
|
|
52
|
+
const pattern = new RegExp(filter.textPattern, 'i');
|
|
53
|
+
filteredMessages = filteredMessages.filter(msg => pattern.test(msg.text));
|
|
54
|
+
}
|
|
55
|
+
if (filter.source) {
|
|
56
|
+
filteredMessages = filteredMessages.filter(msg => msg.source === filter.source);
|
|
57
|
+
}
|
|
58
|
+
if (filter.startTime) {
|
|
59
|
+
filteredMessages = filteredMessages.filter(msg => msg.timestamp >= filter.startTime);
|
|
60
|
+
}
|
|
61
|
+
if (filter.endTime) {
|
|
62
|
+
filteredMessages = filteredMessages.filter(msg => msg.timestamp <= filter.endTime);
|
|
63
|
+
}
|
|
64
|
+
if (filter.maxMessages && filter.maxMessages > 0) {
|
|
65
|
+
filteredMessages = filteredMessages.slice(-filter.maxMessages);
|
|
66
|
+
}
|
|
67
|
+
return filteredMessages;
|
|
68
|
+
}
|
|
69
|
+
getLatestConsoleMessage(connectionId, filter) {
|
|
70
|
+
const messages = this.getConsoleMessages(connectionId, filter);
|
|
71
|
+
return messages.length > 0 ? messages[messages.length - 1] : null;
|
|
72
|
+
}
|
|
73
|
+
clearConsoleMessages(connectionId) {
|
|
74
|
+
this.consoleMessages.delete(connectionId);
|
|
75
|
+
this.logger.debug(`Cleared console messages for connection ${connectionId}`);
|
|
76
|
+
}
|
|
77
|
+
getConsoleMessageCount(connectionId, filter) {
|
|
78
|
+
return this.getConsoleMessages(connectionId, filter).length;
|
|
79
|
+
}
|
|
80
|
+
addNetworkRequest(connectionId, request) {
|
|
81
|
+
this.logger.info(`[DEBUG] addNetworkRequest called for connection ${connectionId}, method: ${request.method}, url: ${request.url.substring(0, 100)}`);
|
|
82
|
+
if (!this.networkRequests.has(connectionId)) {
|
|
83
|
+
this.networkRequests.set(connectionId, []);
|
|
84
|
+
this.logger.info(`[DEBUG] Created new network requests array for connection ${connectionId}`);
|
|
85
|
+
}
|
|
86
|
+
const requests = this.networkRequests.get(connectionId);
|
|
87
|
+
requests.push(request);
|
|
88
|
+
this.logger.debug(`Added network request for connection ${connectionId}`, {
|
|
89
|
+
connectionId,
|
|
90
|
+
method: request.method,
|
|
91
|
+
url: request.url.substring(0, 100),
|
|
92
|
+
totalRequests: requests.length
|
|
93
|
+
});
|
|
94
|
+
if (requests.length > this.maxNetworkRequests) {
|
|
95
|
+
const removed = requests.splice(0, requests.length - this.maxNetworkRequests);
|
|
96
|
+
this.logger.logMemoryEvent('limit-reached', `Network request limit reached for connection ${connectionId}`, {
|
|
97
|
+
connectionCount: 1,
|
|
98
|
+
requestCount: requests.length,
|
|
99
|
+
requestsRemoved: removed.length,
|
|
100
|
+
maxLimit: this.maxNetworkRequests
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
this.logger.debug(`Network request stored successfully`, {
|
|
104
|
+
connectionId,
|
|
105
|
+
requestId: request.requestId,
|
|
106
|
+
method: request.method
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
updateNetworkRequest(connectionId, requestId, update) {
|
|
110
|
+
const requests = this.networkRequests.get(connectionId);
|
|
111
|
+
if (!requests) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const request = requests.find(r => r.requestId === requestId);
|
|
115
|
+
if (request) {
|
|
116
|
+
Object.assign(request, update);
|
|
117
|
+
this.logger.debug(`Updated network request ${requestId} for connection ${connectionId}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
getNetworkRequests(connectionId, filter) {
|
|
121
|
+
const requests = this.networkRequests.get(connectionId) || [];
|
|
122
|
+
if (!filter) {
|
|
123
|
+
return [...requests];
|
|
124
|
+
}
|
|
125
|
+
let filteredRequests = [...requests];
|
|
126
|
+
if (filter.methods && filter.methods.length > 0) {
|
|
127
|
+
filteredRequests = filteredRequests.filter(req => filter.methods.includes(req.method.toUpperCase()));
|
|
128
|
+
}
|
|
129
|
+
if (filter.statusCodes && filter.statusCodes.length > 0) {
|
|
130
|
+
filteredRequests = filteredRequests.filter(req => req.status && filter.statusCodes.includes(req.status));
|
|
131
|
+
}
|
|
132
|
+
if (filter.urlPattern) {
|
|
133
|
+
const pattern = new RegExp(filter.urlPattern, 'i');
|
|
134
|
+
filteredRequests = filteredRequests.filter(req => pattern.test(req.url));
|
|
135
|
+
}
|
|
136
|
+
if (filter.startTime) {
|
|
137
|
+
filteredRequests = filteredRequests.filter(req => req.timestamp >= filter.startTime);
|
|
138
|
+
}
|
|
139
|
+
if (filter.endTime) {
|
|
140
|
+
filteredRequests = filteredRequests.filter(req => req.timestamp <= filter.endTime);
|
|
141
|
+
}
|
|
142
|
+
if (!filter.includeResponseBody) {
|
|
143
|
+
filteredRequests = filteredRequests.map(req => ({
|
|
144
|
+
...req,
|
|
145
|
+
responseBody: undefined
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
if (filter.maxRequests && filter.maxRequests > 0) {
|
|
149
|
+
filteredRequests = filteredRequests.slice(-filter.maxRequests);
|
|
150
|
+
}
|
|
151
|
+
return filteredRequests;
|
|
152
|
+
}
|
|
153
|
+
getLatestNetworkRequest(connectionId, filter) {
|
|
154
|
+
const requests = this.getNetworkRequests(connectionId, filter);
|
|
155
|
+
return requests.length > 0 ? requests[requests.length - 1] : null;
|
|
156
|
+
}
|
|
157
|
+
clearNetworkRequests(connectionId) {
|
|
158
|
+
this.networkRequests.delete(connectionId);
|
|
159
|
+
this.logger.debug(`Cleared network requests for connection ${connectionId}`);
|
|
160
|
+
}
|
|
161
|
+
getNetworkRequestCount(connectionId, filter) {
|
|
162
|
+
return this.getNetworkRequests(connectionId, filter).length;
|
|
163
|
+
}
|
|
164
|
+
cleanupConnection(connectionId) {
|
|
165
|
+
this.clearConsoleMessages(connectionId);
|
|
166
|
+
this.clearNetworkRequests(connectionId);
|
|
167
|
+
this.logger.info(`Cleaned up all data for connection ${connectionId}`);
|
|
168
|
+
}
|
|
169
|
+
clearAll() {
|
|
170
|
+
this.consoleMessages.clear();
|
|
171
|
+
this.networkRequests.clear();
|
|
172
|
+
this.logger.info('Cleared all stored messages and requests');
|
|
173
|
+
}
|
|
174
|
+
enforceMemoryLimits() {
|
|
175
|
+
let totalConsoleMessages = 0;
|
|
176
|
+
let totalNetworkRequests = 0;
|
|
177
|
+
const connectionCount = Math.max(this.consoleMessages.size, this.networkRequests.size);
|
|
178
|
+
for (const messages of this.consoleMessages.values()) {
|
|
179
|
+
totalConsoleMessages += messages.length;
|
|
180
|
+
}
|
|
181
|
+
for (const requests of this.networkRequests.values()) {
|
|
182
|
+
totalNetworkRequests += requests.length;
|
|
183
|
+
}
|
|
184
|
+
this.logger.logMemoryEvent('cleanup', 'Memory usage check', {
|
|
185
|
+
memoryUsage: process.memoryUsage(),
|
|
186
|
+
connectionCount,
|
|
187
|
+
messageCount: totalConsoleMessages,
|
|
188
|
+
requestCount: totalNetworkRequests
|
|
189
|
+
});
|
|
190
|
+
if (totalConsoleMessages > this.maxConsoleMessages * 2) {
|
|
191
|
+
this.logger.logMemoryEvent('limit-reached', 'Global console message limit exceeded, cleaning up oldest messages', {
|
|
192
|
+
messageCount: totalConsoleMessages,
|
|
193
|
+
maxLimit: this.maxConsoleMessages * 2,
|
|
194
|
+
connectionCount
|
|
195
|
+
});
|
|
196
|
+
this.cleanupOldestConsoleMessages();
|
|
197
|
+
}
|
|
198
|
+
if (totalNetworkRequests > this.maxNetworkRequests * 2) {
|
|
199
|
+
this.logger.logMemoryEvent('limit-reached', 'Global network request limit exceeded, cleaning up oldest requests', {
|
|
200
|
+
requestCount: totalNetworkRequests,
|
|
201
|
+
maxLimit: this.maxNetworkRequests * 2,
|
|
202
|
+
connectionCount
|
|
203
|
+
});
|
|
204
|
+
this.cleanupOldestNetworkRequests();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
getStorageStats() {
|
|
208
|
+
const stats = {
|
|
209
|
+
connections: 0,
|
|
210
|
+
totalConsoleMessages: 0,
|
|
211
|
+
totalNetworkRequests: 0,
|
|
212
|
+
consoleMessagesByConnection: {},
|
|
213
|
+
networkRequestsByConnection: {}
|
|
214
|
+
};
|
|
215
|
+
const connectionIds = new Set([
|
|
216
|
+
...this.consoleMessages.keys(),
|
|
217
|
+
...this.networkRequests.keys()
|
|
218
|
+
]);
|
|
219
|
+
stats.connections = connectionIds.size;
|
|
220
|
+
for (const connectionId of connectionIds) {
|
|
221
|
+
const consoleCount = this.consoleMessages.get(connectionId)?.length || 0;
|
|
222
|
+
const networkCount = this.networkRequests.get(connectionId)?.length || 0;
|
|
223
|
+
stats.consoleMessagesByConnection[connectionId] = consoleCount;
|
|
224
|
+
stats.networkRequestsByConnection[connectionId] = networkCount;
|
|
225
|
+
stats.totalConsoleMessages += consoleCount;
|
|
226
|
+
stats.totalNetworkRequests += networkCount;
|
|
227
|
+
}
|
|
228
|
+
return stats;
|
|
229
|
+
}
|
|
230
|
+
processConsoleAPIEvent(connectionId, params) {
|
|
231
|
+
try {
|
|
232
|
+
this.logger.info(`[DEBUG] processConsoleAPIEvent called for connection ${connectionId}, type: ${params.type}`);
|
|
233
|
+
const message = {
|
|
234
|
+
connectionId,
|
|
235
|
+
type: this.mapConsoleType(params.type),
|
|
236
|
+
text: this.formatConsoleArgs(params.args || []),
|
|
237
|
+
args: (params.args || []).map((arg) => arg.value || arg.description || ''),
|
|
238
|
+
timestamp: params.timestamp || Date.now(),
|
|
239
|
+
stackTrace: params.stackTrace ? this.convertStackTrace(params.stackTrace.callFrames) : undefined,
|
|
240
|
+
source: 'Runtime.consoleAPICalled'
|
|
241
|
+
};
|
|
242
|
+
this.logger.info(`[DEBUG] Created console message: ${JSON.stringify(message).substring(0, 200)}`);
|
|
243
|
+
this.addConsoleMessage(connectionId, message);
|
|
244
|
+
this.logger.info(`[DEBUG] Console message added to store for connection ${connectionId}`);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
this.logger.error('[DEBUG] Error processing console API event:', error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
processLogEntryEvent(connectionId, params) {
|
|
251
|
+
try {
|
|
252
|
+
const entry = params.entry;
|
|
253
|
+
const message = {
|
|
254
|
+
connectionId,
|
|
255
|
+
type: this.mapLogLevel(entry.level),
|
|
256
|
+
text: entry.text || '',
|
|
257
|
+
args: [entry.text || ''],
|
|
258
|
+
timestamp: entry.timestamp || Date.now(),
|
|
259
|
+
stackTrace: entry.url && entry.lineNumber ? [{
|
|
260
|
+
functionName: '<unknown>',
|
|
261
|
+
url: entry.url,
|
|
262
|
+
lineNumber: entry.lineNumber,
|
|
263
|
+
columnNumber: 0
|
|
264
|
+
}] : undefined,
|
|
265
|
+
source: 'Log.entryAdded'
|
|
266
|
+
};
|
|
267
|
+
this.addConsoleMessage(connectionId, message);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
this.logger.error('Error processing log entry event:', error);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
mapConsoleType(cdpType) {
|
|
274
|
+
switch (cdpType) {
|
|
275
|
+
case 'log': return 'log';
|
|
276
|
+
case 'info': return 'info';
|
|
277
|
+
case 'warning': return 'warn';
|
|
278
|
+
case 'error': return 'error';
|
|
279
|
+
case 'debug': return 'debug';
|
|
280
|
+
default: return 'log';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
mapLogLevel(logLevel) {
|
|
284
|
+
switch (logLevel.toLowerCase()) {
|
|
285
|
+
case 'verbose':
|
|
286
|
+
case 'info': return 'info';
|
|
287
|
+
case 'warning': return 'warn';
|
|
288
|
+
case 'error': return 'error';
|
|
289
|
+
default: return 'log';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
formatConsoleArgs(args) {
|
|
293
|
+
return args.map(arg => {
|
|
294
|
+
if (arg.value !== undefined) {
|
|
295
|
+
if (typeof arg.value === 'string') {
|
|
296
|
+
return arg.value;
|
|
297
|
+
}
|
|
298
|
+
return JSON.stringify(arg.value);
|
|
299
|
+
}
|
|
300
|
+
return arg.description || '';
|
|
301
|
+
}).join(' ');
|
|
302
|
+
}
|
|
303
|
+
convertStackTrace(callFrames) {
|
|
304
|
+
return callFrames.map(frame => ({
|
|
305
|
+
functionName: frame.functionName || '<anonymous>',
|
|
306
|
+
url: frame.url,
|
|
307
|
+
lineNumber: frame.lineNumber,
|
|
308
|
+
columnNumber: frame.columnNumber
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
cleanupOldestConsoleMessages() {
|
|
312
|
+
let maxMessages = 0;
|
|
313
|
+
let connectionToCleanup = '';
|
|
314
|
+
for (const [connectionId, messages] of this.consoleMessages) {
|
|
315
|
+
if (messages.length > maxMessages) {
|
|
316
|
+
maxMessages = messages.length;
|
|
317
|
+
connectionToCleanup = connectionId;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (connectionToCleanup) {
|
|
321
|
+
const messages = this.consoleMessages.get(connectionToCleanup);
|
|
322
|
+
const toRemove = Math.floor(messages.length * 0.2);
|
|
323
|
+
const beforeCount = messages.length;
|
|
324
|
+
messages.splice(0, toRemove);
|
|
325
|
+
this.logger.logMemoryEvent('cleanup', `Cleaned up old console messages from connection with highest usage`, {
|
|
326
|
+
connectionCount: 1,
|
|
327
|
+
messageCount: messages.length,
|
|
328
|
+
messagesRemoved: toRemove,
|
|
329
|
+
connectionId: connectionToCleanup,
|
|
330
|
+
beforeCount,
|
|
331
|
+
afterCount: messages.length
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
cleanupOldestNetworkRequests() {
|
|
336
|
+
let maxRequests = 0;
|
|
337
|
+
let connectionToCleanup = '';
|
|
338
|
+
for (const [connectionId, requests] of this.networkRequests) {
|
|
339
|
+
if (requests.length > maxRequests) {
|
|
340
|
+
maxRequests = requests.length;
|
|
341
|
+
connectionToCleanup = connectionId;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (connectionToCleanup) {
|
|
345
|
+
const requests = this.networkRequests.get(connectionToCleanup);
|
|
346
|
+
const toRemove = Math.floor(requests.length * 0.2);
|
|
347
|
+
const beforeCount = requests.length;
|
|
348
|
+
requests.splice(0, toRemove);
|
|
349
|
+
this.logger.logMemoryEvent('cleanup', `Cleaned up old network requests from connection with highest usage`, {
|
|
350
|
+
connectionCount: 1,
|
|
351
|
+
requestCount: requests.length,
|
|
352
|
+
requestsRemoved: toRemove,
|
|
353
|
+
connectionId: connectionToCleanup,
|
|
354
|
+
beforeCount,
|
|
355
|
+
afterCount: requests.length
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
exports.MessageStore = MessageStore;
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PerformanceMonitor = void 0;
|
|
37
|
+
const logger_1 = require("../../utils/logger");
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
class PerformanceMonitor {
|
|
40
|
+
constructor(connectionPool, messageStore, thresholds) {
|
|
41
|
+
this.metricsHistory = [];
|
|
42
|
+
this.maxHistorySize = 100;
|
|
43
|
+
this.lastAlertTimes = new Map();
|
|
44
|
+
this.alertCooldownMs = 300000;
|
|
45
|
+
this.logger = (0, logger_1.createLogger)({ component: 'PerformanceMonitor' });
|
|
46
|
+
this.connectionPool = connectionPool;
|
|
47
|
+
this.messageStore = messageStore;
|
|
48
|
+
this.startTime = Date.now();
|
|
49
|
+
this.thresholds = {
|
|
50
|
+
memoryUtilizationWarning: 80,
|
|
51
|
+
memoryUtilizationCritical: 95,
|
|
52
|
+
heapUtilizationWarning: 80,
|
|
53
|
+
heapUtilizationCritical: 95,
|
|
54
|
+
connectionCountWarning: 50,
|
|
55
|
+
connectionCountCritical: 100,
|
|
56
|
+
messageCountWarning: 10000,
|
|
57
|
+
messageCountCritical: 50000,
|
|
58
|
+
...thresholds
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
start(intervalMs = 60000) {
|
|
62
|
+
if (this.monitoringInterval) {
|
|
63
|
+
this.logger.warn('Performance monitoring is already running');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.logger.info('Starting performance monitoring', {
|
|
67
|
+
intervalMs,
|
|
68
|
+
thresholds: this.thresholds
|
|
69
|
+
});
|
|
70
|
+
this.collectMetrics();
|
|
71
|
+
this.monitoringInterval = setInterval(() => {
|
|
72
|
+
this.collectMetrics();
|
|
73
|
+
}, intervalMs);
|
|
74
|
+
}
|
|
75
|
+
stop() {
|
|
76
|
+
if (this.monitoringInterval) {
|
|
77
|
+
clearInterval(this.monitoringInterval);
|
|
78
|
+
this.monitoringInterval = undefined;
|
|
79
|
+
this.logger.info('Performance monitoring stopped');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
collectMetrics() {
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
const memoryUsage = process.memoryUsage();
|
|
85
|
+
const connections = this.connectionPool.getAllConnections();
|
|
86
|
+
const storageStats = this.messageStore.getStorageStats();
|
|
87
|
+
const loadAvg = os.loadavg();
|
|
88
|
+
const freeMemory = os.freemem();
|
|
89
|
+
const totalMemory = os.totalmem();
|
|
90
|
+
const memoryUtilization = ((totalMemory - freeMemory) / totalMemory) * 100;
|
|
91
|
+
const heapUtilization = (memoryUsage.heapUsed / memoryUsage.heapTotal) * 100;
|
|
92
|
+
const healthyConnections = connections.filter(c => c.isHealthy).length;
|
|
93
|
+
const totalClientConnections = connections.reduce((sum, c) => sum + c.clientCount, 0);
|
|
94
|
+
const averageClientCount = connections.length > 0 ? totalClientConnections / connections.length : 0;
|
|
95
|
+
const averageMessagesPerConnection = connections.length > 0
|
|
96
|
+
? storageStats.totalConsoleMessages / connections.length
|
|
97
|
+
: 0;
|
|
98
|
+
const averageRequestsPerConnection = connections.length > 0
|
|
99
|
+
? storageStats.totalNetworkRequests / connections.length
|
|
100
|
+
: 0;
|
|
101
|
+
const metrics = {
|
|
102
|
+
timestamp: now,
|
|
103
|
+
uptime: now - this.startTime,
|
|
104
|
+
memory: {
|
|
105
|
+
heapUsed: memoryUsage.heapUsed,
|
|
106
|
+
heapTotal: memoryUsage.heapTotal,
|
|
107
|
+
external: memoryUsage.external,
|
|
108
|
+
rss: memoryUsage.rss,
|
|
109
|
+
heapUtilization
|
|
110
|
+
},
|
|
111
|
+
system: {
|
|
112
|
+
loadAverage: loadAvg,
|
|
113
|
+
freeMemory,
|
|
114
|
+
totalMemory,
|
|
115
|
+
memoryUtilization,
|
|
116
|
+
cpuCount: os.cpus().length
|
|
117
|
+
},
|
|
118
|
+
connections: {
|
|
119
|
+
total: connections.length,
|
|
120
|
+
healthy: healthyConnections,
|
|
121
|
+
unhealthy: connections.length - healthyConnections,
|
|
122
|
+
averageClientCount,
|
|
123
|
+
totalClientConnections
|
|
124
|
+
},
|
|
125
|
+
messages: {
|
|
126
|
+
totalConsoleMessages: storageStats.totalConsoleMessages,
|
|
127
|
+
totalNetworkRequests: storageStats.totalNetworkRequests,
|
|
128
|
+
averageMessagesPerConnection,
|
|
129
|
+
averageRequestsPerConnection
|
|
130
|
+
},
|
|
131
|
+
performance: {}
|
|
132
|
+
};
|
|
133
|
+
this.metricsHistory.push(metrics);
|
|
134
|
+
if (this.metricsHistory.length > this.maxHistorySize) {
|
|
135
|
+
this.metricsHistory.shift();
|
|
136
|
+
}
|
|
137
|
+
this.logger.logPerformanceMetrics('PerformanceMonitor', {
|
|
138
|
+
uptime: metrics.uptime,
|
|
139
|
+
memoryUtilization: metrics.system.memoryUtilization,
|
|
140
|
+
heapUtilization: metrics.memory.heapUtilization,
|
|
141
|
+
connectionCount: metrics.connections.total,
|
|
142
|
+
messageCount: metrics.messages.totalConsoleMessages,
|
|
143
|
+
requestCount: metrics.messages.totalNetworkRequests,
|
|
144
|
+
loadAverage: metrics.system.loadAverage[0]
|
|
145
|
+
});
|
|
146
|
+
this.checkThresholds(metrics);
|
|
147
|
+
return metrics;
|
|
148
|
+
}
|
|
149
|
+
checkThresholds(metrics) {
|
|
150
|
+
if (metrics.system.memoryUtilization >= this.thresholds.memoryUtilizationCritical) {
|
|
151
|
+
this.sendAlert('memory-critical', 'Critical system memory utilization', {
|
|
152
|
+
utilization: metrics.system.memoryUtilization,
|
|
153
|
+
threshold: this.thresholds.memoryUtilizationCritical,
|
|
154
|
+
freeMemory: metrics.system.freeMemory,
|
|
155
|
+
totalMemory: metrics.system.totalMemory
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else if (metrics.system.memoryUtilization >= this.thresholds.memoryUtilizationWarning) {
|
|
159
|
+
this.sendAlert('memory-warning', 'High system memory utilization', {
|
|
160
|
+
utilization: metrics.system.memoryUtilization,
|
|
161
|
+
threshold: this.thresholds.memoryUtilizationWarning,
|
|
162
|
+
freeMemory: metrics.system.freeMemory,
|
|
163
|
+
totalMemory: metrics.system.totalMemory
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (metrics.memory.heapUtilization >= this.thresholds.heapUtilizationCritical) {
|
|
167
|
+
this.sendAlert('heap-critical', 'Critical heap memory utilization', {
|
|
168
|
+
utilization: metrics.memory.heapUtilization,
|
|
169
|
+
threshold: this.thresholds.heapUtilizationCritical,
|
|
170
|
+
heapUsed: metrics.memory.heapUsed,
|
|
171
|
+
heapTotal: metrics.memory.heapTotal
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
else if (metrics.memory.heapUtilization >= this.thresholds.heapUtilizationWarning) {
|
|
175
|
+
this.sendAlert('heap-warning', 'High heap memory utilization', {
|
|
176
|
+
utilization: metrics.memory.heapUtilization,
|
|
177
|
+
threshold: this.thresholds.heapUtilizationWarning,
|
|
178
|
+
heapUsed: metrics.memory.heapUsed,
|
|
179
|
+
heapTotal: metrics.memory.heapTotal
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (metrics.connections.total >= this.thresholds.connectionCountCritical) {
|
|
183
|
+
this.sendAlert('connections-critical', 'Critical number of connections', {
|
|
184
|
+
connectionCount: metrics.connections.total,
|
|
185
|
+
threshold: this.thresholds.connectionCountCritical,
|
|
186
|
+
healthyConnections: metrics.connections.healthy,
|
|
187
|
+
totalClientConnections: metrics.connections.totalClientConnections
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else if (metrics.connections.total >= this.thresholds.connectionCountWarning) {
|
|
191
|
+
this.sendAlert('connections-warning', 'High number of connections', {
|
|
192
|
+
connectionCount: metrics.connections.total,
|
|
193
|
+
threshold: this.thresholds.connectionCountWarning,
|
|
194
|
+
healthyConnections: metrics.connections.healthy,
|
|
195
|
+
totalClientConnections: metrics.connections.totalClientConnections
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const totalMessages = metrics.messages.totalConsoleMessages + metrics.messages.totalNetworkRequests;
|
|
199
|
+
if (totalMessages >= this.thresholds.messageCountCritical) {
|
|
200
|
+
this.sendAlert('messages-critical', 'Critical number of stored messages', {
|
|
201
|
+
totalMessages,
|
|
202
|
+
threshold: this.thresholds.messageCountCritical,
|
|
203
|
+
consoleMessages: metrics.messages.totalConsoleMessages,
|
|
204
|
+
networkRequests: metrics.messages.totalNetworkRequests
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else if (totalMessages >= this.thresholds.messageCountWarning) {
|
|
208
|
+
this.sendAlert('messages-warning', 'High number of stored messages', {
|
|
209
|
+
totalMessages,
|
|
210
|
+
threshold: this.thresholds.messageCountWarning,
|
|
211
|
+
consoleMessages: metrics.messages.totalConsoleMessages,
|
|
212
|
+
networkRequests: metrics.messages.totalNetworkRequests
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
sendAlert(alertType, message, data) {
|
|
217
|
+
const now = Date.now();
|
|
218
|
+
const lastAlert = this.lastAlertTimes.get(alertType);
|
|
219
|
+
if (!lastAlert || (now - lastAlert) >= this.alertCooldownMs) {
|
|
220
|
+
this.logger.warn(`[PERFORMANCE ALERT] ${message}`, data);
|
|
221
|
+
this.lastAlertTimes.set(alertType, now);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
getCurrentMetrics() {
|
|
225
|
+
return this.metricsHistory.length > 0
|
|
226
|
+
? this.metricsHistory[this.metricsHistory.length - 1]
|
|
227
|
+
: null;
|
|
228
|
+
}
|
|
229
|
+
getMetricsHistory(count) {
|
|
230
|
+
if (count && count < this.metricsHistory.length) {
|
|
231
|
+
return this.metricsHistory.slice(-count);
|
|
232
|
+
}
|
|
233
|
+
return [...this.metricsHistory];
|
|
234
|
+
}
|
|
235
|
+
getPerformanceSummary(periodMs = 3600000) {
|
|
236
|
+
const now = Date.now();
|
|
237
|
+
const cutoff = now - periodMs;
|
|
238
|
+
const recentMetrics = this.metricsHistory.filter(m => m.timestamp >= cutoff);
|
|
239
|
+
if (recentMetrics.length === 0) {
|
|
240
|
+
return {
|
|
241
|
+
averageMemoryUtilization: 0,
|
|
242
|
+
averageHeapUtilization: 0,
|
|
243
|
+
averageConnectionCount: 0,
|
|
244
|
+
averageMessageCount: 0,
|
|
245
|
+
peakMemoryUtilization: 0,
|
|
246
|
+
peakConnectionCount: 0,
|
|
247
|
+
alertCount: 0
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const avgMemoryUtilization = recentMetrics.reduce((sum, m) => sum + m.system.memoryUtilization, 0) / recentMetrics.length;
|
|
251
|
+
const avgHeapUtilization = recentMetrics.reduce((sum, m) => sum + m.memory.heapUtilization, 0) / recentMetrics.length;
|
|
252
|
+
const avgConnectionCount = recentMetrics.reduce((sum, m) => sum + m.connections.total, 0) / recentMetrics.length;
|
|
253
|
+
const avgMessageCount = recentMetrics.reduce((sum, m) => sum + m.messages.totalConsoleMessages + m.messages.totalNetworkRequests, 0) / recentMetrics.length;
|
|
254
|
+
const peakMemoryUtilization = Math.max(...recentMetrics.map(m => m.system.memoryUtilization));
|
|
255
|
+
const peakConnectionCount = Math.max(...recentMetrics.map(m => m.connections.total));
|
|
256
|
+
const alertCount = Math.floor(periodMs / this.alertCooldownMs) * this.lastAlertTimes.size;
|
|
257
|
+
return {
|
|
258
|
+
averageMemoryUtilization: avgMemoryUtilization,
|
|
259
|
+
averageHeapUtilization: avgHeapUtilization,
|
|
260
|
+
averageConnectionCount: avgConnectionCount,
|
|
261
|
+
averageMessageCount: avgMessageCount,
|
|
262
|
+
peakMemoryUtilization,
|
|
263
|
+
peakConnectionCount,
|
|
264
|
+
alertCount
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
updateThresholds(newThresholds) {
|
|
268
|
+
this.thresholds = { ...this.thresholds, ...newThresholds };
|
|
269
|
+
this.logger.info('Performance thresholds updated', { thresholds: this.thresholds });
|
|
270
|
+
}
|
|
271
|
+
clearHistory() {
|
|
272
|
+
this.metricsHistory = [];
|
|
273
|
+
this.lastAlertTimes.clear();
|
|
274
|
+
this.logger.info('Performance metrics history cleared');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
exports.PerformanceMonitor = PerformanceMonitor;
|