How to Implement Logging in Node.js with Winston
A step-by-step guide on how to implement structured, leveled logging in Node.js using Winston for debugging and production monitoring.
Understand Why console.log Is Not Enough for Production
console.log outputs unstructured plain text with no severity level, no timestamps, and no way to control output based on environment. In production, you need structured logs with severity levels so you can filter by importance, timestamps so you can correlate events across services, and the ability to ship logs to external monitoring systems automatically.
Install Winston
Install the winston package from npm. Winston is the most widely used logging library for Node.js. It supports multiple transports meaning multiple output destinations simultaneously, custom log levels, structured JSON output, and log formatting. Create a dedicated logger module in your project and export the configured logger instance for use throughout your application.
Create and Configure the Logger
Call winston.createLogger with a configuration object. Set the level property to define the minimum severity level to log, such as 'info' in production which suppresses debug and verbose messages. Set the format property to define how log entries are structured. Add transports to define where logs are sent, such as the console or log files.
Configure Log Formats
Use winston.format.combine to chain multiple formatters together. Use winston.format.timestamp to add a timestamp to every log entry. Use winston.format.json to output log entries as JSON objects, which are easy to parse and query in log management systems. For development, use winston.format.prettyPrint or winston.format.colorize to produce readable colored output instead of raw JSON.
Add Multiple Transports
Configure at least two transports. Add a Console transport for all log output during development and for container-based deployments where logs are collected from stdout. Add a File transport to write error-level logs to an error.log file and all logs to a combined.log file. In production, add transports for external services like Datadog, CloudWatch, or Elasticsearch.
Use Log Levels Correctly
Winston's default log levels from highest to lowest priority are error, warn, info, http, verbose, debug, and silly. Use error for failures that need immediate attention. Use warn for unexpected situations that are not failures. Use info for significant application events like startup and successful operations. Use debug for detailed diagnostic information useful only during development. Set the level to 'warn' or 'error' in production to reduce log volume.
Add Request Logging with Morgan
Install the morgan package and configure it to use Winston as its output stream. Morgan logs details of every HTTP request including the method, URL, status code, and response time. Configure Morgan to use the 'combined' format in production for complete request information and 'dev' format in development for concise colored output. Pass Winston's stream as the stream option in Morgan's configuration.
Add Contextual Information to Logs
Add relevant context to every log message to make troubleshooting easier. Include the request ID in all logs related to a specific request so you can trace the entire lifecycle of one request across multiple log entries. Use a middleware to generate a unique request ID using a UUID, attach it to the request object, and pass it as metadata to the Winston logger using child loggers created per request.
Ready to master this completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

