[object Object]

← back to Cncp Failure Collector

tick 2: heartbeat log every 10 quiet cycles, skip self in pm2 scan

495181cc6dfa3a96a76febb96d505977c33d24d3 · 2026-05-11 11:17:47 -0700 · SteveStudio2

Files touched

Diff

commit 495181cc6dfa3a96a76febb96d505977c33d24d3
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Mon May 11 11:17:47 2026 -0700

    tick 2: heartbeat log every 10 quiet cycles, skip self in pm2 scan
---
 index.js | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/index.js b/index.js
index 7b0eaa0..8f40736 100644
--- a/index.js
+++ b/index.js
@@ -82,6 +82,11 @@ async function scanPm2(state) {
   for (const p of procs) {
     const name = p.name;
     seenNames.add(name);
+    // Skip ourselves — a restart of the collector (intentional deploy) shouldn't flood the ledger.
+    if (name === 'cncp-failure-collector') {
+      state.pm2[name] = { restart_time: p.pm2_env?.restart_time || 0, status: p.pm2_env?.status || 'unknown' };
+      continue;
+    }
     const prior = state.pm2[name];
     const restartTime = p.pm2_env?.restart_time || 0;
     const status = p.pm2_env?.status || 'unknown';
@@ -180,11 +185,32 @@ async function scanLaunchdErrLogs(state) {
   }
 }
 
+let cyclesSinceLastHeartbeat = 0;
+const HEARTBEAT_EVERY = 10; // log once every 10 cycles (~5 min at 30s poll) when quiet
+
 async function cycle() {
   const state = loadState();
+  const before = loadCurrentFailuresCount();
   try { await scanPm2(state); } catch (e) { log('scanPm2 threw:', e.message); }
   try { await scanLaunchdErrLogs(state); } catch (e) { log('scanLaunchdErrLogs threw:', e.message); }
   saveState(state);
+  const after = loadCurrentFailuresCount();
+  cyclesSinceLastHeartbeat++;
+  if (after === before && cyclesSinceLastHeartbeat >= HEARTBEAT_EVERY) {
+    log(`heartbeat — ${cyclesSinceLastHeartbeat} quiet cycles, ${Object.keys(state.pm2).length} pm2 tracked, ${Object.keys(state.logOffsets).length} logs tracked`);
+    cyclesSinceLastHeartbeat = 0;
+  } else if (after !== before) {
+    cyclesSinceLastHeartbeat = 0;
+  }
+}
+
+function loadCurrentFailuresCount() {
+  // Read directly from CNCP's file (cheaper than HTTP). Returns 0 if unreadable.
+  try {
+    const p = path.join(os.homedir(), 'cncp-starter', 'cncp-failures.json');
+    if (fs.existsSync(p)) return JSON.parse(fs.readFileSync(p, 'utf-8')).length;
+  } catch {}
+  return 0;
 }
 
 async function main() {

← f20012e initial scaffold — pm2 + launchd failure collector for CNCP  ·  back to Cncp Failure Collector  ·  tick 3: launchd plist-driven .err.log tailing (97 com.steve. c57181c →