Back to Blog
Node.jsDebuggingPerformanceDevTools

Debugging Node.js Applications

Debug Node.js effectively. From console methods to debugger to memory leaks and performance profiling.

B
Bootspring Team
Engineering
June 10, 2022
5 min read

Effective debugging saves hours of frustration. Here's how to debug Node.js applications like a pro.

Console Methods#

1// Beyond console.log 2const user = { id: 1, name: 'John', email: 'john@example.com' }; 3const users = [user, { id: 2, name: 'Jane' }]; 4 5// Formatted output 6console.log('User:', user); 7console.log('User: %o', user); // Object format 8console.log('Name: %s, ID: %d', user.name, user.id); 9 10// Table format for arrays/objects 11console.table(users); 12console.table(users, ['id', 'name']); // Specific columns 13 14// Grouped output 15console.group('User Details'); 16console.log('ID:', user.id); 17console.log('Name:', user.name); 18console.groupEnd(); 19 20// Collapsed group 21console.groupCollapsed('Debug Info'); 22console.log('Detailed info here'); 23console.groupEnd(); 24 25// Timing 26console.time('fetchData'); 27await fetchData(); 28console.timeEnd('fetchData'); // fetchData: 150.23ms 29 30// Count calls 31function processItem() { 32 console.count('processItem called'); 33} 34 35// Stack trace 36console.trace('Where am I?'); 37 38// Assertions 39console.assert(user.id > 0, 'User ID should be positive'); 40 41// Clear console 42console.clear();

Node.js Inspector#

1# Start with inspector 2node --inspect app.js 3 4# Break on first line 5node --inspect-brk app.js 6 7# Custom port 8node --inspect=9230 app.js 9 10# Connect Chrome DevTools 11# Open chrome://inspect in Chrome 12# Click "inspect" under Remote Target
1// Programmatic breakpoints 2function processData(data) { 3 debugger; // Pauses here when inspector connected 4 return transform(data); 5} 6 7// Conditional breakpoint in DevTools 8// Right-click line -> Add conditional breakpoint 9// Enter condition: data.length > 100

VS Code Debugging#

1// .vscode/launch.json 2{ 3 "version": "0.2.0", 4 "configurations": [ 5 { 6 "name": "Debug Current File", 7 "type": "node", 8 "request": "launch", 9 "program": "${file}", 10 "skipFiles": ["<node_internals>/**"] 11 }, 12 { 13 "name": "Debug Application", 14 "type": "node", 15 "request": "launch", 16 "program": "${workspaceFolder}/src/index.ts", 17 "preLaunchTask": "tsc: build", 18 "outFiles": ["${workspaceFolder}/dist/**/*.js"], 19 "env": { 20 "NODE_ENV": "development" 21 } 22 }, 23 { 24 "name": "Debug Tests", 25 "type": "node", 26 "request": "launch", 27 "program": "${workspaceFolder}/node_modules/jest/bin/jest", 28 "args": ["--runInBand", "--no-cache"], 29 "console": "integratedTerminal" 30 }, 31 { 32 "name": "Attach to Process", 33 "type": "node", 34 "request": "attach", 35 "port": 9229 36 } 37 ] 38}

Error Handling Debug#

1// Catch uncaught exceptions 2process.on('uncaughtException', (error) => { 3 console.error('Uncaught Exception:', error); 4 console.error('Stack:', error.stack); 5 process.exit(1); 6}); 7 8// Catch unhandled promise rejections 9process.on('unhandledRejection', (reason, promise) => { 10 console.error('Unhandled Rejection at:', promise); 11 console.error('Reason:', reason); 12}); 13 14// Async stack traces (Node 12+) 15// Run with: node --async-stack-traces app.js 16 17// Error with cause 18function fetchUser(id: string) { 19 try { 20 return db.query(`SELECT * FROM users WHERE id = $1`, [id]); 21 } catch (error) { 22 throw new Error(`Failed to fetch user ${id}`, { cause: error }); 23 } 24} 25 26// Log full error chain 27function logErrorChain(error: Error, depth = 0) { 28 console.error(' '.repeat(depth) + error.message); 29 if (error.cause) { 30 logErrorChain(error.cause as Error, depth + 1); 31 } 32}

Memory Debugging#

1// Check memory usage 2function logMemory() { 3 const used = process.memoryUsage(); 4 console.log({ 5 heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)} MB`, 6 heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)} MB`, 7 external: `${Math.round(used.external / 1024 / 1024)} MB`, 8 rss: `${Math.round(used.rss / 1024 / 1024)} MB`, 9 }); 10} 11 12// Periodic memory logging 13setInterval(logMemory, 5000); 14 15// Heap snapshot 16const v8 = require('v8'); 17const fs = require('fs'); 18 19function takeHeapSnapshot() { 20 const snapshotStream = v8.writeHeapSnapshot(); 21 console.log('Heap snapshot written to:', snapshotStream); 22} 23 24// Trigger with signal 25process.on('SIGUSR2', takeHeapSnapshot); 26 27// Force garbage collection (run with --expose-gc) 28if (global.gc) { 29 global.gc(); 30 console.log('Garbage collection triggered'); 31}
1# Run with memory debugging 2node --expose-gc --max-old-space-size=4096 app.js 3 4# Generate heap snapshot on OOM 5node --heap-prof app.js 6 7# Analyze with Chrome DevTools Memory tab

Performance Profiling#

1// Built-in profiler 2const { performance, PerformanceObserver } = require('perf_hooks'); 3 4// Measure function execution 5performance.mark('start'); 6await expensiveOperation(); 7performance.mark('end'); 8performance.measure('operation', 'start', 'end'); 9 10// Observer for measurements 11const obs = new PerformanceObserver((items) => { 12 items.getEntries().forEach((entry) => { 13 console.log(`${entry.name}: ${entry.duration}ms`); 14 }); 15}); 16obs.observe({ entryTypes: ['measure'] }); 17 18// Wrap function for timing 19function withTiming<T>(name: string, fn: () => T): T { 20 const start = performance.now(); 21 try { 22 return fn(); 23 } finally { 24 console.log(`${name}: ${(performance.now() - start).toFixed(2)}ms`); 25 } 26} 27 28// Async version 29async function withTimingAsync<T>( 30 name: string, 31 fn: () => Promise<T> 32): Promise<T> { 33 const start = performance.now(); 34 try { 35 return await fn(); 36 } finally { 37 console.log(`${name}: ${(performance.now() - start).toFixed(2)}ms`); 38 } 39}
1# CPU profiling 2node --prof app.js 3node --prof-process isolate-*.log > profile.txt 4 5# Generate flamegraph 6node --perf-basic-prof app.js 7# Use perf and flamegraph tools

Debug Library#

1import debug from 'debug'; 2 3// Create namespaced loggers 4const logApp = debug('app'); 5const logDb = debug('app:db'); 6const logApi = debug('app:api'); 7const logAuth = debug('app:auth'); 8 9// Usage 10logApp('Application starting'); 11logDb('Connected to database'); 12logApi('Request received: %O', { method: 'GET', path: '/users' }); 13logAuth('User authenticated: %s', userId); 14 15// Enable via environment variable 16// DEBUG=app:* node app.js // All app logs 17// DEBUG=app:db node app.js // Only db logs 18// DEBUG=app:db,app:api node app.js // Multiple 19// DEBUG=* node app.js // Everything 20 21// In code 22debug.enable('app:db'); 23debug.disable('app:api');

Network Debugging#

1// Log all HTTP requests 2import http from 'http'; 3 4const originalRequest = http.request; 5http.request = function (options, callback) { 6 console.log('HTTP Request:', { 7 method: options.method || 'GET', 8 host: options.host || options.hostname, 9 path: options.path, 10 }); 11 return originalRequest.apply(this, arguments); 12}; 13 14// Axios interceptors 15import axios from 'axios'; 16 17axios.interceptors.request.use((config) => { 18 console.log('Request:', config.method?.toUpperCase(), config.url); 19 return config; 20}); 21 22axios.interceptors.response.use( 23 (response) => { 24 console.log('Response:', response.status, response.config.url); 25 return response; 26 }, 27 (error) => { 28 console.error('Request failed:', error.message); 29 return Promise.reject(error); 30 } 31);

Best Practices#

General: ✓ Use structured logging ✓ Add context to errors ✓ Use debug namespaces ✓ Remove debug code before production Tools: ✓ Master VS Code debugger ✓ Learn Chrome DevTools ✓ Use source maps ✓ Profile before optimizing Memory: ✓ Monitor memory usage ✓ Take heap snapshots ✓ Check for event listener leaks ✓ Review closure references

Conclusion#

Effective debugging combines multiple techniques. Use console methods for quick checks, the inspector for complex issues, and profiling tools for performance. Structure your logging with namespaces and levels to quickly isolate problems in production.

Share this article

Help spread the word about Bootspring