Winston

TypeScript/Node.js向けの最も人気なロギングライブラリ(1,390万週間ダウンロード)。豊富でカスタマイズ可能なAPIとフォーマット・トランスポートオプションを提供。型定義サポートにより、TypeScriptプロジェクトでの開発体験が向上。

GitHub概要

winstonjs/winston

A logger for just about everything.

スター23,801
ウォッチ223
フォーク1,831
作成日:2010年12月29日
言語:JavaScript
ライセンス:MIT License

トピックス

なし

スター履歴

winstonjs/winston Star History
データ取得日時: 2025/7/17 10:01

winston

Winstonは、Node.js向けの強力で柔軟なロギングライブラリです。複数のトランスポート(出力先)、カスタマイズ可能なフォーマット、ログレベル管理など、エンタープライズアプリケーションに必要な包括的なロギング機能を提供します。

主な特徴

  • マルチトランスポート - 複数の出力先に同時ログ出力
  • カスタマイズ可能なフォーマット - 柔軟なログフォーマット設定
  • 例外ハンドリング - 未処理の例外を自動的にログ
  • チャイルドロガー - コンテキスト付きロガーの作成
  • コンテナシステム - 複数のロガーインスタンス管理

インストール

npm install winston

# TypeScript型定義(Winstonに含まれている)
# 追加の型定義は不要

# よく使用される追加トランスポート
npm install winston-daily-rotate-file

基本的な使い方

シンプルな設定

import winston from 'winston';

// 基本的なロガーの作成
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  defaultMeta: { service: 'user-service' },
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

// ログ出力
logger.info('情報メッセージ');
logger.warn('警告メッセージ');
logger.error('エラーメッセージ');

ログレベル

// デフォルトのnpmスタイルのレベル
const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  verbose: 4,
  debug: 5,
  silly: 6
};

// カスタムレベルの定義
const customLevels = {
  levels: {
    fatal: 0,
    error: 1,
    warn: 2,
    info: 3,
    debug: 4,
    trace: 5
  },
  colors: {
    fatal: 'red',
    error: 'red',
    warn: 'yellow',
    info: 'green',
    debug: 'blue',
    trace: 'magenta'
  }
};

const logger = winston.createLogger({
  levels: customLevels.levels,
  level: 'info',
  transports: [new winston.transports.Console()]
});

winston.addColors(customLevels.colors);

フォーマットのカスタマイズ

複数フォーマットの組み合わせ

import winston from 'winston';

const { combine, timestamp, label, printf, colorize, errors } = winston.format;

// カスタムフォーマット関数
const myFormat = printf(({ level, message, label, timestamp, stack }) => {
  if (stack) {
    return `${timestamp} [${label}] ${level}: ${message}\n${stack}`;
  }
  return `${timestamp} [${label}] ${level}: ${message}`;
});

const logger = winston.createLogger({
  format: combine(
    label({ label: 'my-app' }),
    timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    errors({ stack: true }),
    myFormat
  ),
  transports: [
    new winston.transports.Console({
      format: combine(
        colorize(),
        myFormat
      )
    })
  ]
});

JSON形式とメタデータ

const logger = winston.createLogger({
  format: combine(
    timestamp(),
    errors({ stack: true }),
    winston.format.metadata(),
    winston.format.json()
  ),
  defaultMeta: { 
    service: 'api-service',
    version: '1.0.0',
    environment: process.env.NODE_ENV 
  },
  transports: [
    new winston.transports.File({ filename: 'app.log' })
  ]
});

// メタデータ付きログ
logger.info('ユーザーログイン', {
  userId: 123,
  ipAddress: '192.168.1.1',
  userAgent: 'Mozilla/5.0...'
});

トランスポートの設定

ファイルトランスポート

import winston from 'winston';

const logger = winston.createLogger({
  transports: [
    // すべてのログ
    new winston.transports.File({
      filename: 'logs/combined.log',
      maxsize: 5242880, // 5MB
      maxFiles: 5,
      format: winston.format.json()
    }),
    // エラーログのみ
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
      maxsize: 5242880,
      maxFiles: 5
    }),
    // 開発環境用コンソール
    new winston.transports.Console({
      level: process.env.NODE_ENV === 'production' ? 'error' : 'debug',
      format: winston.format.simple()
    })
  ]
});

日次ローテーションファイル

import winston from 'winston';
import 'winston-daily-rotate-file';

const fileRotateTransport = new winston.transports.DailyRotateFile({
  filename: 'logs/%DATE%-combined.log',
  datePattern: 'YYYY-MM-DD',
  maxSize: '20m',
  maxFiles: '14d', // 14日間保持
  zippedArchive: true,
  format: combine(
    timestamp(),
    winston.format.json()
  )
});

const logger = winston.createLogger({
  transports: [fileRotateTransport]
});

HTTPトランスポート

const httpTransport = new winston.transports.Http({
  host: 'localhost',
  port: 3001,
  path: '/logs',
  ssl: false,
  format: winston.format.json()
});

const logger = winston.createLogger({
  transports: [httpTransport]
});

高度な機能

チャイルドロガー

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

// リクエスト処理用のチャイルドロガー
function handleRequest(req: any, res: any) {
  const childLogger = logger.child({
    requestId: req.id,
    method: req.method,
    url: req.url
  });
  
  childLogger.info('リクエスト受信');
  
  try {
    // 処理
    childLogger.info('処理完了');
  } catch (error) {
    childLogger.error('処理エラー', { error });
  }
}

例外とRejectionハンドリング

const logger = winston.createLogger({
  transports: [
    new winston.transports.File({ filename: 'combined.log' })
  ],
  exceptionHandlers: [
    new winston.transports.File({ filename: 'exceptions.log' })
  ],
  rejectionHandlers: [
    new winston.transports.File({ filename: 'rejections.log' })
  ],
  exitOnError: false // プロセスを終了しない
});

// グローバル例外ハンドリング
process.on('unhandledRejection', (reason, promise) => {
  logger.error('Unhandled Rejection at:', { promise, reason });
});

コンテナとマルチロガー

import winston from 'winston';

// コンテナの作成
const container = new winston.Container();

// 異なる設定のロガーを追加
container.add('categoryA', {
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'categoryA.log' })
  ]
});

container.add('categoryB', {
  format: winston.format.simple(),
  transports: [
    new winston.transports.File({ filename: 'categoryB.log' })
  ]
});

// 使用
const loggerA = container.get('categoryA');
const loggerB = container.get('categoryB');

プロファイリング

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

// プロファイリング開始
logger.profile('データベース処理');

// 重い処理
await performDatabaseOperation();

// プロファイリング終了(経過時間が自動的に記録される)
logger.profile('データベース処理');

TypeScript固有の実装

型安全なラッパー

import winston from 'winston';

interface LogMetadata {
  [key: string]: any;
}

class TypedLogger {
  private logger: winston.Logger;

  constructor(service: string) {
    this.logger = winston.createLogger({
      defaultMeta: { service },
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.errors({ stack: true }),
        winston.format.json()
      ),
      transports: [
        new winston.transports.Console({
          level: process.env.LOG_LEVEL || 'info'
        })
      ]
    });
  }

  info(message: string, meta?: LogMetadata): void {
    this.logger.info(message, meta);
  }

  error(message: string, error?: Error, meta?: LogMetadata): void {
    this.logger.error(message, { 
      ...meta, 
      error: error ? {
        message: error.message,
        stack: error.stack,
        name: error.name
      } : undefined
    });
  }

  debug(message: string, meta?: LogMetadata): void {
    if (this.logger.isDebugEnabled()) {
      this.logger.debug(message, meta);
    }
  }
}

// 使用例
const logger = new TypedLogger('user-service');
logger.info('ユーザー作成', { userId: 123 });

構造化ログ用インターフェース

interface LogEntry {
  timestamp: string;
  level: string;
  service: string;
  message: string;
  context?: {
    requestId?: string;
    userId?: string;
    [key: string]: any;
  };
  error?: {
    message: string;
    stack?: string;
    code?: string;
  };
}

class StructuredLogger {
  private logger: winston.Logger;

  constructor() {
    this.logger = winston.createLogger({
      format: winston.format.printf((info) => {
        const logEntry: LogEntry = {
          timestamp: new Date().toISOString(),
          level: info.level,
          service: info.service || 'app',
          message: info.message,
          context: info.context,
          error: info.error
        };
        return JSON.stringify(logEntry);
      }),
      transports: [new winston.transports.Console()]
    });
  }

  log(entry: Partial<LogEntry>): void {
    this.logger.log(entry.level || 'info', entry.message || '', entry);
  }
}

ベストプラクティス

1. 環境別設定

import winston from 'winston';

function createLogger() {
  const isDevelopment = process.env.NODE_ENV === 'development';
  const isTest = process.env.NODE_ENV === 'test';

  if (isTest) {
    return winston.createLogger({
      silent: true // テスト時はログを無効化
    });
  }

  return winston.createLogger({
    level: isDevelopment ? 'debug' : 'info',
    format: winston.format.combine(
      winston.format.timestamp(),
      isDevelopment ? winston.format.colorize() : winston.format.uncolorize(),
      winston.format.json()
    ),
    transports: [
      new winston.transports.Console({
        format: isDevelopment ? winston.format.simple() : winston.format.json()
      }),
      ...(isDevelopment ? [] : [
        new winston.transports.File({
          filename: 'logs/error.log',
          level: 'error'
        }),
        new winston.transports.File({
          filename: 'logs/combined.log'
        })
      ])
    ]
  });
}

export const logger = createLogger();

2. エラーの適切なログ

class ErrorLogger {
  constructor(private logger: winston.Logger) {}

  logError(error: Error, context?: any): void {
    const errorInfo = {
      message: error.message,
      stack: error.stack,
      name: error.name,
      ...context
    };

    if (this.isCriticalError(error)) {
      this.logger.error('重大なエラー', errorInfo);
      // アラート送信など
    } else {
      this.logger.warn('エラー', errorInfo);
    }
  }

  private isCriticalError(error: Error): boolean {
    // 重大なエラーの判定ロジック
    return error.name === 'DatabaseError' || 
           error.name === 'AuthenticationError';
  }
}

3. パフォーマンス監視

const performanceLogger = winston.createLogger({
  format: winston.format.json(),
  transports: [
    new winston.transports.File({
      filename: 'logs/performance.log',
      level: 'info'
    })
  ]
});

function logPerformance(operation: string, duration: number, metadata?: any) {
  performanceLogger.info('パフォーマンス測定', {
    operation,
    duration,
    timestamp: new Date().toISOString(),
    ...metadata
  });
}

// 使用例
const start = Date.now();
await processData();
logPerformance('データ処理', Date.now() - start, { recordCount: 1000 });

まとめ

Winstonは、Node.jsアプリケーションに包括的なロギング機能を提供する成熟したライブラリです。複数のトランスポート、柔軟なフォーマット設定、例外ハンドリングなど、エンタープライズアプリケーションに必要な機能を網羅しています。TypeScriptとの組み合わせにより、型安全で保守性の高いロギングシステムを構築できます。適切な設定とベストプラクティスに従うことで、効果的なログ管理とトラブルシューティングが可能になります。