Stop 19 · Monitoring & LoggingTopics · 2/9

Winston

Part of the Backend Developer Roadmap

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.

The purple blob sorts letters into red, yellow and blue mail bags at a post office table.

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

logger.warn
Level check
Format
Console
error.log

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

ts
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

~20 min

Quick check

The logger level is 'info'. A File transport has level 'error'. You call logger.warn(...). Where does it appear?

Go deeper

  1. 1Set up Winston with both console and file transports.
  2. 2Configure structured JSON logging for easier parsing by log aggregation tools.
  3. 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.

Download
InsideEdge