Structured logging is essential for debugging and monitoring production applications.
// Install: npm install winston
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Usage
logger.info('Server started', { port: 3000 });
logger.error('Database connection failed', { error: err.message });
// Health check endpoint
app.get('/health', (req, res) => {
const health = {
uptime: process.uptime(),
status: 'OK',
timestamp: Date.now(),
memory: process.memoryUsage()
};
res.json(health);
});
// Using PM2
// Install: npm install -g pm2
// pm2 start app.js -i max
// pm2 monit
// pm2 logs
// pm2 status
npm install compression// Compression example
const compression = require('compression');
app.use(compression());
// Simple in-memory cache
const cache = new Map();
async function getCachedData(key, fetchFn, ttl = 60) {
if (cache.has(key)) {
const { data, expiry } = cache.get(key);
if (Date.now() < expiry) return data;
cache.delete(key);
}
const data = await fetchFn();
cache.set(key, { data, expiry: Date.now() + ttl * 1000 });
return data;
}
Use child_process to run CPU-intensive operations in separate processes.
const { spawn, exec, fork } = require('child_process');
// Spawn (stream output)
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => console.log(`stdout: ${data}`));
// Exec (buffer output)
exec('node --version', (err, stdout, stderr) => {
if (err) return console.error(err);
console.log('Node version:', stdout);
});
// Fork (new Node.js process)
const child = fork('worker.js');
child.send({ task: 'compute' });
child.on('message', (result) => {
console.log('Worker result:', result);
});
The cluster module allows you to create child processes that share the same server port, utilizing all CPU cores.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master process ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died. Restarting...`);
cluster.fork(); // Auto-restart
});
} else {
// Workers share the HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Handled by worker ${process.pid}`);
}).listen(3000);
console.log(`Worker ${process.pid} started`);
}
For CPU-intensive JavaScript operations, use worker_threads which share the same process but run on separate threads.
// main.js
const { Worker } = require('worker_threads');
const worker = new Worker('./worker-thread.js', {
workerData: { iterations: 1000000 }
});
worker.on('message', (result) => {
console.log('Result from worker:', result);
});
worker.on('error', (err) => console.error(err));
// worker-thread.js
const { parentPort, workerData } = require('worker_threads');
function compute(iterations) {
let result = 0;
for (let i = 0; i < iterations; i++) {
result += Math.sqrt(i);
}
return result;
}
parentPort.postMessage(compute(workerData.iterations));
autocannon or wrk.