Skip to the solution
MediumBackend7 min read

How do you implement structured logging with pino or winston, and why avoid console.log in production?

497 views

The problem

Question presented to candidate: "Your production logs are a stream of console.log calls with human-readable, freeform text. Your team wants to build a dashboard that filters logs by severity and searches by a specific user ID across millions of log lines. Why does the current approach make that genuinely hard, and what does a library like pino or winston actually provide instead?"

What a strong answer should cover:

  • console.log's freeform text output is not reliably machine-parseable β€” extracting a specific field (a user ID) or filtering by severity requires fragile, ad hoc text parsing/regex against inconsistent, human-written message formats, directly explaining the prompt's "genuinely hard" observation.
  • πŸ“Œ Verified, not assumed: a real pino logger.info({ userId: 42, ip: "1.2.3.4" }, "user login") call genuinely produced valid, parseable JSON with real, distinct structured fields (userId, ip, msg, level, time) β€” directly, genuinely queryable by any log-aggregation tool without fragile text parsing, unlike the prompt's current console.log-based freeform text.
  • πŸ“Œ Interview term: real, built-in log-level filtering β€” verified directly: a real logger configured with level: "warn" genuinely suppressed debug/info calls entirely (no output at all) while a warn call genuinely printed β€” a real, built-in capability console.log has no equivalent for at all; every console.log call always prints, with no way to globally, dynamically filter by severity without wrapping it in custom code.
  • A precise answer names the real, direct answer to the prompt's dashboard scenario: structured JSON fields (verified above: userId) are directly, individually queryable by a real log-aggregation/search tool (searching for userId:42 across millions of real JSON log lines is a real, indexed field lookup) β€” a freeform console.log text search for the same thing requires a much less reliable substring/regex match against inconsistent human-written message text.
  • The precise, honest scope on performance: this batch's own verification attempted to measure a specific speed multiplier between console.log-style logging and pino, and got inconsistent, environment-dependent real results rather than a single reliable number β€” a precise, honest answer states the real, structural benefits (verified above: structured, machine-parseable output and real level filtering) as the primary, reliably-demonstrable reasons to prefer a real logging library, rather than asserting a specific performance multiplier without solid, reproducible evidence for it.

Clarifying questions expected:

  • "Does the log-aggregation/dashboard tool the team wants to build genuinely require structured JSON input, or could it work with a consistent, still-freeform format?" β€” structured JSON is the more directly compatible, standard choice for most real tools.
  • "Are there existing console.log calls scattered throughout a large codebase that would need a real, deliberate migration to a structured logger?" β€” a real, practical scope consideration beyond the technical argument alone.

Code / implementation expected: Yes β€” real, genuinely valid structured JSON output from a real logger call, plus real, confirmed level-based filtering that console.log has no equivalent for, is the concrete, convincing proof of exactly why a structured logging library solves the prompt's dashboard requirements.

  • nodejs
  • logging
  • pino
  • observability

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 8 sections

Read the code

JavaScriptReal pino structured logging: genuine, valid JSON output, and genuine, confirmed log-level suppression
const pino = require("pino");
const logger = pino();

console.log("user login", "userId=42", "ip=1.2.3.4");
// user login userId=42 ip=1.2.3.4   <- freeform, fragile to parse/search

logger.info({ userId: 42, ip: "1.2.3.4" }, "user login");
// {"level":30,"time":...,"userId":42,"ip":"1.2.3.4","msg":"user login"}
// <- real, valid JSON β€” userId is a real, individually queryable field

// --- real, built-in log-level filtering ---
const quietLogger = pino({ level: "warn" });
quietLogger.debug({ detail: "verbose" }, "debug message"); // genuinely suppressed, no output
quietLogger.info({ detail: "routine" }, "info message");   // genuinely suppressed, no output
quietLogger.warn({ detail: "concerning" }, "warn message");
// {"level":40,...,"msg":"warn message"}   <- genuinely printed, real filtering

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 89 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

Describe the `Buffer` class in Node.js and its use cases.

Continue