I want to be precise: console.log is not bad. It is the right tool for a specific, narrow job: quickly dumping a value in a local dev environment when you need to see it right now. That is a legitimate use case and I use it too.
The problem is that most engineers use it as a default, not as a deliberate choice. And when it leaks into production — which it almost always does — the costs are real and rarely measured.
The three hidden costs
1. Serialization performance on the hot path
Logging an object with console.log triggers JSON serialization of the entire object graph at log time. In a request handler that runs 5,000 times per second, this is not free. I have seen a single console.log({ user, session, request }) on a hot path add 4ms of average request latency — which sounds small until you look at the p99 tail, where it shows up as 40ms.
The specific cost depends on the object depth, the runtime, and whether you are logging synchronously to a terminal or buffering. The point is: you almost never measure it, so you almost never know it is there.
2. Zero queryability
A console.log line is prose. You read it with your eyes. You cannot filter it, aggregate it, or join it to another log line from the same request. When your system processes a million requests per day, an unstructured log is not a debugging tool — it is a fire hose you cannot drink from.
Structured logging — emitting key-value pairs or JSON — lets you answer 'how many users hit this path in the last hour?' with a single query. console.log gives you: scroll up and count.
3. The false confidence problem
This is the subtlest cost. When you debug with console.log, you add output at the place you think the bug is. The tool gives you exactly the information you thought to ask for. But bugs live in the place you did not think to look.
A debugger lets you pause execution, inspect every variable in scope, step through the call stack, and change values at runtime. It gives you information about the state you did not predict, not just the state you asked about. That is a categorically different tool.
Node.js has a built-in inspector: run `node --inspect-brk your-script.js` and open chrome://inspect in Chrome. You get a full debugger with breakpoints, variable inspection, and call stack — zero additional dependencies.
What to use instead
- arrow_rightLocal dev, quick value check: console.log is fine — just don't commit it
- arrow_rightLocal dev, complex state / async flow: Node inspector or browser DevTools debugger
- arrow_rightStaging / production event recording: structured logger (pino, winston) with trace ID
- arrow_rightProduction performance profiling: your runtime's profiler (Node --prof, browser Performance tab)
- arrow_rightProduction error capture: an error tracking tool (Sentry, Honeybadger) with source maps
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL ?? 'info',
base: { service: 'api' },
});
// In a request handler:
logger.info({ userId, requestId, durationMs }, 'user.login.success');A debugger gives you information about the state you did not predict. console.log only gives you the state you already thought to ask about.
Frequently asked questions
Is console.log bad practice?
No — it's a tool. It's bad practice to leave it in production code on the hot path, or to use it as your primary debugging method when a structured logger or debugger would give you ten times more information. Use the right tool for the context.
What is the performance cost of console.log?
In Node.js, console.log is synchronous to stderr when piped to a terminal and asynchronous in some runtime environments, but the serialization cost of complex objects can be significant. In browser environments on the hot path, logging large objects during render can measurably slow frames.
What should I use instead of console.log for production debugging?
A structured logger (pino, winston, bunyan for Node; the platform logging SDK for cloud environments) with a severity level, trace ID, and structured fields. For local exploration, the Node.js debugger or browser DevTools debugger gives you live state inspection without any log spam.