Good logging is what makes debugging production issues possible after the fact, when you can't attach a live debugger. This lesson covers request logging with morgan and structured application logging with winston.
Two Kinds of Logs You Need
Request logs (via morgan) capture every incoming HTTP request, method, path, status, response time, giving you a timeline of traffic. Application logs (via winston or a similar logger) capture meaningful events and errors from your own business logic, with structured detail console.log() alone doesn't provide.
Both matter: request logs tell you *what* happened at the HTTP level, application logs tell you *why*, from inside your code.
winston.format.json() produces structured, machine-parseable log lines, far more useful than plain strings once you're searching logs at scale.
Logging Levels
logger.error(message, meta); // failures needing attention
logger.warn(message, meta); // unexpected but non-fatal situations
logger.info(message, meta); // normal, notable application events
logger.debug(message, meta); // verbose detail, usually off in production
Use error sparingly, for things that genuinely need investigation, not routine validation failures.
info is a good default level for meaningful business events (user signed up, order placed).
Configure log level per environment, verbose in development, quieter in production.
Never log secrets, passwords, tokens, or full credit card numbers.
Logging Cheatsheet
The tools and patterns covered in this lesson.
Tool
Purpose
morgan
Automatic HTTP request logging
winston
Structured, leveled application logging
pino
Alternative high-performance structured logger
Log aggregation service
Centralizing logs across multiple instances (e.g. Datadog, Loggly)
Structured Logging With Context
Rather than logging plain strings, attach structured metadata, request IDs, user IDs, relevant identifiers, so logs can be filtered and correlated later, especially useful when diagnosing an issue reported by a specific user.
Logging Errors in Centralized Error-Handling Middleware
The centralized error handler (from the Error Middleware lesson) is the natural place to log unexpected errors with full context, before sending a clean, generic response to the client.
Structured, leveled logging is far more useful at scale than plain console.log() strings.
Never log secrets or sensitive personal data.
Attaching a request ID to every log line makes tracing a single request's full journey much easier.
Pro Tip
Generate a unique request ID at the very top of your middleware stack and include it in every log line for that request, this single habit makes tracing a specific user's bug report through your logs dramatically easier.
You now know how to implement structured logging. Next, learn how to add real-time features with WebSockets.