@gazzehamine/armada-watch-agent 1.4.3 → 1.4.4

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/dist/collector.js CHANGED
@@ -10,6 +10,7 @@ exports.collectDockerContainers = collectDockerContainers;
10
10
  exports.collectNginxMetrics = collectNginxMetrics;
11
11
  exports.collectPM2Processes = collectPM2Processes;
12
12
  exports.collectSSLCertificates = collectSSLCertificates;
13
+ exports.collectSystemdServices = collectSystemdServices;
13
14
  const systeminformation_1 = __importDefault(require("systeminformation"));
14
15
  const os_1 = __importDefault(require("os"));
15
16
  const fs_1 = __importDefault(require("fs"));
@@ -383,3 +384,149 @@ async function collectSSLCertificates() {
383
384
  return certificates;
384
385
  }
385
386
  }
387
+ /**
388
+ * Collect systemd service information with auto-discovery
389
+ */
390
+ async function collectSystemdServices() {
391
+ try {
392
+ // Check if systemd is available
393
+ try {
394
+ await execAsync('which systemctl');
395
+ }
396
+ catch (error) {
397
+ // systemd not available (e.g., not a Linux system or using a different init system)
398
+ return [];
399
+ }
400
+ const services = [];
401
+ // Common services to monitor
402
+ const COMMON_SERVICES = [
403
+ 'nginx', 'apache2', 'httpd', // Web servers
404
+ 'docker', 'containerd', // Containers
405
+ 'postgresql', 'mysql', 'mariadb', // Databases
406
+ 'mongodb', 'redis', 'memcached',
407
+ 'ssh', 'sshd', // System services
408
+ ];
409
+ // Get all PM2 services (pm2-*)
410
+ let pm2Services = [];
411
+ try {
412
+ const { stdout } = await execAsync('systemctl list-units --type=service --all --no-pager --no-legend | grep "pm2-"');
413
+ if (stdout) {
414
+ pm2Services = stdout
415
+ .split('\n')
416
+ .filter(line => line.trim())
417
+ .map(line => line.split(/\s+/)[0].replace('.service', ''));
418
+ }
419
+ }
420
+ catch (error) {
421
+ // No PM2 services or error listing them
422
+ }
423
+ // Get all failed services
424
+ let failedServices = [];
425
+ try {
426
+ const { stdout } = await execAsync('systemctl list-units --type=service --state=failed --no-pager --no-legend');
427
+ if (stdout) {
428
+ failedServices = stdout
429
+ .split('\n')
430
+ .filter(line => line.trim())
431
+ .map(line => line.split(/\s+/)[0].replace('.service', ''));
432
+ }
433
+ }
434
+ catch (error) {
435
+ // No failed services or error listing them
436
+ }
437
+ // Combine all services to monitor (remove duplicates)
438
+ const servicesToMonitor = Array.from(new Set([...COMMON_SERVICES, ...pm2Services, ...failedServices]));
439
+ // Collect info for each service
440
+ for (const serviceName of servicesToMonitor) {
441
+ try {
442
+ const serviceInfo = await getServiceInfo(serviceName);
443
+ if (serviceInfo) {
444
+ services.push(serviceInfo);
445
+ }
446
+ }
447
+ catch (error) {
448
+ // Service doesn't exist or error getting info, skip it
449
+ continue;
450
+ }
451
+ }
452
+ return services;
453
+ }
454
+ catch (error) {
455
+ console.error('Error collecting systemd services:', error);
456
+ return [];
457
+ }
458
+ }
459
+ /**
460
+ * Get detailed information for a single systemd service
461
+ */
462
+ async function getServiceInfo(serviceName) {
463
+ try {
464
+ // Get detailed service properties
465
+ const { stdout } = await execAsync(`systemctl show ${serviceName}.service --no-pager`, { timeout: 5000 });
466
+ // Parse the output into key-value pairs
467
+ const props = {};
468
+ stdout.split('\n').forEach(line => {
469
+ const [key, ...valueParts] = line.split('=');
470
+ if (key && valueParts.length > 0) {
471
+ props[key.trim()] = valueParts.join('=').trim();
472
+ }
473
+ });
474
+ // Check if service exists
475
+ if (props.LoadState === 'not-found') {
476
+ return null;
477
+ }
478
+ // Extract service information
479
+ const activeState = props.ActiveState || 'unknown';
480
+ const subState = props.SubState || 'unknown';
481
+ const unitFileState = props.UnitFileState || 'unknown';
482
+ const enabled = ['enabled', 'enabled-runtime', 'static'].includes(unitFileState);
483
+ // Parse timestamps
484
+ const activeEnterTimestamp = props.ActiveEnterTimestamp ? new Date(props.ActiveEnterTimestamp).getTime() : 0;
485
+ const now = Date.now();
486
+ // Calculate uptime (in seconds)
487
+ let uptime = 0;
488
+ if (activeState === 'active' && activeEnterTimestamp > 0) {
489
+ uptime = Math.floor((now - activeEnterTimestamp) / 1000);
490
+ }
491
+ // Get restart count
492
+ const restartCount = parseInt(props.NRestarts || '0', 10);
493
+ // Get main PID
494
+ const mainPID = parseInt(props.MainPID || '0', 10);
495
+ // Get CPU and Memory usage
496
+ let cpuPercent = 0;
497
+ let memoryBytes = 0;
498
+ if (mainPID > 0) {
499
+ try {
500
+ // Use ps to get CPU and memory for the main PID
501
+ const { stdout: psOutput } = await execAsync(`ps -p ${mainPID} -o %cpu=,%mem=,rss= --no-headers`, { timeout: 2000 });
502
+ if (psOutput) {
503
+ const parts = psOutput.trim().split(/\s+/);
504
+ if (parts.length >= 3) {
505
+ cpuPercent = parseFloat(parts[0]) || 0;
506
+ // RSS is in KB, convert to bytes
507
+ memoryBytes = (parseInt(parts[2], 10) || 0) * 1024;
508
+ }
509
+ }
510
+ }
511
+ catch (error) {
512
+ // Process might have ended, use 0 values
513
+ }
514
+ }
515
+ return {
516
+ name: serviceName,
517
+ status: activeState,
518
+ subState: subState,
519
+ enabled: enabled,
520
+ uptime: uptime,
521
+ startedAt: activeEnterTimestamp,
522
+ restartCount: restartCount,
523
+ cpuPercent: cpuPercent,
524
+ memoryBytes: memoryBytes,
525
+ pid: mainPID,
526
+ };
527
+ }
528
+ catch (error) {
529
+ // Service doesn't exist or error getting info
530
+ return null;
531
+ }
532
+ }
package/dist/index.js CHANGED
@@ -77,6 +77,7 @@ async function sendMetrics() {
77
77
  const dockerContainers = await (0, collector_1.collectDockerContainers)();
78
78
  const pm2Processes = await (0, collector_1.collectPM2Processes)();
79
79
  const nginxMetrics = await (0, collector_1.collectNginxMetrics)();
80
+ const systemdServices = await (0, collector_1.collectSystemdServices)();
80
81
  // Check if it's time to refresh SSL certificates (every 5 minutes)
81
82
  const now = Date.now();
82
83
  const shouldCheckSSL = (now - lastSSLCheckTime) >= SSL_CHECK_INTERVAL;
@@ -97,6 +98,7 @@ async function sendMetrics() {
97
98
  dockerContainers,
98
99
  pm2Processes: pm2Processes.length > 0 ? pm2Processes : undefined,
99
100
  nginxMetrics: nginxMetrics || undefined,
101
+ systemdServices: systemdServices.length > 0 ? systemdServices : undefined,
100
102
  // Always send SSL certificates if we have them (from startup or last check)
101
103
  sslCertificates: lastSSLCertificates.length > 0 ? lastSSLCertificates : undefined,
102
104
  };
@@ -105,7 +107,8 @@ async function sendMetrics() {
105
107
  });
106
108
  const pm2Info = pm2Processes.length > 0 ? ` | PM2: ${pm2Processes.length}` : '';
107
109
  const nginxInfo = nginxMetrics ? ` | Nginx: ${nginxMetrics.activeConnections} conn` : '';
108
- console.log(`✓ Metrics sent successfully - CPU: ${metrics.cpuUsage.toFixed(1)}% | Memory: ${metrics.memoryUsage.toFixed(1)}%${pm2Info}${nginxInfo}`);
110
+ const systemdInfo = systemdServices.length > 0 ? ` | Services: ${systemdServices.length}` : '';
111
+ console.log(`✓ Metrics sent successfully - CPU: ${metrics.cpuUsage.toFixed(1)}% | Memory: ${metrics.memoryUsage.toFixed(1)}%${pm2Info}${nginxInfo}${systemdInfo}`);
109
112
  }
110
113
  catch (error) {
111
114
  if (axios_1.default.isAxiosError(error)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gazzehamine/armada-watch-agent",
3
- "version": "1.4.3",
4
- "description": "Monitoring agent for Armada Watch - EC2 instance monitoring with SSL, PM2, and Nginx monitoring",
3
+ "version": "1.4.4",
4
+ "description": "Monitoring agent for Armada Watch - EC2 instance monitoring with SSL, PM2, Nginx, and Systemd monitoring",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "armada-watch-agent": "dist/index.js"