In short
Winston is Node's most flexible logger. One logger can send the same entry to the console, a file and a log service, each with its own level and format.
A flexible, widely used logging library for Node.js supporting multiple output destinations (console, file, remote services) and structured JSON logging.

Think of it like this
The post office sorting table
Every letter lands on one sorting table. The clerk stamps it, checks the type and drops it into the right bags: speed post in red, ordinary mail in blue, a copy for the register. Winston's logger is the table, and transports are the bags.
Watch it happen
One log call, many destinations
1/5Your code calls logger.warn with a message and some fields.
The big ideas
What you need to know
Transports
Each transport is a destination: Console, File, HTTP, or community ones for log services. Add as many as you need.
Formats compose
combine(timestamp(), errors({ stack: true }), json()) builds the output format step by step.
Levels per transport
The console can show debug while a file keeps only errors.
Child loggers
logger.child({ reqId }) returns a logger that adds those fields to every line it writes.
See it in code
Walk through the code
1import winston from "winston";2const { combine, timestamp, errors, json } = winston.format;3 4export const logger = winston.createLogger({5 level: process.env.LOG_LEVEL ?? "info",6 format: combine(timestamp(), errors({ stack: true }), json()),7 defaultMeta: { service: "orders-api" },8 transports: [9 new winston.transports.Console(),10 new winston.transports.File({ filename: "error.log", level: "error" }),11 ],12});13 14const log = logger.child({ reqId: "c1a9" });15log.warn("stock low", { sku: "TSHIRT-M", left: 2 });1/4The overall level comes from config, so you can turn on debug without a deploy.
In containers you'd usually keep only the Console transport and let the platform collect stdout. The File transport is handy on a plain VM.
Save yourself the pain
Common mistakes
Oops
Logging only err.message: logger.error(err.message).
Do this instead
You lose the stack trace. Pass the error object and enable errors({ stack: true }) in the format.
Oops
Writing log files inside a container and never rotating them.
Do this instead
The disk fills and the app dies. In containers log to stdout, or use a rotating file transport on VMs.
Your mission
Set up Winston properly
Quick check
The logger level is 'info'. A File transport has level 'error'. You call logger.warn(...). Where does it appear?
Go deeper
- 1Set up Winston with both console and file transports.
- 2Configure structured JSON logging for easier parsing by log aggregation tools.
- 3Add request-specific context, e.g. a request ID, to every log line.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.