Debugging & Production Logging

📖 Concept

Effective debugging and structured logging are critical for maintaining Node.js applications. In production, logs are your primary window into application behavior.

Debugging tools:

Tool Use Case
console.log() Quick dev debugging (remove before committing!)
debugger statement Breakpoints in Node.js inspector
node --inspect Chrome DevTools debugger
VS Code debugger IDE-integrated debugging
debug package Conditional debug logging (e.g., Express uses it)

Production logging with Winston: Winston is the most popular logging library for Node.js. It supports multiple log levels, transports (console, file, database), and structured JSON logging.

Log levels (severity):

error: 0  →  Application errors, crashes
warn:  1  →  Unexpected behavior, deprecations
info:  2  →  Important events (server start, user actions)
http:  3  →  HTTP request/response logs
debug: 4  →  Detailed debugging (disabled in production)

Structured logging best practices:

  1. Use JSON format — parseable by log aggregation tools (ELK, Datadog)
  2. Include context — request ID, user ID, timestamp
  3. Log at appropriate levels — don't log everything at info
  4. Never log sensitive data — passwords, tokens, credit cards
  5. Use correlation IDs — trace a request across microservices

🏠 Real-world analogy: Debugging is like being a detective. console.log is asking witnesses (prints). The Node.js debugger is reviewing security camera footage (step-by-step execution). Production logging is like a flight recorder (black box) — capturing everything needed to reconstruct events after they happen.

💻 Code Example

codeTap to expand ⛶
1// Debugging & Production Logging
2
3// === 1. Winston Logger Setup ===
4const winston = require("winston");
5
6const logger = winston.createLogger({
7 level: process.env.LOG_LEVEL || "info",
8 format: winston.format.combine(
9 winston.format.timestamp(),
10 winston.format.errors({ stack: true }),
11 winston.format.json()
12 ),
13 defaultMeta: { service: "my-api" },
14 transports: [
15 // Console (colorized for development)
16 new winston.transports.Console({
17 format:
18 process.env.NODE_ENV === "development"
19 ? winston.format.combine(winston.format.colorize(), winston.format.simple())
20 : winston.format.json(),
21 }),
22 // File transports (production)
23 new winston.transports.File({ filename: "logs/error.log", level: "error", maxsize: 5242880, maxFiles: 5 }),
24 new winston.transports.File({ filename: "logs/combined.log", maxsize: 5242880, maxFiles: 5 }),
25 ],
26});
27
28// 2. Request logging middleware
29function requestLogger(req, res, next) {
30 const requestId = req.headers["x-request-id"] || require("crypto").randomUUID();
31 req.requestId = requestId;
32 res.setHeader("X-Request-Id", requestId);
33
34 const start = Date.now();
35
36 res.on("finish", () => {
37 const duration = Date.now() - start;
38 const logData = {
39 requestId,
40 method: req.method,
41 url: req.originalUrl,
42 statusCode: res.statusCode,
43 duration: `${duration}ms`,
44 ip: req.ip,
45 userAgent: req.headers["user-agent"],
46 userId: req.user?.id,
47 };
48
49 if (res.statusCode >= 500) {
50 logger.error("Request failed", logData);
51 } else if (res.statusCode >= 400) {
52 logger.warn("Client error", logData);
53 } else {
54 logger.info("Request completed", logData);
55 }
56 });
57
58 next();
59}
60
61// 3. Usage in application code
62logger.info("Server starting", { port: 3000, env: process.env.NODE_ENV });
63logger.warn("Cache miss", { key: "user:42", source: "redis" });
64logger.error("Database query failed", { query: "SELECT...", error: "Connection refused" });
65
66// ❌ NEVER log sensitive data
67// logger.info("User login", { email, password }); // BAD!
68// ✅ Log safely
69// logger.info("User login", { email, userId: user.id }); // GOOD
70
71// 4. Node.js Debugger
72// Start with: node --inspect src/server.js
73// Open: chrome://inspect in Chrome
74// Or use VS Code's built-in debugger
75
76// .vscode/launch.json
77const vscodeLaunchConfig = {
78 version: "0.2.0",
79 configurations: [
80 {
81 type: "node",
82 request: "launch",
83 name: "Debug Server",
84 program: "${workspaceFolder}/src/server.js",
85 env: { NODE_ENV: "development" },
86 restart: true,
87 },
88 {
89 type: "node",
90 request: "launch",
91 name: "Debug Tests",
92 program: "${workspaceFolder}/node_modules/.bin/jest",
93 args: ["--runInBand", "--no-cache"],
94 },
95 ],
96};
97
98// 5. Conditional debug logging (debug package)
99// const debug = require("debug");
100// const dbDebug = debug("app:db");
101// const httpDebug = debug("app:http");
102// dbDebug("Query executed:", query); // Only shows if DEBUG=app:db
103// Start with: DEBUG=app:* node server.js
104
105// 6. Performance profiling
106function measurePerformance() {
107 console.time("operation");
108 // ... do work
109 console.timeEnd("operation"); // "operation: 123ms"
110
111 // More precise
112 const { performance, PerformanceObserver } = require("perf_hooks");
113 const start = performance.now();
114 // ... do work
115 const duration = performance.now() - start;
116 logger.debug("Operation completed", { durationMs: duration.toFixed(2) });
117}
118
119module.exports = { logger, requestLogger };

🏋️ Practice Exercise

Exercises:

  1. Set up Winston with console, file, and error-specific transports
  2. Create request logging middleware that includes request ID, method, URL, status, and duration
  3. Add correlation IDs — generate a unique ID per request and include it in all logs
  4. Configure VS Code debugger for both running the server and running tests
  5. Profile a slow endpoint using console.time, perf_hooks, and Node.js --prof flag
  6. Set up structured logging that can be parsed by ELK Stack or Datadog

⚠️ Common Mistakes

  • Using console.log in production — it's synchronous, has no log levels, and can't be routed to files or services; use Winston or Pino

  • Logging sensitive data — passwords, tokens, credit card numbers must NEVER appear in logs; audit your logs regularly

  • Not including correlation IDs — without request IDs, you can't trace a request through multiple log entries or services

  • Logging too much in production — excessive logging impacts performance and storage costs; use info level in production, debug in development

  • Not setting up log rotation — without rotation, log files grow indefinitely and fill the disk; use Winston's maxsize and maxFiles options

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Debugging & Production Logging. Login to unlock this feature.