Node.js Logging with Winston: A Complete Guide
Tutorials September 30, 2025 ยท 3 min read

Node.js Logging with Winston: A Complete Guide

Master Winston for Node.js logging. Learn setup, configuration, transports, and how to send logs to a centralized service.

Winston is the most popular logging library for Node.js applications. It's flexible, extensible, and supports multiple transports out of the box. This guide covers everything from basic setup to advanced configurations.

Installation

npm install winston

Basic Setup

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});

// Add console transport in development
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}

module.exports = logger;

Log Levels

Winston uses npm log levels by default:

const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  verbose: 4,
  debug: 5,
  silly: 6
};

Custom Formatting

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.printf(({ level, message, timestamp, ...meta }) => {
          return `${timestamp} [${level}]: ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ''}`;
        })
      ),
    }),
  ],
});

Sending Logs to a Central Service

Create a custom HTTP transport to send logs to a service like 401 Clicks:

const Transport = require('winston-transport');
const axios = require('axios');

class HttpTransport extends Transport {
  constructor(opts) {
    super(opts);
    this.url = opts.url;
    this.token = opts.token;
    this.buffer = [];
    this.bufferSize = opts.bufferSize || 10;
    this.flushInterval = opts.flushInterval || 5000;

    setInterval(() => this.flush(), this.flushInterval);
  }

  log(info, callback) {
    this.buffer.push({
      level: info.level,
      message: info.message,
      timestamp: info.timestamp || new Date().toISOString(),
      context: { ...info },
    });

    if (this.buffer.length >= this.bufferSize) {
      this.flush();
    }

    callback();
  }

  async flush() {
    if (this.buffer.length === 0) return;

    const logs = [...this.buffer];
    this.buffer = [];

    try {
      await axios.post(this.url, { logs }, {
        headers: { Authorization: `Bearer ${this.token}` },
      });
    } catch (error) {
      console.error('Failed to send logs:', error.message);
      this.buffer = logs.concat(this.buffer);
    }
  }
}

module.exports = HttpTransport;

Using the Custom Transport

const HttpTransport = require('./http-transport');

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
    new HttpTransport({
      url: 'https://logs.401clicks.com/api/v1/logs/batch',
      token: process.env.CLICKS_API_TOKEN,
      bufferSize: 10,
      flushInterval: 5000,
    }),
  ],
});

Express.js Integration

Log HTTP requests with winston and morgan:

const morgan = require('morgan');
const logger = require('./logger');

const stream = {
  write: (message) => logger.http(message.trim()),
};

app.use(morgan('combined', { stream }));

Error Handling

Capture unhandled exceptions and rejections:

const logger = winston.createLogger({
  exceptionHandlers: [
    new winston.transports.File({ filename: 'exceptions.log' }),
  ],
  rejectionHandlers: [
    new winston.transports.File({ filename: 'rejections.log' }),
  ],
});

Best Practices

  • Always include timestamps in your logs
  • Use structured logging (objects, not strings)
  • Set appropriate log levels per environment
  • Buffer logs before sending to remote services
  • Handle transport failures gracefully

Conclusion

Winston provides the flexibility to build a robust logging system for any Node.js application. Combined with a centralized logging service, you get powerful debugging capabilities across your entire infrastructure.

A

Admin

Published on September 30, 2025