Skip to content

stephen-shopopop/node-metrics

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

193 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

node-metrics

npm version License: ISC Node.js

πŸš€ Process load measuring plugin for Node.js with automatic "Service Unavailable" handling

A lightweight, production-ready middleware for monitoring Node.js application health and automatically handling server overload situations. Integrates seamlessly with popular frameworks (Koa, Express, Hono, Fastify) and provides Prometheus-compatible metrics.

✨ Features

  • 🎯 Automatic Overload Protection - Returns HTTP 503 when system is under pressure
  • πŸ“Š Real-time Metrics - Monitor event loop delay, heap usage, RSS, and event loop utilization
  • πŸ”Œ Framework Support - Works with Koa, Express, Hono, Fastify, and vanilla Node.js
  • πŸ“ˆ Prometheus Integration - Built-in metrics endpoint and dashboard
  • ⚑ Low Overhead - Uses setTimeout instead of setInterval to minimize system pressure
  • 🎨 Web Dashboard - Beautiful UI to visualize metrics in real-time

πŸ“¦ Installation

npm install @stephen-shopopop/node-metrics

πŸš€ Quick Start

Koa

const Koa = require('koa');
const { underPressureKoaMiddleware } = require('@stephen-shopopop/node-metrics');

const app = new Koa();

app.use(underPressureKoaMiddleware({
  appName: 'service-order',
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000,
  maxEventLoopUtilization: 0.98,
  retryAfter: 10,
  webServerMetricsPort: 9090
}));

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

Express

const express = require('express');
const { underPressureExpressMiddleware } = require('@stephen-shopopop/node-metrics');

const app = express();
const port = 3000;

app.use(underPressureExpressMiddleware({
  appName: 'service-order',
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000,
  maxEventLoopUtilization: 0.98,
  retryAfter: 10,
  webServerMetricsPort: 9090
}));

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Hono

import { Hono } from 'hono';
import { underPressureHonoMiddleware } from '@stephen-shopopop/node-metrics';

const app = new Hono();

app.use('*', underPressureHonoMiddleware({
  appName: 'service-order',
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000,
  maxEventLoopUtilization: 0.98,
  retryAfter: 10,
  webServerMetricsPort: 9090
}));

app.get('/', (c) => c.text('Hello Hono!'));

export default app;

Fastify

import Fastify from 'fastify';
import { underPressureFastifyPlugin } from '@stephen-shopopop/node-metrics';

const fastify = Fastify();

fastify.register(underPressureFastifyPlugin({
  appName: 'service-order',
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000,
  maxEventLoopUtilization: 0.98,
  retryAfter: 10,
  webServerMetricsPort: 9090
}));

fastify.get('/', async (request, reply) => {
  return { hello: 'world' };
});

fastify.listen({ port: 3000 });

Manual Integration (Vanilla Node.js)

import { createServer } from 'node:http';
import { Metrics, isUnderPressure } from '@stephen-shopopop/node-metrics';

const metrics = Metrics.start({
  appName: 'service-order',
  sampleIntervalInMs: 1000,
  resolution: 10,
  webServerMetricsPort: 9090
});

const options = {
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000,
  maxEventLoopUtilization: 0.98
};

const hostname = '127.0.0.1';
const port = 3000;

const server = createServer((req, res) => {
  if (isUnderPressure({ ...options, ...metrics.measures() })) {
    res.statusCode = 503;
    res.setHeader('Retry-After', '10');
    res.end('Service Unavailable');
    return;
  }

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Standalone Metrics Setup

You can set up metrics separately and preload them when starting your application:

// file: metrics.js
import { Metrics } from '@stephen-shopopop/node-metrics';

const metrics = Metrics.start({
  webServerMetricsPort: 9090,
  appName: 'service-test'
});

process.on('SIGTERM', () => {
  metrics
    .closeWebServerMetrics()
    .then(() => console.log('Metrics terminated'))
    .catch((error) => console.error('Error terminating metrics', error))
    .finally(() => process.exit(0));
});

Run your application with metrics preloaded:

node -r ./metrics.js app.js

βš™οΈ Configuration Options

Option Type Default Description
appName string - Required. Application name (format: service-name)
sampleIntervalInMs number 1000 Interval in milliseconds for metrics collection
resolution number 10 Resolution/granularity of collected metrics
webServerMetricsPort number - Port for Prometheus metrics server (recommended: 9090)
maxEventLoopDelay number - Maximum allowed event loop delay (ms)
maxEventLoopUtilization number - Maximum event loop utilization (0-1, e.g., 0.98 = 98%)
maxHeapUsedBytes number - Maximum heap memory usage in bytes
maxRssBytes number - Maximum Resident Set Size in bytes
retryAfter number - Seconds to wait before retrying (sent in Retry-After header)

Example Configuration

{
  appName: 'service-order',
  sampleIntervalInMs: 1000,        // Collect metrics every second
  resolution: 10,                   // Keep last 10 samples
  maxEventLoopDelay: 1000,         // Alert if event loop delay > 1000ms
  maxEventLoopUtilization: 0.98,   // Alert if event loop > 98% utilized
  maxHeapUsedBytes: 100000000,     // Alert if heap > 100MB
  maxRssBytes: 100000000,          // Alert if RSS > 100MB
  retryAfter: 10,                  // Tell clients to retry after 10s
  webServerMetricsPort: 9090       // Serve metrics on port 9090
}

πŸ“Š Monitoring & Metrics

Dashboard UI

Access the beautiful real-time dashboard at:

http://127.0.0.1:9090

Dashboard UI

Prometheus Metrics Endpoint

Metrics are exposed in Prometheus format at:

http://127.0.0.1:9090/metrics

Available Metrics:

# HELP nodejs_event_loop_delay_milliseconds The mean of the recorded event loop delays
# TYPE nodejs_event_loop_delay_milliseconds gauge
nodejs_event_loop_delay_milliseconds{service="service-order"} 0.9878575824175826

# HELP nodejs_event_loop_utilized The percentage of event loop utilization
# TYPE nodejs_event_loop_utilized gauge
nodejs_event_loop_utilized{service="service-order"} 0.10445105761836926

# HELP nodejs_heap_used_bytes The amount of memory used by the V8 heap
# TYPE nodejs_heap_used_bytes gauge
nodejs_heap_used_bytes{service="service-order"} 32637488

# HELP nodejs_heap_total_bytes The total size of the V8 heap
# TYPE nodejs_heap_total_bytes gauge
nodejs_heap_total_bytes{service="service-order"} 34684928

# HELP nodejs_rss_bytes The resident set size, or total memory allocated for the process
# TYPE nodejs_rss_bytes gauge
nodejs_rss_bytes{service="service-order"} 179077120

# HELP nodejs_process_start_time_seconds The process start time, represented in seconds since the Unix epoch
# TYPE nodejs_process_start_time_seconds gauge
nodejs_process_start_time_seconds{service="service-order"} 1750345329

# HELP nodejs_process_cpu_user_seconds_total The total user CPU time consumed by the process, in seconds
# TYPE nodejs_process_cpu_user_seconds_total counter
nodejs_process_cpu_user_seconds_total{service="service-order"} 1.494779

# HELP nodejs_process_cpu_system_seconds_total The total system CPU time consumed by the process, in seconds
# TYPE nodejs_process_cpu_system_seconds_total counter
nodejs_process_cpu_system_seconds_total{service="service-order"} 0.120983

# HELP nodejs_process_cpu_seconds_total The total CPU time (user + system) consumed by the process, in seconds
# TYPE nodejs_process_cpu_seconds_total counter
nodejs_process_cpu_seconds_total{service="service-order"} 1.615762

# HELP nodejs_active_handles Number of active libuv handles grouped by handle type
# TYPE nodejs_active_handles gauge
nodejs_active_handles{service="service-order",type="WriteStream"} 2
nodejs_active_handles{service="service-order",type="ReadStream"} 1
nodejs_active_handles{service="service-order",type="Server"} 1
nodejs_active_handles{service="service-order",type="Socket"} 1

πŸ” How It Works

setTimeout vs setInterval

Under the hood, node-metrics uses setTimeout to perform polling checks instead of setInterval. This design choice is intentional to avoid adding additional pressure to an already stressed system.

Why setTimeout?

  • setInterval calls repeatedly at scheduled intervals regardless of whether the previous call completed, potentially piling up calls when the server is under load
  • setTimeout is called only once and reschedules itself after completion, preventing call accumulation
  • This ensures the monitoring itself doesn't contribute to system overload

Note: The two methods are not identical in behavior. The timer function is not guaranteed to run at the exact same rate when the system is under pressure or running long processes, but this trade-off is acceptable for health monitoring purposes.

Architecture Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          APPLICATION LAYER                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚     Koa      β”‚  β”‚   Express    β”‚  β”‚     Hono     β”‚  β”‚   Fastify    β”‚ β”‚
β”‚  β”‚  Application β”‚  β”‚  Application β”‚  β”‚  Application β”‚  β”‚  Application β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚         β”‚                 β”‚                 β”‚                 β”‚         β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                                     β”‚                                   β”‚
β”‚                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚                      β”‚   Middleware Integration    β”‚                    β”‚
β”‚                      β”‚  underPressure*Middleware() β”‚                    β”‚
β”‚                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           CORE LAYER                                   β”‚
β”‚                                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚  β”‚              Metrics (Singleton)                         β”‚          β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚          β”‚
β”‚  β”‚  β”‚  start()  β”‚  register()  β”‚  measures()  β”‚ destroy()β”‚  β”‚          β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚          β”‚
β”‚  β”‚                                                          β”‚          β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚          β”‚
β”‚  β”‚  β”‚   Timer     │──────▢│   Mediator   β”‚                  β”‚          β”‚
β”‚  β”‚  β”‚(setTimeout) β”‚      β”‚  (Plugins)   β”‚                   β”‚          β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚          β”‚
β”‚  β”‚                              β”‚                           β”‚          β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚          β”‚
β”‚  β”‚  β”‚            StoreBuilder (Metrics Storage)          β”‚  β”‚          β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚          β”‚
β”‚  β”‚                              β”‚                           β”‚          β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚          β”‚
β”‚  β”‚  β”‚         BroadcastChannel (Pub/Sub)                 β”‚  β”‚          β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β”‚                              β”‚                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    Metrics Server                                β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚  β”‚
β”‚  β”‚  β”‚  GET /       β”‚  β”‚ GET /metrics β”‚  β”‚ GET /metrics-stream  β”‚    β”‚  β”‚
β”‚  β”‚  β”‚  Dashboard   β”‚  β”‚  Prometheus  β”‚  β”‚    SSE Stream        β”‚    β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         PLUGIN LAYER                                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚   Memory     β”‚  β”‚  Event Loop  β”‚  β”‚   Event Loop             β”‚      β”‚
β”‚  β”‚   Usage      β”‚  β”‚    Delay     β”‚  β”‚   Utilization            β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚  Process CPU β”‚  β”‚   Process    β”‚  β”‚   Active Handles/        β”‚      β”‚
β”‚  β”‚    Usage     β”‚  β”‚   Uptime     β”‚  β”‚   Resources              β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


πŸ“Š Request Flow with Pressure Detection:

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  HTTP       β”‚
  β”‚  Request    β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Middleware     β”‚
  β”‚   Check          β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  measures()      β”‚
  β”‚  Get current     β”‚
  β”‚  metrics         β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  isUnderPressure()           β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚  β”‚ Event Loop Delay > max?β”‚  β”‚
  β”‚  β”‚ Heap Usage > max?      β”‚  β”‚
  β”‚  β”‚ RSS Memory > max?      β”‚  β”‚
  β”‚  β”‚ EL Utilization > max?  β”‚  β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚             β”‚
    Yes  β”‚             β”‚  No
         β–Ό             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Return 503 β”‚  β”‚  Continue to β”‚
  β”‚  Service    β”‚  β”‚  next()      β”‚
  β”‚  Unavailableβ”‚  β”‚              β”‚
  β”‚  Retry-Afterβ”‚  β”‚              β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


⏱️  Periodic Metrics Collection (Every sampleIntervalInMs):

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  setTimeout  β”‚
  β”‚  Trigger     β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Mediator        β”‚
  β”‚  Iterate Plugins β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Each Plugin.capture(StoreBuilder)       β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚  β”‚ MemoryUsagePlugin                  β”‚  β”‚
  β”‚  β”‚   β†’ heap_used_bytes                β”‚  β”‚
  β”‚  β”‚   β†’ rss_bytes                      β”‚  β”‚
  β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”‚
  β”‚  β”‚ EventLoopDelayPlugin               β”‚  β”‚
  β”‚  β”‚   β†’ event_loop_delay_milliseconds  β”‚  β”‚
  β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”‚
  β”‚  β”‚ EventLoopUtilizationPlugin         β”‚  β”‚
  β”‚  β”‚   β†’ event_loop_utilized            β”‚  β”‚
  β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”‚
  β”‚  β”‚ ProcessCpuUsagePlugin              β”‚  β”‚
  β”‚  β”‚   β†’ process_cpu_*_seconds_total    β”‚  β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  StoreBuilder    β”‚
  β”‚  Update Metrics  β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ BroadcastChannel β”‚
  β”‚ Publish Update   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Subscribers (Dashboard,     β”‚
  β”‚  Metrics Stream, etc.)       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“š Documentation

For detailed API documentation and advanced usage:

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“ License

ISC Β© Stephen Deletang

πŸ”— Links


Made with ❀️ by Stephen Deletang