Debugging Node.js Like a Pro

작성자

카테고리:

← 피드로
DEV Community · Stack Horizon · 2026-09-17 개발(SW)

Most Node.js debugging happens with console.log. That works until it doesn’t: async stacks that point to the wrong place, a variable that changes between the log and the crash, or a server that only misbehaves on one request out of a thousand. Here’s the toolkit I reach for instead.

Start with --inspect, not print statements

The built-in inspector gives you breakpoints, step-through, and a real call stack. Start your process with:

node --inspect app.js

Enter fullscreen mode Exit fullscreen mode

Then open chrome://inspect in Chrome and click “inspect”. For a process you can’t restart easily (a worker, a Docker container), use --inspect-brk to pause on the first line, or send SIGUSR1 to a running process to enable the inspector on the fly.

kill -USR1 <pid>

Enter fullscreen mode Exit fullscreen mode

If the port needs to be reachable from outside a container, bind it explicitly: node --inspect=0.0.0.0:9229 app.js. Don’t do this on a public host.

Break on the condition, not the line

A plain breakpoint inside a hot loop stops on every iteration. Right-click the line in DevTools and add a conditional breakpoint. Instead of:

for (const order of orders) {
  processOrder(order); // stops 10,000 times
}

Enter fullscreen mode Exit fullscreen mode

Set the condition to order.id === 'abc123' and it stops exactly once. This turns a flaky bug into a reproducible one.

Use debugger statements deliberately

A debugger; line behaves like a breakpoint when the inspector is attached, and is a no-op otherwise. That makes it safe to commit temporarily:

function applyDiscount(cart) {
  debugger; // only pauses when --inspect is active
  return cart.total * 0.9;
}

Enter fullscreen mode Exit fullscreen mode

I’ll leave these in during a debugging session and strip them before the PR.

Log objects, not strings

console.log('user:', user) prints [object Object] in some terminals and truncates deeply nested data. Two fixes:

console.log(JSON.stringify(user, null, 2));
console.dir(user, { depth: null });

Enter fullscreen mode Exit fullscreen mode

console.dir with depth: null is the one I use most. It respects circular references, which JSON.stringify throws on.

Trace async properly with --async-stack-traces

Async stack traces are on by default in modern Node, but if you’re on an older release or have them disabled, enable them:

node --async-stack-traces app.js

Enter fullscreen mode Exit fullscreen mode

Without this, an error thrown inside a setTimeout or a promise chain shows a stack that starts at the callback, not at the code that scheduled it. With it, you see the full causal chain.

Attach a profiler when it’s slow, not broken

If the bug is “it’s slow,” breakpoints won’t help. Use the built-in profiler:

node --cpu-prof --cpu-prof-dir=./profiles app.js

Enter fullscreen mode Exit fullscreen mode

This writes a .cpuprofile file you can load in Chrome DevTools under the Performance tab. Look for wide bars: those are the functions eating your time. For memory, --heap-prof does the same for allocations, and process.memoryUsage() gives you a cheap snapshot:

setInterval(() => {
  const { heapUsed } = process.memoryUsage();
  console.log(`heap: ${(heapUsed / 1024 / 1024).toFixed(1)} MB`);
}, 5000);

Enter fullscreen mode Exit fullscreen mode

A heap that climbs steadily and never drops is a leak. A heap that sawtooths is just GC doing its job.

Read the error, including code

Node errors carry structured fields that err.message hides:

server.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${err.port} is taken`);
  }
  console.error(err);
});

Enter fullscreen mode Exit fullscreen mode

EADDRINUSE, ECONNREFUSED, and ENOENT tell you the category of failure instantly. Log the whole error object, not err.message.

A quick checklist

  • Reproduce it reliably first. A conditional breakpoint beats guessing.
  • --inspect-brk for startup crashes, SIGUSR1 for running processes.
  • console.dir(obj, { depth: null }) over string concatenation.
  • --cpu-prof and --heap-prof for performance, not breakpoints.
  • Check err.code before you check err.message.

None of this is exotic. It’s all built into Node. The trick is remembering it exists before you add the twentieth console.log.

원문에서 계속 ↗