npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hono-status-monitor

v1.0.7

Published

Real-time server monitoring dashboard for Hono.js. Works with Node.js and Cloudflare Workers. Express-status-monitor style metrics with polling updates.

Readme

hono-status-monitor

npm version License: MIT

Real-time server monitoring dashboard for Hono.js applications. Works with Node.js and Cloudflare Workers.

✨ Features

| Feature | Description | |---------|-------------| | 7 Real-Time Metrics | CPU, Memory, Heap, Load Average, Response Time, RPS, Event Loop Lag | | Response Percentiles | P50, P95, P99 latency tracking for accurate performance insights | | Route Analytics | Top routes by traffic, slowest routes, routes with most errors | | Error Tracking | Recent errors with timestamps, paths, and status codes | | Visual Alerts | Automatic warnings when CPU >80%, Memory >90%, Response >500ms | | Dark Mode | Toggle with localStorage persistence | | Polling Updates | Configurable polling interval (1s default for Node.js, 5s for edge) | | Edge Support | Works in Cloudflare Workers and edge runtimes | | Configurable | Custom thresholds, paths, titles, polling intervals, and more |

📦 Installation

npm install hono-status-monitor
# or
yarn add hono-status-monitor
# or
pnpm add hono-status-monitor

🚀 Quick Start

import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { statusMonitor } from 'hono-status-monitor';

const app = new Hono();

// Create status monitor
const monitor = statusMonitor();

// Add middleware to track ALL requests (must be first!)
app.use('*', monitor.middleware);

// Mount status dashboard at /status
app.route('/status', monitor.routes);

// Your routes
app.get('/', (c) => c.text('Hello World!'));

// Start server
const server = serve({ fetch: app.fetch, port: 3000 });

// Optional: initSocket is now a no-op, can be removed
// monitor.initSocket(server);

console.log('📊 Status monitor: http://localhost:3000/status');

⚙️ Configuration

const monitor = statusMonitor({
    // Dashboard path (default: '/status')
    path: '/status',
    
    // Dashboard title (default: 'Server Status')
    title: 'My App Status',
    
    // Dashboard polling interval in ms (default: 1000 for Node.js, 5000 for edge)
    pollingInterval: 1000,
    
    // Metrics collection interval in ms (default: 1000)
    updateInterval: 1000,
    
    // History retention in seconds (default: 60)
    retentionSeconds: 60,
    
    // Max recent errors to store (default: 10)
    maxRecentErrors: 10,
    
    // Max routes to show in analytics (default: 10)
    maxRoutes: 10,
    
    // Alert thresholds
    alerts: {
        cpu: 80,           // CPU percentage
        memory: 90,        // Memory percentage
        responseTime: 500, // Response time in ms
        errorRate: 5,      // Error rate percentage
        eventLoopLag: 100  // Event loop lag in ms
    },
    
    // Custom database health check (optional)
    healthCheck: async () => {
        const start = performance.now();
        await mongoose.connection.db?.admin().ping();
        return {
            connected: mongoose.connection.readyState === 1,
            latencyMs: performance.now() - start,
            name: 'MongoDB'
        };
    },
    
    // Custom path normalization (optional)
    normalizePath: (path) => {
        return path
            .replace(/\/users\/\d+/g, '/users/:id')
            .replace(/\/posts\/[a-f0-9]{24}/g, '/posts/:id');
    },
    
    // Enable cluster mode for PM2 (auto-detected by default)
    clusterMode: true
});

📊 Dashboard Sections

Header

  • Server hostname
  • Connection status (Live/Offline)
  • Dark mode toggle

Stats Bar

  • Uptime
  • Total requests
  • Active connections
  • Error rate

Response Percentiles

  • Average response time
  • P50 (median)
  • P95
  • P99

Real-Time Charts

  • CPU usage
  • Memory usage (MB)
  • Heap usage (MB)
  • Load average
  • Response time (ms)
  • Requests per second
  • Event loop lag (ms)

Route Analytics

  • 🔥 Top Routes (by request count)
  • 🐢 Slowest Routes (by avg response time)

HTTP Status Codes

  • 2xx success count
  • 3xx redirect count
  • 4xx client error count
  • 5xx server error count
  • Rate limited count

Recent Errors

  • Last 10 errors with timestamp, path, and status code

Health Checks

  • Database connection status and latency
  • Heap total and growth rate

Process Info

  • Node.js version
  • Platform
  • PID
  • CPU count

🔌 API Endpoints

| Endpoint | Description | |----------|-------------| | GET /status | Dashboard HTML page | | GET /status/api/metrics | JSON API with full metrics snapshot |

JSON API Response

{
  "snapshot": {
    "timestamp": 1704067200000,
    "cpu": 12.5,
    "memoryMB": 256.4,
    "memoryPercent": 15.8,
    "heapUsedMB": 48.2,
    "heapTotalMB": 64.0,
    "loadAvg": 1.25,
    "uptime": 86400,
    "processUptime": 3600,
    "responseTime": 15.4,
    "rps": 125,
    "totalRequests": 450000,
    "activeConnections": 42,
    "eventLoopLag": 2.1,
    "percentiles": {
      "avg": 15.4,
      "p50": 12.0,
      "p95": 45.0,
      "p99": 120.0
    },
    "topRoutes": [...],
    "slowestRoutes": [...],
    "recentErrors": [...],
    "alerts": {
      "cpu": false,
      "memory": false,
      "responseTime": false,
      "errorRate": false,
      "eventLoopLag": false
    },
    "database": {
      "connected": true,
      "latencyMs": 1.2
    }
  },
  "charts": {
    "cpu": [{ "timestamp": 1704067200000, "value": 12.5 }, ...],
    "memory": [...],
    "heap": [...],
    "loadAvg": [...],
    "responseTime": [...],
    "rps": [...],
    "eventLoopLag": [...],
    "errorRate": [...]
  }
}

🎛️ Advanced Usage

Rate Limit Tracking

// In your rate limiter middleware
import { monitor } from './your-app';

if (isRateLimited) {
    monitor.trackRateLimit(true);  // Track blocked request
    return c.text('Too many requests', 429);
}
monitor.trackRateLimit(false);  // Track allowed request

Custom Path Normalization

Group similar routes for meaningful analytics:

const monitor = statusMonitor({
    normalizePath: (path) => {
        return path
            // Replace user IDs
            .replace(/\/users\/\d+/g, '/users/:id')
            // Replace MongoDB ObjectIds
            .replace(/\/[a-f0-9]{24}/g, '/:id')
            // Replace UUIDs
            .replace(/\/[0-9a-f-]{36}/g, '/:uuid');
    }
});

Multiple Health Checks

const monitor = statusMonitor({
    healthCheck: async () => {
        const dbStart = performance.now();
        const dbConnected = mongoose.connection.readyState === 1;
        
        if (dbConnected) {
            await mongoose.connection.db?.admin().ping();
        }
        
        return {
            connected: dbConnected,
            latencyMs: performance.now() - dbStart,
            name: 'MongoDB'
        };
    }
});

PM2 / Cluster Mode Support

To enable metrics aggregation across multiple processes (Cluster Mode), you need a specific entry point script that handles message relaying between workers.

  1. Create a cluster.ts (or .js) entry file:
import cluster from 'node:cluster';
import * as os from 'node:os';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const numCPUs = os.cpus().length;

if (cluster.isPrimary) {
    console.log(`Primary ${process.pid} is running`);

    // Fork workers
    for (let i = 0; i < numCPUs; i++) {
        const worker = cluster.fork();
        
        // Relay messages between workers
        worker.on('message', (message) => {
            if (message?.type === 'worker-metrics') {
                // Broadcast to all workers (including sender)
                for (const id in cluster.workers) {
                    cluster.workers[id]?.send(message);
                }
            }
        });
    }

    cluster.on('exit', (worker) => {
        console.log(`worker ${worker.process.pid} died`);
        // Replace dead worker
        const newWorker = cluster.fork();
        newWorker.on('message', (message) => {
            if (message?.type === 'worker-metrics') {
                for (const id in cluster.workers) {
                    cluster.workers[id]?.send(message);
                }
            }
        });
    });
} else {
    // Workers share the TCP connection in this file
    await import('./index'); // Path to your main app file
}
  1. Run this cluster script with PM2:
# Start the cluster script as a SINGLE PM2 instance
# (The script itself manages the worker processes)
pm2 start cluster.ts --name my-app

Note: Do NOT use pm2 start app.ts -i max directly, as PM2's default isolation prevents workers from sharing metrics without an external adapter (like Redis). Using the script above provides a zero-dependency aggregation solution.

🛡️ Security Considerations

The status dashboard exposes server metrics. Consider:

  1. Authentication: Add middleware to protect the /status route
  2. Rate Limiting: Limit access to the dashboard
  3. Internal Only: Only expose on internal networks
import { basicAuth } from 'hono/basic-auth';

// Protect status routes
app.use('/status/*', basicAuth({
    username: 'admin',
    password: process.env.STATUS_PASSWORD!
}));

app.route('/status', monitor.routes);

☁️ Cloudflare Workers / Edge Support

This package provides a separate edge entry point that has zero Node.js dependencies.

Available Metrics in Edge Environments

| Metric | Available | Notes | |--------|-----------|-------| | Request count & RPS | ✅ | Fully supported | | Response time percentiles | ✅ | P50, P95, P99, Avg | | Status code breakdown | ✅ | 2xx, 3xx, 4xx, 5xx | | Route analytics | ✅ | Top routes, slowest routes | | Error tracking | ✅ | Recent errors with timestamps | | CPU / Memory / Heap | ❌ | Not available in Workers | | Event loop lag | ❌ | Not available in Workers | | Load average | ❌ | Not available in Workers | | WebSocket real-time | ❌ | Uses polling (configurable, default 5s) |

Usage in Cloudflare Workers

Important: Use the /edge import path for Cloudflare Workers!

import { Hono } from 'hono';
// Use the edge-specific import (no Node.js dependencies)
import { statusMonitor } from 'hono-status-monitor/edge';

const app = new Hono();

// Create status monitor with custom polling interval
const monitor = statusMonitor({
    pollingInterval: 3000  // Update dashboard every 3 seconds (default: 5000)
});

// Add middleware to track requests
app.use('*', monitor.middleware);

// Mount status dashboard
app.route('/status', monitor.routes);

// Your routes
app.get('/', (c) => c.text('Hello from Cloudflare Workers!'));

export default app;

Note: In edge environments, the dashboard uses HTTP polling instead of WebSocket for updates. The polling interval is configurable via pollingInterval (defaults to 5 seconds for edge, 1 second for Node.js).

Force Edge Mode in Node.js

You can also use the edge-compatible monitor in Node.js if you don't need system metrics:

import { statusMonitor } from 'hono-status-monitor/edge';

const monitor = statusMonitor();

📋 Requirements

  • Node.js >= 18.0.0 (for Node.js mode)
  • Hono.js >= 4.0.0
  • @hono/node-server >= 1.0.0 (for Node.js mode only)

🤝 Contributing

Contributions welcome! Please read the contributing guidelines first.

📄 License

MIT © Vinit Kumar Goel


Made with ❤️ for the Hono.js community