tutorial

Monitoring Your Etch.js App with Vigilmon: Health Checks, Heartbeats & Uptime Alerts

Add production-grade monitoring to an Etch.js application — a health API, background job heartbeats, and instant alerts when your Atom/Electron plugin or backend goes down.

Etch.js is a small, JSX-compatible virtual DOM library purpose-built for building UI components in Atom editor packages and Electron applications. Its synchronous rendering model makes it reliable for editor tooling, but that reliability depends entirely on your backend services staying up. Vigilmon gives you the visibility you need: continuous HTTP monitoring, heartbeat checks, and instant alerts via email or Slack when something goes wrong.

What You'll Build

  • A health API endpoint for your Etch.js backend or Electron app's companion server
  • Vigilmon HTTP monitors for your app's web presence and health endpoint
  • A heartbeat for scheduled background tasks (file watchers, sync jobs)
  • Alert channels (email and Slack)
  • An uptime status indicator in your Etch.js UI

Prerequisites

  • An Etch.js project (Atom package or Electron app)
  • Node.js backend or companion server for the health endpoint
  • A free Vigilmon account

Step 1: Add a Health Endpoint

Etch.js renders UI in the browser/Electron renderer process. Health checks belong in your main process or companion HTTP server:

// main-server.js (Electron main process or standalone Node server)
const http = require("http");

const server = http.createServer(async (req, res) => {
  if (req.url === "/health" && req.method === "GET") {
    const checks = {};
    let degraded = false;

    // Check local file system or plugin state
    try {
      const fs = require("fs");
      fs.accessSync(process.env.PLUGIN_DATA_DIR ?? ".", fs.constants.R_OK);
      checks.filesystem = "ok";
    } catch (err) {
      checks.filesystem = `error: ${err.message}`;
      degraded = true;
    }

    // Check remote API (e.g. language server)
    try {
      const resp = await fetch("http://localhost:2089/health", {
        signal: AbortSignal.timeout(2000),
      });
      checks.languageServer = resp.ok ? "ok" : `http_${resp.status}`;
    } catch {
      checks.languageServer = "unreachable";
      // Not critical for all users — leave degraded = false here
    }

    const body = JSON.stringify({
      status: degraded ? "degraded" : "ok",
      timestamp: new Date().toISOString(),
      checks,
    });

    res.writeHead(degraded ? 503 : 200, {
      "Content-Type": "application/json",
      "Content-Length": Buffer.byteLength(body),
    });
    res.end(body);
  } else {
    res.writeHead(404);
    res.end();
  }
});

server.listen(process.env.HEALTH_PORT ?? 9090, () => {
  console.log("Health server on port", process.env.HEALTH_PORT ?? 9090);
});

Test it:

curl http://localhost:9090/health | jq .
# {"status":"ok","timestamp":"2026-06-29T...","checks":{"filesystem":"ok","languageServer":"ok"}}

Step 2: Configure Vigilmon HTTP Monitors

Log in to VigilmonMonitors → New Monitor:

Monitor 1: App Web Presence (if applicable)

| Field | Value | |---|---| | URL | https://yourapp.com/ | | Method | GET | | Expected status | 200 | | Expected body | Stable string from your landing page | | Check interval | 1 minute |

Monitor 2: Health API

| Field | Value | |---|---| | URL | https://yourapp.com/health or http://your-vps:9090/health | | Method | GET | | Expected status | 200 | | Check interval | 1 minute |


Step 3: Heartbeat for Background Tasks

Etch.js–backed tools often run file watchers, background indexers, or sync tasks. A Vigilmon heartbeat catches silent failures:

// background-tasks.js
const VIGILMON_HEARTBEAT_URL = process.env.VIGILMON_HEARTBEAT_URL ?? "";

async function pingHeartbeat() {
  if (!VIGILMON_HEARTBEAT_URL) return;
  try {
    await fetch(VIGILMON_HEARTBEAT_URL, { signal: AbortSignal.timeout(5000) });
  } catch {
    // Never crash the background task due to monitoring failure
  }
}

async function runIndexer() {
  // Your file indexing / background sync logic
  console.log("Running background indexer...");
}

// Run every 15 minutes
setInterval(async () => {
  try {
    await runIndexer();
    await pingHeartbeat();
  } catch (err) {
    console.error("[background] Indexer failed:", err);
    // No ping → Vigilmon alert fires after grace window
  }
}, 15 * 60 * 1000);

Set up the heartbeat in Vigilmon:

  1. Monitors → New Heartbeat Monitor
  2. Set Expected interval to 30 minutes
  3. Copy the ping URL to your .env or plugin settings:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx

Step 4: Display Uptime Status in Your Etch.js UI

Etch.js components use a synchronous render() method and update() for state changes:

/** @jsx etch.dom */
const etch = require("etch");

const BADGE_URL = "https://vigilmon.online/api/badge/your-monitor-id.svg";
const STATUS_URL = "https://vigilmon.online/status/your-monitor-slug";

class StatusBadgeComponent {
  constructor() {
    this.loaded = false;
    etch.initialize(this);
  }

  render() {
    return (
      <a
        href={STATUS_URL}
        target="_blank"
        rel="noopener noreferrer"
        aria-label="Service uptime status"
        style="display: inline-flex; align-items: center;"
      >
        <img
          src={BADGE_URL}
          alt="Uptime"
          width="120"
          height="20"
          on={{ load: () => this.markLoaded() }}
          style={`opacity: ${this.loaded ? 1 : 0}; transition: opacity 0.2s;`}
        />
      </a>
    );
  }

  markLoaded() {
    this.loaded = true;
    etch.update(this);
  }

  async update() {
    await etch.update(this);
  }

  async destroy() {
    await etch.destroy(this);
  }
}

module.exports = StatusBadgeComponent;

Use it inside an Atom panel or Electron view:

const StatusBadgeComponent = require("./status-badge-component");

// In your package activate() or Electron window
const badge = new StatusBadgeComponent();
document.querySelector("#footer").appendChild(badge.element);

Step 5: Set Up Alert Channels

In Vigilmon's Alert Channels settings:

Email

  1. Alert Channels → Email → add your on-call email
  2. Attach to both HTTP monitors and the heartbeat

Slack

  1. Create a Slack Incoming Webhook for #alerts
  2. Alert Channels → Webhook → paste the URL
  3. Payload template:
{
  "text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}

Step 6: Verify Everything Works

# 1. Health endpoint
curl -s http://localhost:9090/health | jq .

# 2. Simulate filesystem inaccessible → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes

# 3. Stop the background task → let heartbeat window expire
# Expected: heartbeat alert fires after the grace window

# 4. Vigilmon → "Test Alert" → confirm delivery to Slack/email

Summary

| Monitor | What It Catches | |---|---| | HTTP app check | Landing page or download server failures | | HTTP health API | Filesystem, language server, and dependency failures | | Heartbeat | Background indexer crashes, silent sync failures |


Next Steps

  • Create separate monitors for dev and production companion servers
  • Use Vigilmon's response time graphs to detect slow indexer runtimes
  • Configure multi-channel alerting: email for low-severity, Slack for critical

Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →