Effective debugging saves development time. Here's how to debug Node.js applications.
Console Methods#
1// Basic logging
2console.log('Value:', value);
3console.error('Error:', error);
4console.warn('Warning:', warning);
5
6// Table format
7const users = [
8 { id: 1, name: 'John', age: 30 },
9 { id: 2, name: 'Jane', age: 25 },
10];
11console.table(users);
12// ┌─────────┬────┬────────┬─────┐
13// │ (index) │ id │ name │ age │
14// ├─────────┼────┼────────┼─────┤
15// │ 0 │ 1 │ 'John' │ 30 │
16// │ 1 │ 2 │ 'Jane' │ 25 │
17// └─────────┴────┴────────┴─────┘
18
19// Grouping
20console.group('User Processing');
21console.log('Step 1: Validate');
22console.log('Step 2: Transform');
23console.log('Step 3: Save');
24console.groupEnd();
25
26// Timing
27console.time('operation');
28await someOperation();
29console.timeEnd('operation');
30// operation: 123.456ms
31
32// Count calls
33function processItem(item) {
34 console.count('processItem called');
35 // ...
36}
37
38// Trace stack
39console.trace('Trace point');
40
41// Assert
42console.assert(value > 0, 'Value must be positive');
43
44// Clear
45console.clear();
46
47// Dir for objects
48console.dir(complexObject, { depth: null, colors: true });Debugger Statement#
1// Pause execution when debugger attached
2function processData(data) {
3 debugger; // Execution pauses here
4 const result = transform(data);
5 return result;
6}
7
8// Run with inspect
9// node --inspect app.js
10// node --inspect-brk app.js // Break at start
11
12// Open in Chrome DevTools
13// chrome://inspectVS Code Debugging#
1// .vscode/launch.json
2{
3 "version": "0.2.0",
4 "configurations": [
5 {
6 "type": "node",
7 "request": "launch",
8 "name": "Launch Program",
9 "program": "${workspaceFolder}/src/index.ts",
10 "preLaunchTask": "tsc: build",
11 "outFiles": ["${workspaceFolder}/dist/**/*.js"],
12 "console": "integratedTerminal"
13 },
14 {
15 "type": "node",
16 "request": "launch",
17 "name": "Debug Tests",
18 "program": "${workspaceFolder}/node_modules/jest/bin/jest",
19 "args": ["--runInBand", "--watchAll=false"],
20 "console": "integratedTerminal"
21 },
22 {
23 "type": "node",
24 "request": "attach",
25 "name": "Attach to Process",
26 "port": 9229,
27 "restart": true
28 }
29 ]
30}1// Conditional breakpoints
2// Right-click breakpoint in VS Code
3// Expression: user.id === '123'
4
5// Logpoints (like console.log without modifying code)
6// Right-click line number -> Add Logpoint
7// Message: "User: {user.name}, Status: {status}"Error Stack Traces#
1// Capture stack trace
2function captureStack() {
3 const err = new Error();
4 return err.stack;
5}
6
7// Async stack traces
8// node --async-stack-traces app.js
9
10// Custom error with stack
11class AppError extends Error {
12 constructor(message, code) {
13 super(message);
14 this.name = 'AppError';
15 this.code = code;
16 Error.captureStackTrace(this, AppError);
17 }
18}
19
20// Clean stack traces
21function cleanStack(stack) {
22 return stack
23 .split('\n')
24 .filter(line => !line.includes('node_modules'))
25 .join('\n');
26}
27
28// Source maps for TypeScript
29// tsconfig.json: "sourceMap": true
30// node --enable-source-maps dist/app.jsMemory Debugging#
1// Memory usage
2const used = process.memoryUsage();
3console.log({
4 rss: `${Math.round(used.rss / 1024 / 1024)} MB`, // Total memory
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});
9
10// Heap snapshot
11const v8 = require('v8');
12const fs = require('fs');
13
14function takeHeapSnapshot() {
15 const filename = `heap-${Date.now()}.heapsnapshot`;
16 const snapshotStream = v8.writeHeapSnapshot(filename);
17 console.log(`Heap snapshot written to ${filename}`);
18}
19
20// Trigger with signal
21process.on('SIGUSR2', takeHeapSnapshot);
22
23// Heap dump on memory limit
24const MEMORY_LIMIT = 500 * 1024 * 1024; // 500MB
25
26setInterval(() => {
27 const { heapUsed } = process.memoryUsage();
28 if (heapUsed > MEMORY_LIMIT) {
29 takeHeapSnapshot();
30 console.error('Memory limit exceeded');
31 }
32}, 30000);
33
34// Detect memory leaks
35// npm install memwatch-next
36const memwatch = require('memwatch-next');
37
38memwatch.on('leak', (info) => {
39 console.error('Memory leak detected:', info);
40});Performance Profiling#
1// CPU profiling
2const inspector = require('inspector');
3const session = new inspector.Session();
4session.connect();
5
6function startProfiling() {
7 session.post('Profiler.enable');
8 session.post('Profiler.start');
9}
10
11function stopProfiling() {
12 session.post('Profiler.stop', (err, { profile }) => {
13 if (err) return console.error(err);
14 fs.writeFileSync(
15 `profile-${Date.now()}.cpuprofile`,
16 JSON.stringify(profile)
17 );
18 });
19}
20
21// Command line profiling
22// node --prof app.js
23// node --prof-process isolate-*.log > processed.txt
24
25// Clinic.js for analysis
26// npm install -g clinic
27// clinic doctor -- node app.js
28// clinic flame -- node app.js
29// clinic bubbleprof -- node app.js
30
31// perf_hooks
32const { performance, PerformanceObserver } = require('perf_hooks');
33
34const obs = new PerformanceObserver((items) => {
35 items.getEntries().forEach((entry) => {
36 console.log(`${entry.name}: ${entry.duration}ms`);
37 });
38});
39obs.observe({ entryTypes: ['measure'] });
40
41performance.mark('start');
42await doWork();
43performance.mark('end');
44performance.measure('work', 'start', 'end');Async Debugging#
1// Async hooks for tracking
2const asyncHooks = require('async_hooks');
3
4const asyncStorage = new Map();
5
6const hook = asyncHooks.createHook({
7 init(asyncId, type, triggerAsyncId) {
8 asyncStorage.set(asyncId, {
9 type,
10 triggerAsyncId,
11 stack: new Error().stack,
12 });
13 },
14 destroy(asyncId) {
15 asyncStorage.delete(asyncId);
16 },
17});
18
19hook.enable();
20
21// Track unhandled rejections
22process.on('unhandledRejection', (reason, promise) => {
23 console.error('Unhandled Rejection:', reason);
24 console.error('Promise:', promise);
25});
26
27// Long stack traces
28// node --async-stack-traces app.js
29async function main() {
30 await step1();
31 await step2();
32 await step3(); // Error here shows full async trace
33}HTTP Debugging#
1// Debug HTTP requests
2const http = require('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// Using debug module
15// DEBUG=express:* node app.js
16const debug = require('debug')('app:http');
17debug('Request received: %s %s', req.method, req.url);
18
19// Request timing
20app.use((req, res, next) => {
21 const start = Date.now();
22
23 res.on('finish', () => {
24 const duration = Date.now() - start;
25 console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`);
26 });
27
28 next();
29});Production Debugging#
1// Structured logging
2const pino = require('pino');
3const logger = pino({
4 level: process.env.LOG_LEVEL || 'info',
5 formatters: {
6 level: (label) => ({ level: label }),
7 },
8});
9
10logger.info({ userId: user.id, action: 'login' }, 'User logged in');
11logger.error({ err, requestId }, 'Request failed');
12
13// Error tracking
14const Sentry = require('@sentry/node');
15Sentry.init({ dsn: process.env.SENTRY_DSN });
16
17process.on('uncaughtException', (error) => {
18 Sentry.captureException(error);
19 process.exit(1);
20});
21
22// Remote debugging
23// node --inspect=0.0.0.0:9229 app.js
24// Use SSH tunnel for production
25// ssh -L 9229:localhost:9229 user@serverDebugging Tips#
1# Run with verbose output
2NODE_DEBUG=http,net node app.js
3
4# Increase stack trace limit
5node --stack-trace-limit=100 app.js
6
7# Trace warnings
8node --trace-warnings app.js
9
10# Trace deprecations
11node --trace-deprecation app.js
12
13# Print V8 options
14node --v8-options | grep -i debugBest Practices#
Development:
✓ Use VS Code debugger
✓ Set meaningful breakpoints
✓ Use conditional breakpoints
✓ Enable source maps
Logging:
✓ Use structured logging
✓ Include context (request IDs)
✓ Log at appropriate levels
✓ Don't log sensitive data
Production:
✓ Set up error tracking
✓ Use proper logging service
✓ Monitor memory and CPU
✓ Enable remote debugging carefully
Conclusion#
Effective debugging combines multiple techniques: console methods for quick checks, debuggers for stepping through code, profilers for performance issues, and proper logging for production. Master VS Code debugging, use Chrome DevTools for profiling, and set up robust error tracking for production applications.