Introduction: Why Monitoring is Critical for Node.js Applications
Modern JavaScript web applications (Node.js) operate under high load and distributed architectures. Even perfectly written code can fail due to memory leaks, performance degradation, or errors in asynchronous operations. Monitoring JavaScript applications allows not only to detect a problem but also to prevent it before it affects users.
In this article, we will break down which metrics need to be tracked, how to configure data collection using popular tools (Prometheus, Grafana), and how to organize an alerting system for Node.js. All code examples are written in JavaScript and ready for use.
Key Metrics for Node.js Applications
Monitoring Node.js applications can be divided into three levels: infrastructure, application, and business metrics. Let's look at each of them.
Infrastructure Metrics
These metrics show the state of the server and runtime environment:
- CPU and Memory (RSS, Heap Total, Heap Used) — memory leaks and high CPU load.
- Event Loop Lag — event loop delay (if >50ms, the application starts to slow down).
- Garbage Collection (GC) — frequency and duration of garbage collection.
- File Descriptors — number of open files and sockets.
Application Metrics
Show how the application processes requests:
- RPS (Requests Per Second) — number of requests per second.
- Latency (p50, p95, p99) — response delay.
- Error Rate — percentage of errors (4xx, 5xx).
- Active Connections — number of active WebSocket/HTTP connections.
Business Metrics
Specific to your application:
- Number of registered users.
- Payment processing speed.
- External API response time.
Collecting Metrics with Prometheus and Node.js
Prometheus is the de facto standard for collecting metrics in modern infrastructure. For Node.js, there is the prom-client library, which allows you to easily export metrics.
Installing and Configuring prom-client
npm install prom-client expressLet's create a basic server with metrics:
const express = require('express');const client = require('prom-client');const app = express();
// Create a metric registryconst register = new client.Registry();client.collectDefaultMetrics({ register });
// Create a custom histogram for latenciesconst httpRequestDuration = new client.Histogram({ name: 'http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'status_code'], buckets: [0.1, 0.5, 1, 2, 5]});register.registerMetric(httpRequestDuration);
// Middleware for measuring latenciesapp.use((req, res, next) => { const end = httpRequestDuration.startTimer(); res.on('finish', () => { end({ method: req.method, route: req.route?.path || 'unknown', status_code: res.statusCode }); }); next();});
// Endpoint for metricsapp.get('/metrics', async (req, res) => { res.set('Content-Type', register.contentType); res.end(await register.metrics());});
app.get('/', (req, res) => { res.send('Hello World');});
app.listen(3000, () => console.log('Server running on port 3000'));Collecting Event Loop Metrics
For monitoring the event loop, use process.hrtime() or the event-loop-lag library:
const eventLoopLag = new client.Gauge({ name: 'nodejs_event_loop_lag_seconds', help: 'Event loop lag in seconds'});register.re