Monitoring Node.js Applications
Why Monitor?
Monitoring helps detect issues, track performance, understand user behavior, and ensure application health in production.
Application Performance Monitoring (APM)
New Relic
require('newrelic');
const express = require('express');
const app = express();
// Automatic instrumentation
app.get('/api/users', async (req, res) => {
const users = await User.find();
res.json(users);
});Datadog
const tracer = require('dd-trace').init();
app.get('/api/products', async (req, res) => {
const span = tracer.startSpan('get.products');
try {
const products = await Product.find();
res.json(products);
} finally {
span.finish();
}
});Logging
Winston
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
new winston.transports.Console({
format: winston.format.simple()
})
]
});
// Usage
logger.info('Server started', { port: 3000 });
logger.error('Database error', { error: err.message });Morgan (HTTP Logging)
const morgan = require('morgan');
// Predefined formats
app.use(morgan('combined')); // Apache combined format
app.use(morgan('dev')); // Development format
// Custom format
app.use(morgan(':method :url :status :response-time ms'));
// Log to file
const fs = require('fs');
const accessLogStream = fs.createWriteStream(
path.join(__dirname, 'access.log'),
{ flags: 'a' }
);
app.use(morgan('combined', { stream: accessLogStream }));Metrics Collection
prom-client (Prometheus)
const client = require('prom-client');
// Create a Registry
const register = new client.Registry();
// Add default metrics
client.collectDefaultMetrics({ register });
// Custom metrics
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
registers: [register]
});
// Middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration
.labels(req.method, req.route?.path || req.path, res.statusCode)
.observe(duration);
});
next();
});
// Metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});Health Checks
Comprehensive Health Endpoint
app.get('/health', async (req, res) => {
const health = {
uptime: process.uptime(),
timestamp: Date.now(),
status: 'healthy',
checks: {}
};
try {
// Database check
await mongoose.connection.db.admin().ping();
health.checks.database = 'healthy';
} catch (err) {
health.checks.database = 'unhealthy';
health.status = 'unhealthy';
}
try {
// Redis check
await redis.ping();
health.checks.redis = 'healthy';
} catch (err) {
health.checks.redis = 'unhealthy';
health.status = 'unhealthy';
}
// Memory check
const used = process.memoryUsage();
health.checks.memory = {
heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
rss: `${Math.round(used.rss / 1024 / 1024)}MB`
};
const statusCode = health.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(health);
});Error Tracking
Sentry
const Sentry = require('@sentry/node');
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0
});
// Error handler
app.use(Sentry.Handlers.errorHandler());
// Manual error capture
try {
await riskyOperation();
} catch (err) {
Sentry.captureException(err);
throw err;
}Performance Monitoring
Memory Monitoring
setInterval(() => {
const used = process.memoryUsage();
logger.info('Memory usage', {
rss: `${Math.round(used.rss / 1024 / 1024)}MB`,
heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
external: `${Math.round(used.external / 1024 / 1024)}MB`
});
}, 60000);CPU Monitoring
const os = require('os');
setInterval(() => {
const cpus = os.cpus();
const usage = cpus.map(cpu => {
const total = Object.values(cpu.times).reduce((a, b) => a + b);
const idle = cpu.times.idle;
return ((total - idle) / total) * 100;
});
logger.info('CPU usage', {
average: usage.reduce((a, b) => a + b) / usage.length,
cores: usage
});
}, 60000);Request Tracking
Request ID Middleware
const { v4: uuidv4 } = require('uuid');
app.use((req, res, next) => {
req.id = req.headers['x-request-id'] || uuidv4();
res.setHeader('X-Request-ID', req.id);
next();
});
// Use in logging
app.use((req, res, next) => {
logger.info('Request received', {
requestId: req.id,
method: req.method,
url: req.url,
ip: req.ip
});
next();
});Alerting
Custom Alerts
const alertThresholds = {
errorRate: 0.05, // 5%
responseTime: 1000, // 1 second
memoryUsage: 0.9 // 90%
};
function checkAlerts() {
const metrics = getMetrics();
if (metrics.errorRate > alertThresholds.errorRate) {
sendAlert('High error rate', metrics.errorRate);
}
if (metrics.avgResponseTime > alertThresholds.responseTime) {
sendAlert('Slow response time', metrics.avgResponseTime);
}
const memUsage = process.memoryUsage().heapUsed / process.memoryUsage().heapTotal;
if (memUsage > alertThresholds.memoryUsage) {
sendAlert('High memory usage', memUsage);
}
}
setInterval(checkAlerts, 60000);Dashboard
Express Status Monitor
const expressStatusMonitor = require('express-status-monitor');
app.use(expressStatusMonitor({
title: 'API Status',
path: '/status',
spans: [{
interval: 1,
retention: 60
}],
chartVisibility: {
cpu: true,
mem: true,
load: true,
responseTime: true,
rps: true,
statusCodes: true
}
}));Best Practices
- Log everything important but avoid sensitive data
- Use structured logging (JSON format)
- Implement health checks for all dependencies
- Track custom metrics relevant to your business
- Set up alerts for critical issues
- Monitor resource usage (CPU, memory, disk)
- Track error rates and response times
- Use APM tools in production
Interview Tips
- Explain monitoring importance: Detect issues, track performance
- Show logging: Winston, Morgan for HTTP logs
- Demonstrate metrics: Prometheus, custom metrics
- Discuss health checks: Database, Redis, memory
- Mention APM tools: New Relic, Datadog
- Show error tracking: Sentry integration
Summary
Monitor Node.js applications using logging (Winston, Morgan), metrics collection (Prometheus), health checks, APM tools (New Relic, Datadog), and error tracking (Sentry). Track memory, CPU, response times, and error rates. Set up alerts for critical issues.
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.