pms_md 1.0.2 → 1.0.3
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/BUILD_SUMMARY.md +257 -0
- package/CHANGELOG.md +176 -0
- package/DATABASE_SUPPORT.md +582 -0
- package/FINAL_CHECKLIST.md +210 -0
- package/PACKAGE_READY.txt +169 -0
- package/QUICK_DATABASE_REFERENCE.md +247 -0
- package/README.md +582 -57
- package/RELEASE_v1.0.3.md +237 -0
- package/node-monitor/src/monitors/dbConnectionMonitor.js +536 -4
- package/package.json +47 -3
- package/MYSQL2_SEQUELIZE_SUPPORT.md +0 -334
- package/pms_md-1.0.1.tgz +0 -0
- package/pms_md-1.0.2.tgz +0 -0
- package/test-sequelize-monitor.js +0 -102
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Database Connection Monitor
|
|
3
|
-
* Monitors database connections for MongoDB, PostgreSQL, MySQL,
|
|
3
|
+
* Monitors database connections for MongoDB, PostgreSQL, MySQL, MSSQL, SQLite,
|
|
4
|
+
* MariaDB, Redis, CouchDB, Cassandra, and more
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
const cron = require('node-cron');
|
|
@@ -114,12 +115,41 @@ class DbConnectionMonitor {
|
|
|
114
115
|
case 'mysql2':
|
|
115
116
|
return this.checkMySqlConnection(connection, testQuery);
|
|
116
117
|
|
|
118
|
+
case 'mariadb':
|
|
119
|
+
return this.checkMariaDBConnection(connection, testQuery);
|
|
120
|
+
|
|
121
|
+
case 'mssql':
|
|
122
|
+
case 'sqlserver':
|
|
123
|
+
case 'tedious':
|
|
124
|
+
return this.checkMSSQLConnection(connection, testQuery);
|
|
125
|
+
|
|
126
|
+
case 'sqlite':
|
|
127
|
+
case 'sqlite3':
|
|
128
|
+
return this.checkSQLiteConnection(connection, testQuery);
|
|
129
|
+
|
|
117
130
|
case 'sequelize':
|
|
118
131
|
return this.checkSequelizeConnection(connection);
|
|
119
132
|
|
|
120
133
|
case 'redis':
|
|
121
134
|
return this.checkRedisConnection(connection);
|
|
122
135
|
|
|
136
|
+
case 'couchdb':
|
|
137
|
+
case 'nano':
|
|
138
|
+
return this.checkCouchDBConnection(connection);
|
|
139
|
+
|
|
140
|
+
case 'cassandra':
|
|
141
|
+
return this.checkCassandraConnection(connection);
|
|
142
|
+
|
|
143
|
+
case 'elasticsearch':
|
|
144
|
+
case 'elastic':
|
|
145
|
+
return this.checkElasticsearchConnection(connection);
|
|
146
|
+
|
|
147
|
+
case 'dynamodb':
|
|
148
|
+
return this.checkDynamoDBConnection(connection);
|
|
149
|
+
|
|
150
|
+
case 'neo4j':
|
|
151
|
+
return this.checkNeo4jConnection(connection);
|
|
152
|
+
|
|
123
153
|
default:
|
|
124
154
|
throw new Error(`Unsupported database type: ${type}`);
|
|
125
155
|
}
|
|
@@ -225,6 +255,259 @@ class DbConnectionMonitor {
|
|
|
225
255
|
}
|
|
226
256
|
}
|
|
227
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Check MariaDB connection
|
|
260
|
+
*/
|
|
261
|
+
async checkMariaDBConnection(connection, testQuery = 'SELECT 1') {
|
|
262
|
+
try {
|
|
263
|
+
// MariaDB uses same interface as MySQL
|
|
264
|
+
if (connection.query) {
|
|
265
|
+
const result = await Promise.race([
|
|
266
|
+
connection.query(testQuery),
|
|
267
|
+
this.timeout(this.config.database.queryTimeout)
|
|
268
|
+
]);
|
|
269
|
+
return Array.isArray(result) ? !!result[0] : !!result;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (connection.execute) {
|
|
273
|
+
const [rows] = await Promise.race([
|
|
274
|
+
connection.execute(testQuery),
|
|
275
|
+
this.timeout(this.config.database.queryTimeout)
|
|
276
|
+
]);
|
|
277
|
+
return !!rows;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return false;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
this.logger.logWarning('mariadb_check_failed', `MariaDB connection check failed: ${error.message}`);
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Check MSSQL connection
|
|
289
|
+
*/
|
|
290
|
+
async checkMSSQLConnection(connection, testQuery = 'SELECT 1') {
|
|
291
|
+
try {
|
|
292
|
+
// For mssql package (ConnectionPool)
|
|
293
|
+
if (connection.connected !== undefined) {
|
|
294
|
+
if (!connection.connected) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Try to execute a query
|
|
300
|
+
if (connection.query) {
|
|
301
|
+
const result = await Promise.race([
|
|
302
|
+
connection.query(testQuery),
|
|
303
|
+
this.timeout(this.config.database.queryTimeout)
|
|
304
|
+
]);
|
|
305
|
+
return !!result;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// For tedious connection
|
|
309
|
+
if (connection.request) {
|
|
310
|
+
const request = connection.request();
|
|
311
|
+
const result = await Promise.race([
|
|
312
|
+
request.query(testQuery),
|
|
313
|
+
this.timeout(this.config.database.queryTimeout)
|
|
314
|
+
]);
|
|
315
|
+
return !!result;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return false;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
this.logger.logWarning('mssql_check_failed', `MSSQL connection check failed: ${error.message}`);
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Check SQLite connection
|
|
327
|
+
*/
|
|
328
|
+
async checkSQLiteConnection(connection, testQuery = 'SELECT 1') {
|
|
329
|
+
try {
|
|
330
|
+
// For sqlite3 package
|
|
331
|
+
if (connection.get) {
|
|
332
|
+
return new Promise((resolve) => {
|
|
333
|
+
const timeout = setTimeout(() => resolve(false), this.config.database.queryTimeout);
|
|
334
|
+
|
|
335
|
+
connection.get(testQuery, (err) => {
|
|
336
|
+
clearTimeout(timeout);
|
|
337
|
+
resolve(!err);
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// For better-sqlite3 package
|
|
343
|
+
if (connection.prepare) {
|
|
344
|
+
const stmt = connection.prepare(testQuery);
|
|
345
|
+
const result = stmt.get();
|
|
346
|
+
return !!result || result === undefined;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return false;
|
|
350
|
+
} catch (error) {
|
|
351
|
+
this.logger.logWarning('sqlite_check_failed', `SQLite connection check failed: ${error.message}`);
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Check CouchDB connection
|
|
358
|
+
*/
|
|
359
|
+
async checkCouchDBConnection(connection) {
|
|
360
|
+
try {
|
|
361
|
+
// For nano package
|
|
362
|
+
if (connection.db && connection.db.list) {
|
|
363
|
+
const result = await Promise.race([
|
|
364
|
+
connection.db.list(),
|
|
365
|
+
this.timeout(this.config.database.queryTimeout)
|
|
366
|
+
]);
|
|
367
|
+
return !!result;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// For direct HTTP client
|
|
371
|
+
if (connection.info) {
|
|
372
|
+
const result = await Promise.race([
|
|
373
|
+
connection.info(),
|
|
374
|
+
this.timeout(this.config.database.queryTimeout)
|
|
375
|
+
]);
|
|
376
|
+
return !!result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return false;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
this.logger.logWarning('couchdb_check_failed', `CouchDB connection check failed: ${error.message}`);
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Check Cassandra connection
|
|
388
|
+
*/
|
|
389
|
+
async checkCassandraConnection(connection) {
|
|
390
|
+
try {
|
|
391
|
+
// For cassandra-driver package
|
|
392
|
+
if (connection.execute) {
|
|
393
|
+
const result = await Promise.race([
|
|
394
|
+
connection.execute('SELECT now() FROM system.local'),
|
|
395
|
+
this.timeout(this.config.database.queryTimeout)
|
|
396
|
+
]);
|
|
397
|
+
return !!result;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Check connection state
|
|
401
|
+
if (connection.getState) {
|
|
402
|
+
const state = connection.getState();
|
|
403
|
+
return state && state.getConnectedHosts && state.getConnectedHosts().length > 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return false;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
this.logger.logWarning('cassandra_check_failed', `Cassandra connection check failed: ${error.message}`);
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Check Elasticsearch connection
|
|
415
|
+
*/
|
|
416
|
+
async checkElasticsearchConnection(connection) {
|
|
417
|
+
try {
|
|
418
|
+
// For @elastic/elasticsearch package
|
|
419
|
+
if (connection.ping) {
|
|
420
|
+
const result = await Promise.race([
|
|
421
|
+
connection.ping(),
|
|
422
|
+
this.timeout(this.config.database.queryTimeout)
|
|
423
|
+
]);
|
|
424
|
+
return !!result;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Alternative: check cluster health
|
|
428
|
+
if (connection.cluster && connection.cluster.health) {
|
|
429
|
+
const result = await Promise.race([
|
|
430
|
+
connection.cluster.health(),
|
|
431
|
+
this.timeout(this.config.database.queryTimeout)
|
|
432
|
+
]);
|
|
433
|
+
return !!result;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return false;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
this.logger.logWarning('elasticsearch_check_failed', `Elasticsearch connection check failed: ${error.message}`);
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Check DynamoDB connection
|
|
445
|
+
*/
|
|
446
|
+
async checkDynamoDBConnection(connection) {
|
|
447
|
+
try {
|
|
448
|
+
// For AWS SDK DynamoDB client
|
|
449
|
+
if (connection.listTables) {
|
|
450
|
+
const result = await Promise.race([
|
|
451
|
+
connection.listTables({ Limit: 1 }).promise(),
|
|
452
|
+
this.timeout(this.config.database.queryTimeout)
|
|
453
|
+
]);
|
|
454
|
+
return !!result;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// For AWS SDK v3
|
|
458
|
+
if (connection.send) {
|
|
459
|
+
const { ListTablesCommand } = require('@aws-sdk/client-dynamodb');
|
|
460
|
+
const result = await Promise.race([
|
|
461
|
+
connection.send(new ListTablesCommand({ Limit: 1 })),
|
|
462
|
+
this.timeout(this.config.database.queryTimeout)
|
|
463
|
+
]);
|
|
464
|
+
return !!result;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return false;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
this.logger.logWarning('dynamodb_check_failed', `DynamoDB connection check failed: ${error.message}`);
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Check Neo4j connection
|
|
476
|
+
*/
|
|
477
|
+
async checkNeo4jConnection(connection) {
|
|
478
|
+
try {
|
|
479
|
+
// For neo4j-driver package
|
|
480
|
+
if (connection.verifyConnectivity) {
|
|
481
|
+
await Promise.race([
|
|
482
|
+
connection.verifyConnectivity(),
|
|
483
|
+
this.timeout(this.config.database.queryTimeout)
|
|
484
|
+
]);
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Alternative: run a simple query
|
|
489
|
+
if (connection.session) {
|
|
490
|
+
const session = connection.session();
|
|
491
|
+
try {
|
|
492
|
+
const result = await Promise.race([
|
|
493
|
+
session.run('RETURN 1'),
|
|
494
|
+
this.timeout(this.config.database.queryTimeout)
|
|
495
|
+
]);
|
|
496
|
+
await session.close();
|
|
497
|
+
return !!result;
|
|
498
|
+
} catch (error) {
|
|
499
|
+
await session.close();
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return false;
|
|
505
|
+
} catch (error) {
|
|
506
|
+
this.logger.logWarning('neo4j_check_failed', `Neo4j connection check failed: ${error.message}`);
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
228
511
|
/**
|
|
229
512
|
* Handle connection status changes
|
|
230
513
|
*/
|
|
@@ -321,12 +604,41 @@ class DbConnectionMonitor {
|
|
|
321
604
|
case 'mysql2':
|
|
322
605
|
return this.getMySqlStats(connection);
|
|
323
606
|
|
|
607
|
+
case 'mariadb':
|
|
608
|
+
return this.getMariaDBStats(connection);
|
|
609
|
+
|
|
610
|
+
case 'mssql':
|
|
611
|
+
case 'sqlserver':
|
|
612
|
+
case 'tedious':
|
|
613
|
+
return this.getMSSQLStats(connection);
|
|
614
|
+
|
|
615
|
+
case 'sqlite':
|
|
616
|
+
case 'sqlite3':
|
|
617
|
+
return this.getSQLiteStats(connection);
|
|
618
|
+
|
|
324
619
|
case 'sequelize':
|
|
325
620
|
return this.getSequelizeStats(connection);
|
|
326
621
|
|
|
327
622
|
case 'redis':
|
|
328
623
|
return this.getRedisStats(connection);
|
|
329
624
|
|
|
625
|
+
case 'couchdb':
|
|
626
|
+
case 'nano':
|
|
627
|
+
return this.getCouchDBStats(connection);
|
|
628
|
+
|
|
629
|
+
case 'cassandra':
|
|
630
|
+
return this.getCassandraStats(connection);
|
|
631
|
+
|
|
632
|
+
case 'elasticsearch':
|
|
633
|
+
case 'elastic':
|
|
634
|
+
return this.getElasticsearchStats(connection);
|
|
635
|
+
|
|
636
|
+
case 'dynamodb':
|
|
637
|
+
return this.getDynamoDBStats(connection);
|
|
638
|
+
|
|
639
|
+
case 'neo4j':
|
|
640
|
+
return this.getNeo4jStats(connection);
|
|
641
|
+
|
|
330
642
|
default:
|
|
331
643
|
return { type, status: 'unknown' };
|
|
332
644
|
}
|
|
@@ -438,14 +750,14 @@ class DbConnectionMonitor {
|
|
|
438
750
|
const info = await connection.info();
|
|
439
751
|
const lines = info.split('\r\n');
|
|
440
752
|
const stats = {};
|
|
441
|
-
|
|
753
|
+
|
|
442
754
|
lines.forEach(line => {
|
|
443
755
|
const [key, value] = line.split(':');
|
|
444
756
|
if (key && value) {
|
|
445
757
|
stats[key] = value;
|
|
446
758
|
}
|
|
447
759
|
});
|
|
448
|
-
|
|
760
|
+
|
|
449
761
|
return {
|
|
450
762
|
type: 'redis',
|
|
451
763
|
connectedClients: stats.connected_clients,
|
|
@@ -457,11 +769,231 @@ class DbConnectionMonitor {
|
|
|
457
769
|
}
|
|
458
770
|
}
|
|
459
771
|
|
|
772
|
+
/**
|
|
773
|
+
* Get MariaDB statistics
|
|
774
|
+
*/
|
|
775
|
+
async getMariaDBStats(connection) {
|
|
776
|
+
try {
|
|
777
|
+
let rows;
|
|
778
|
+
|
|
779
|
+
if (connection.query) {
|
|
780
|
+
const result = await connection.query('SHOW STATUS LIKE "Threads_connected"');
|
|
781
|
+
rows = Array.isArray(result) ? result[0] : result;
|
|
782
|
+
} else if (connection.execute) {
|
|
783
|
+
[rows] = await connection.execute('SHOW STATUS LIKE "Threads_connected"');
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return {
|
|
787
|
+
type: 'mariadb',
|
|
788
|
+
threadsConnected: rows?.[0]?.Value || 0
|
|
789
|
+
};
|
|
790
|
+
} catch (error) {
|
|
791
|
+
return { type: 'mariadb', error: error.message };
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Get MSSQL statistics
|
|
797
|
+
*/
|
|
798
|
+
async getMSSQLStats(connection) {
|
|
799
|
+
try {
|
|
800
|
+
let result;
|
|
801
|
+
|
|
802
|
+
if (connection.query) {
|
|
803
|
+
result = await connection.query('SELECT @@VERSION as version, @@CONNECTIONS as connections');
|
|
804
|
+
} else if (connection.request) {
|
|
805
|
+
const request = connection.request();
|
|
806
|
+
result = await request.query('SELECT @@VERSION as version, @@CONNECTIONS as connections');
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return {
|
|
810
|
+
type: 'mssql',
|
|
811
|
+
version: result?.recordset?.[0]?.version || 'unknown',
|
|
812
|
+
connections: result?.recordset?.[0]?.connections || 0,
|
|
813
|
+
connected: connection.connected || false
|
|
814
|
+
};
|
|
815
|
+
} catch (error) {
|
|
816
|
+
return { type: 'mssql', error: error.message };
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Get SQLite statistics
|
|
822
|
+
*/
|
|
823
|
+
async getSQLiteStats(connection) {
|
|
824
|
+
try {
|
|
825
|
+
let stats = { type: 'sqlite' };
|
|
826
|
+
|
|
827
|
+
// For sqlite3
|
|
828
|
+
if (connection.get) {
|
|
829
|
+
return new Promise((resolve) => {
|
|
830
|
+
connection.get('PRAGMA database_list', (err, row) => {
|
|
831
|
+
if (!err && row) {
|
|
832
|
+
stats.database = row.file || row.name;
|
|
833
|
+
}
|
|
834
|
+
resolve(stats);
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// For better-sqlite3
|
|
840
|
+
if (connection.name) {
|
|
841
|
+
stats.database = connection.name;
|
|
842
|
+
stats.inTransaction = connection.inTransaction || false;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
return stats;
|
|
846
|
+
} catch (error) {
|
|
847
|
+
return { type: 'sqlite', error: error.message };
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Get CouchDB statistics
|
|
853
|
+
*/
|
|
854
|
+
async getCouchDBStats(connection) {
|
|
855
|
+
try {
|
|
856
|
+
let info;
|
|
857
|
+
|
|
858
|
+
if (connection.db && connection.db.list) {
|
|
859
|
+
const databases = await connection.db.list();
|
|
860
|
+
info = { type: 'couchdb', databases: databases.length };
|
|
861
|
+
} else if (connection.info) {
|
|
862
|
+
const serverInfo = await connection.info();
|
|
863
|
+
info = {
|
|
864
|
+
type: 'couchdb',
|
|
865
|
+
version: serverInfo.version,
|
|
866
|
+
vendor: serverInfo.vendor?.name
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return info || { type: 'couchdb' };
|
|
871
|
+
} catch (error) {
|
|
872
|
+
return { type: 'couchdb', error: error.message };
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Get Cassandra statistics
|
|
878
|
+
*/
|
|
879
|
+
async getCassandraStats(connection) {
|
|
880
|
+
try {
|
|
881
|
+
const stats = { type: 'cassandra' };
|
|
882
|
+
|
|
883
|
+
if (connection.getState) {
|
|
884
|
+
const state = connection.getState();
|
|
885
|
+
const hosts = state.getConnectedHosts();
|
|
886
|
+
stats.connectedHosts = hosts.length;
|
|
887
|
+
stats.hosts = hosts.map(h => h.address);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
if (connection.metadata) {
|
|
891
|
+
stats.keyspaces = Object.keys(connection.metadata.keyspaces || {}).length;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return stats;
|
|
895
|
+
} catch (error) {
|
|
896
|
+
return { type: 'cassandra', error: error.message };
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Get Elasticsearch statistics
|
|
902
|
+
*/
|
|
903
|
+
async getElasticsearchStats(connection) {
|
|
904
|
+
try {
|
|
905
|
+
const stats = { type: 'elasticsearch' };
|
|
906
|
+
|
|
907
|
+
// Get cluster health
|
|
908
|
+
if (connection.cluster && connection.cluster.health) {
|
|
909
|
+
const health = await connection.cluster.health();
|
|
910
|
+
stats.clusterName = health.cluster_name;
|
|
911
|
+
stats.status = health.status;
|
|
912
|
+
stats.numberOfNodes = health.number_of_nodes;
|
|
913
|
+
stats.activeShards = health.active_shards;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Get cluster info
|
|
917
|
+
if (connection.info) {
|
|
918
|
+
const info = await connection.info();
|
|
919
|
+
stats.version = info.version?.number;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
return stats;
|
|
923
|
+
} catch (error) {
|
|
924
|
+
return { type: 'elasticsearch', error: error.message };
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Get DynamoDB statistics
|
|
930
|
+
*/
|
|
931
|
+
async getDynamoDBStats(connection) {
|
|
932
|
+
try {
|
|
933
|
+
const stats = { type: 'dynamodb' };
|
|
934
|
+
|
|
935
|
+
// For AWS SDK v2
|
|
936
|
+
if (connection.listTables) {
|
|
937
|
+
const result = await connection.listTables().promise();
|
|
938
|
+
stats.tableCount = result.TableNames?.length || 0;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// For AWS SDK v3
|
|
942
|
+
if (connection.send) {
|
|
943
|
+
const { ListTablesCommand } = require('@aws-sdk/client-dynamodb');
|
|
944
|
+
const result = await connection.send(new ListTablesCommand({}));
|
|
945
|
+
stats.tableCount = result.TableNames?.length || 0;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return stats;
|
|
949
|
+
} catch (error) {
|
|
950
|
+
return { type: 'dynamodb', error: error.message };
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Get Neo4j statistics
|
|
956
|
+
*/
|
|
957
|
+
async getNeo4jStats(connection) {
|
|
958
|
+
try {
|
|
959
|
+
const stats = { type: 'neo4j' };
|
|
960
|
+
|
|
961
|
+
// Get server info
|
|
962
|
+
if (connection.getServerInfo) {
|
|
963
|
+
const serverInfo = await connection.getServerInfo();
|
|
964
|
+
stats.version = serverInfo.version;
|
|
965
|
+
stats.address = serverInfo.address;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Run a query to get database stats
|
|
969
|
+
if (connection.session) {
|
|
970
|
+
const session = connection.session();
|
|
971
|
+
try {
|
|
972
|
+
const result = await session.run('CALL dbms.components() YIELD name, versions, edition');
|
|
973
|
+
const record = result.records[0];
|
|
974
|
+
if (record) {
|
|
975
|
+
stats.name = record.get('name');
|
|
976
|
+
stats.versions = record.get('versions');
|
|
977
|
+
stats.edition = record.get('edition');
|
|
978
|
+
}
|
|
979
|
+
await session.close();
|
|
980
|
+
} catch (error) {
|
|
981
|
+
await session.close();
|
|
982
|
+
stats.queryError = error.message;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return stats;
|
|
987
|
+
} catch (error) {
|
|
988
|
+
return { type: 'neo4j', error: error.message };
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
460
992
|
/**
|
|
461
993
|
* Timeout helper
|
|
462
994
|
*/
|
|
463
995
|
timeout(ms) {
|
|
464
|
-
return new Promise((_, reject) =>
|
|
996
|
+
return new Promise((_, reject) =>
|
|
465
997
|
setTimeout(() => reject(new Error('Query timeout')), ms)
|
|
466
998
|
);
|
|
467
999
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pms_md",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Comprehensive monitoring solution for Node.js applications with error tracking, health checks, and multi-channel notifications",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Comprehensive monitoring solution for Node.js applications with error tracking, health checks, multi-database support (MongoDB, PostgreSQL, MySQL, MSSQL, SQLite, Redis, Cassandra, Elasticsearch, DynamoDB, Neo4j, CouchDB), and multi-channel notifications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
@@ -47,8 +47,19 @@
|
|
|
47
47
|
"mongoose": "^7.0.0 || ^8.0.0",
|
|
48
48
|
"pg": "^8.0.0",
|
|
49
49
|
"mysql2": "^2.0.0 || ^3.0.0",
|
|
50
|
+
"mariadb": "^3.0.0",
|
|
51
|
+
"mssql": "^9.0.0 || ^10.0.0",
|
|
52
|
+
"tedious": "^15.0.0 || ^16.0.0",
|
|
53
|
+
"sqlite3": "^5.0.0",
|
|
54
|
+
"better-sqlite3": "^8.0.0 || ^9.0.0",
|
|
50
55
|
"sequelize": "^6.0.0",
|
|
51
|
-
"ioredis": "^5.0.0"
|
|
56
|
+
"ioredis": "^5.0.0",
|
|
57
|
+
"nano": "^10.0.0",
|
|
58
|
+
"cassandra-driver": "^4.0.0",
|
|
59
|
+
"@elastic/elasticsearch": "^8.0.0",
|
|
60
|
+
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
61
|
+
"aws-sdk": "^2.0.0",
|
|
62
|
+
"neo4j-driver": "^5.0.0"
|
|
52
63
|
},
|
|
53
64
|
"peerDependenciesMeta": {
|
|
54
65
|
"express": {
|
|
@@ -63,11 +74,44 @@
|
|
|
63
74
|
"mysql2": {
|
|
64
75
|
"optional": true
|
|
65
76
|
},
|
|
77
|
+
"mariadb": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"mssql": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"tedious": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"sqlite3": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"better-sqlite3": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
66
92
|
"sequelize": {
|
|
67
93
|
"optional": true
|
|
68
94
|
},
|
|
69
95
|
"ioredis": {
|
|
70
96
|
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"nano": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"cassandra-driver": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@elastic/elasticsearch": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@aws-sdk/client-dynamodb": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"aws-sdk": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"neo4j-driver": {
|
|
114
|
+
"optional": true
|
|
71
115
|
}
|
|
72
116
|
},
|
|
73
117
|
"devDependencies": {
|