본문으로 건너뛰기 Winston Complete Guide | Node.js Logging Library

Winston Complete Guide | Node.js Logging Library

Winston Complete Guide | Node.js Logging Library

이 글의 핵심

Winston is a versatile logging library for Node.js. It supports multiple transports, custom formats, and is used by millions of applications for production logging.

Introduction

Winston is a simple and universal logging library for Node.js. It’s designed to be flexible and extensible, supporting multiple transports (outputs) and formats.

Why Winston?

console.log (basic):

console.log('User logged in:', userId);
console.error('Database connection failed:', error);

Problems:

  • ❌ No log levels
  • ❌ Can’t filter logs
  • ❌ No file output
  • ❌ No formatting control
  • ❌ Not production-ready

Winston:

logger.info('User logged in', { userId });
logger.error('Database connection failed', { error });

Benefits:

  • ✅ Multiple log levels
  • ✅ Multiple outputs (console, file, database)
  • ✅ Structured logging
  • ✅ Custom formats
  • ✅ Production-ready

1. Installation

npm install winston

2. Basic Setup

const winston = require('winston');

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

logger.info('Application started');
logger.warn('Low memory warning');
logger.error('Database connection failed');

3. Log Levels

Winston uses npm log levels by default:

{
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  verbose: 4,
  debug: 5,
  silly: 6
}
const logger = winston.createLogger({
  level: 'info', // Only log 'info' and above (warn, error)
});

logger.error('Critical error');   // Logged
logger.warn('Warning message');   // Logged
logger.info('Info message');      // Logged
logger.debug('Debug details');    // NOT logged (below 'info')

4. Transports

Console Transport

new winston.transports.Console({
  level: 'debug',
  format: winston.format.simple(),
});

File Transport

new winston.transports.File({
  filename: 'error.log',
  level: 'error',
  maxsize: 5242880, // 5MB
  maxFiles: 5,
});

new winston.transports.File({
  filename: 'combined.log',
});

Multiple Transports

const logger = winston.createLogger({
  transports: [
    // Console for development
    new winston.transports.Console({
      level: 'debug',
      format: winston.format.simple(),
    }),
    
    // Error file
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
    }),
    
    // Combined file
    new winston.transports.File({
      filename: 'logs/combined.log',
    }),
  ],
});

5. Formats

Built-in Formats

const { format } = winston;

// JSON format
format.json();

// Simple format
format.simple();

// Pretty print
format.prettyPrint();

// Timestamp
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' });

// Colorize (for console)
format.colorize();

// Align
format.align();

Combining Formats

const logger = winston.createLogger({
  format: format.combine(
    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    format.errors({ stack: true }),
    format.splat(),
    format.json()
  ),
});

Custom Format

const customFormat = format.printf(({ level, message, timestamp, ...meta }) => {
  return `${timestamp} [${level}]: ${message} ${
    Object.keys(meta).length ? JSON.stringify(meta, null, 2) : ''
  }`;
});

const logger = winston.createLogger({
  format: format.combine(
    format.timestamp(),
    customFormat
  ),
  transports: [new winston.transports.Console()],
});

6. Structured Logging

// Bad: string interpolation
logger.info(`User ${userId} logged in from ${ip}`);

// Good: structured data
logger.info('User logged in', {
  userId,
  ip,
  userAgent: req.headers['user-agent'],
  timestamp: new Date(),
});

// Even better: consistent format
logger.info('User login', {
  event: 'user.login',
  userId,
  metadata: {
    ip,
    userAgent: req.headers['user-agent'],
  },
});

7. Error Logging

const logger = winston.createLogger({
  format: format.combine(
    format.timestamp(),
    format.errors({ stack: true }), // Include stack traces
    format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
  ],
});

// Log error with stack trace
try {
  throw new Error('Something went wrong');
} catch (error) {
  logger.error('Operation failed', { error });
}

// Output includes full stack trace

8. Child Loggers

const logger = winston.createLogger({
  format: format.json(),
  transports: [new winston.transports.Console()],
});

// Create child logger with default metadata
const userLogger = logger.child({ service: 'user-service' });
const authLogger = logger.child({ service: 'auth-service' });

userLogger.info('User created', { userId: 123 });
// Output: { level: 'info', message: 'User created', service: 'user-service', userId: 123 }

authLogger.warn('Failed login attempt', { ip: '192.168.1.1' });
// Output: { level: 'warn', message: 'Failed login attempt', service: 'auth-service', ip: '192.168.1.1' }

9. Production Setup

const winston = require('winston');
const { format } = winston;

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: format.combine(
    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    format.errors({ stack: true }),
    format.splat(),
    format.json()
  ),
  defaultMeta: {
    service: 'my-app',
    environment: process.env.NODE_ENV,
  },
  transports: [
    // Error logs
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
      maxsize: 5242880, // 5MB
      maxFiles: 5,
    }),
    
    // All logs
    new winston.transports.File({
      filename: 'logs/combined.log',
      maxsize: 5242880,
      maxFiles: 5,
    }),
  ],
  exceptionHandlers: [
    new winston.transports.File({ filename: 'logs/exceptions.log' }),
  ],
  rejectionHandlers: [
    new winston.transports.File({ filename: 'logs/rejections.log' }),
  ],
});

// Development: also log to console
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: format.combine(
      format.colorize(),
      format.simple()
    ),
  }));
}

module.exports = logger;

10. Express Integration

const express = require('express');
const winston = require('winston');
const expressWinston = require('express-winston');

const app = express();

// Log all requests
app.use(expressWinston.logger({
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'logs/requests.log' }),
  ],
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  meta: true, // Log request/response metadata
  msg: 'HTTP {{req.method}} {{req.url}}',
  expressFormat: true,
  colorize: false,
}));

// Your routes
app.get('/', (req, res) => {
  res.send('Hello World');
});

// Log errors
app.use(expressWinston.errorLogger({
  transports: [
    new winston.transports.File({ filename: 'logs/error.log' }),
  ],
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
}));

app.listen(3000);

11. Log Rotation

npm install winston-daily-rotate-file
const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');

const logger = winston.createLogger({
  transports: [
    new DailyRotateFile({
      filename: 'logs/application-%DATE%.log',
      datePattern: 'YYYY-MM-DD',
      zippedArchive: true,
      maxSize: '20m',
      maxFiles: '14d', // Keep logs for 14 days
    }),
  ],
});

12. Real-World Example

// logger.js
const winston = require('winston');
const { format } = winston;

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

const level = () => {
  const env = process.env.NODE_ENV || 'development';
  return env === 'development' ? 'debug' : 'warn';
};

const colors = {
  error: 'red',
  warn: 'yellow',
  info: 'green',
  http: 'magenta',
  debug: 'white',
};

winston.addColors(colors);

const consoleFormat = format.combine(
  format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
  format.colorize({ all: true }),
  format.printf(
    (info) => `${info.timestamp} ${info.level}: ${info.message}`,
  ),
);

const fileFormat = format.combine(
  format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
  format.errors({ stack: true }),
  format.splat(),
  format.json(),
);

const transports = [
  new winston.transports.Console({ format: consoleFormat }),
  new winston.transports.File({
    filename: 'logs/error.log',
    level: 'error',
    format: fileFormat,
  }),
  new winston.transports.File({
    filename: 'logs/all.log',
    format: fileFormat,
  }),
];

const logger = winston.createLogger({
  level: level(),
  levels,
  transports,
});

module.exports = logger;
// Usage
const logger = require('./logger');

logger.debug('Debug message');
logger.http('HTTP request');
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message', { error: new Error('Something broke') });

13. Best Practices

1. Use Structured Logging

// Good
logger.info('User action', {
  action: 'purchase',
  userId: 123,
  amount: 99.99,
  currency: 'USD',
});

// Bad
logger.info(`User 123 purchased item for $99.99`);

2. Include Context

logger.info('Database query', {
  query: 'SELECT * FROM users',
  duration: 45,
  rowCount: 100,
  timestamp: new Date(),
});

3. Don’t Log Sensitive Data

// Bad
logger.info('User login', { password: 'secret123' });

// Good
logger.info('User login', { 
  userId: 123,
  timestamp: new Date(),
});

Summary

Winston provides production-ready logging:

  • Multiple transports - console, files, external services
  • Log levels for filtering
  • Structured logging with metadata
  • Custom formats for any output
  • Log rotation for file management

Key Takeaways:

  1. Use log levels appropriately
  2. Structure your logs with metadata
  3. Configure different transports
  4. Rotate logs to manage disk space
  5. Never log sensitive information

Next Steps:

Resources:


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Complete Winston guide for Node.js logging. Learn transports, formats, log levels, error handling, and building producti…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Winston, Logging, Node.js, Backend, Monitoring, DevOps.