Structured Logging

Swap console.log for a fast JSON logger like pino, attach a request id to every line, and never log secrets.

console.log is a development tool. In production you want structured, JSON logs that a log aggregator can index, filter and alert on. pino is the community standard: extremely fast, JSON by default, and it barely touches your event loop.

Correlation is the whole point

The magic is a child logger per request, bound to a request id. Every line emitted while handling that request carries the id, so when something breaks you filter your aggregator by one id and see the entire story — across services, if you propagate the id in a header.

import pino from 'pino';
const logger = pino();
logger.info({ userId: 42, route: '/orders' }, 'order placed');
// -> {"level":30,"userId":42,"route":"/orders","msg":"order placed"}

Pretty in dev, JSON in prod

Pipe through pino-pretty locally for human-readable colors; emit raw JSON in production so machines can parse it.

Example

Example · javascript
import express from 'express';
import pino from 'pino';
import pinoHttp from 'pino-http';
import { randomUUID } from 'node:crypto';

const logger = pino({
  level: process.env.LOG_LEVEL ?? 'info',
  // Strip secrets structurally — before anything is serialized.
  redact: ['req.headers.authorization', 'req.headers.cookie', '*.password', '*.token'],
  transport: process.env.NODE_ENV !== 'production'
    ? { target: 'pino-pretty' }
    : undefined,
});

const app = express();

app.use(
  pinoHttp({
    logger,
    // Reuse an inbound id or mint one; it flows to every child log line.
    genReqId: (req, res) => {
      const id = req.headers['x-request-id'] ?? randomUUID();
      res.setHeader('x-request-id', id);
      return id;
    },
  }),
);

app.get('/orders/:id', (req, res) => {
  // req.log is a child logger already bound to this request's id.
  req.log.info({ orderId: req.params.id }, 'fetching order');
  res.json({ id: req.params.id });
});

When to use it

  • A production Express app uses Pino to write structured JSON logs so a log aggregator like Datadog can index and query log fields.
  • A developer configures separate log levels for development (debug) and production (warn) using Winston transports.
  • A request-scoped logger attaches a correlation ID to every log line so all entries for a single HTTP request can be traced together.

More examples

Structured logging with Pino

Integrates Pino with pino-http middleware so every request generates a structured JSON log line and req.log is available in handlers.

Example · js
const express = require('express');
const pino = require('pino');
const pinoHttp = require('pino-http');

const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
const app = express();

app.use(pinoHttp({ logger }));

app.get('/health', (req, res) => {
  req.log.info('health check hit');
  res.json({ status: 'ok' });
});

app.listen(3000);

Winston with transports

Creates a Winston logger that writes to the console at debug level in development and to an error log file in production.

Example · js
const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.NODE_ENV === 'production' ? 'warn' : 'debug',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
  ],
});

module.exports = logger;

Request correlation ID logger

Creates a child logger per request with a correlation ID so every log line for the same request shares the same requestId field.

Example · js
const { randomUUID } = require('crypto');
const pino = require('pino');
const logger = pino();

app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] || randomUUID();
  req.log = logger.child({ requestId: req.id, method: req.method, path: req.path });
  res.setHeader('X-Request-Id', req.id);
  next();
});

app.get('/data', (req, res) => {
  req.log.info('fetching data');
  res.json({ ok: true });
});

Discussion

  • Be the first to comment on this lesson.