Winston

Node.js向けの最も汎用性の高いロギングライブラリ(17,000+ GitHub スター)。豊富なtransportとフォーマットオプション、高度なカスタマイズ性を提供。未捕捉例外の自動追跡、複数のクラウドロギングサービス(AWS CloudWatch、logz.io等)への送信が可能。

ロギングNode.jsJavaScriptTypeScriptモニタリングデバッグ

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は「A logger for just about everything」として知られる、Node.js界で最も人気の高いロギングライブラリです。モジュラー設計により、複数のトランスポート(出力先)と柔軟なフォーマット機能を提供し、開発からプロダクション環境まで幅広いシナリオに対応します。25,350を超えるプロジェクトで使用される実績豊富なライブラリです。

詳細

Winston 3.17.0は2025年現在も活発に開発されているNode.js界のデファクトスタンダードロガーです。「ログメッセージの作成、フォーマット、転送」を簡素化するために設計され、コンソール、ファイル、HTTP、データベース等への同時出力をサポート。express-winstonによるExpress.js統合やOpenTelemetryサポートなど、現代的な要求に対応しています。

主な特徴

  • マルチトランスポート: コンソール、ファイル、HTTP、クラウドサービス等への同時出力
  • 柔軟なフォーマット: JSON、カスタムprintf、組み合わせ可能なフォーマッター
  • ログレベル管理: NPMスタイル(error, warn, info, http, verbose, debug, silly)
  • プロファイリング機能: 実行時間測定とパフォーマンス分析
  • 例外ハンドリング: 未処理例外とPromise拒否の自動ログ出力
  • Express.js統合: HTTPリクエスト/レスポンスの包括的ログ機能

メリット・デメリット

メリット

  • Node.js界で最も歴史と実績のあるロガー(10年以上の開発実績)
  • 豊富なトランスポートオプション(50以上のサードパーティ製を含む)
  • 高い拡張性とカスタマイズ性(カスタムフォーマット、トランスポート)
  • プロダクション対応機能(例外処理、プロファイリング、パフォーマンス最適化)
  • 充実したエコシステム(express-winston、winston-daily-rotate-file等)
  • 包括的なドキュメントと豊富なコミュニティリソース

デメリット

  • 学習曲線がやや急で初期設定が複雑
  • TypeScript型安全性が新しいライブラリより劣る場合がある
  • 大量ログ出力時のパフォーマンスチューニングが必要
  • 複雑な設定でのメモリ使用量増加
  • 一部の古いAPIとモダンJavaScript構文の混在
  • デバッグ時のエラーメッセージが分かりにくいことがある

参考ページ

書き方の例

基本的なセットアップ

npm install winston
npm install winston-daily-rotate-file  # ローテーション機能用
npm install express-winston  # Express.js統合用

シンプルなロガー作成

const winston = require('winston');

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

// 開発環境では追加でコンソール出力
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}

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

カスタムフォーマットの使用

const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf, colorize } = format;

// カスタムフォーマット定義
const myFormat = printf(({ level, message, label, timestamp, ...meta }) => {
  const metaString = Object.keys(meta).length ? JSON.stringify(meta, null, 2) : '';
  return `${timestamp} [${label}] ${level}: ${message} ${metaString}`;
});

const logger = createLogger({
  level: 'debug',
  format: combine(
    label({ label: 'MyApp' }),
    timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    colorize(),
    myFormat
  ),
  transports: [
    new transports.Console(),
    new transports.File({
      filename: 'app.log',
      format: format.json()  // ファイルはJSON形式
    })
  ]
});

// 使用例
logger.info('ユーザーがログインしました', {
  userId: 123,
  email: '[email protected]',
  ip: '192.168.1.1'
});

複数トランスポートの設定

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

// 日次ローテーションファイル設定
const errorTransport = new DailyRotateFile({
  filename: 'logs/error-%DATE%.log',
  datePattern: 'YYYY-MM-DD-HH',
  level: 'error',
  zippedArchive: true,
  maxSize: '20m',
  maxFiles: '14d'
});

const combinedTransport = new DailyRotateFile({
  filename: 'logs/combined-%DATE%.log',
  datePattern: 'YYYY-MM-DD',
  zippedArchive: true,
  maxSize: '20m',
  maxFiles: '30d'
});

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    errorTransport,
    combinedTransport,
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      )
    })
  ]
});

// HTTP送信用トランスポート
logger.add(new winston.transports.Http({
  host: 'log-server.example.com',
  port: 8080,
  path: '/logs',
  level: 'error'
}));

Express.js統合

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

const app = express();

// リクエストログ(ルートハンドラー前)
app.use(expressWinston.logger({
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'requests.log' })
  ],
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.json()
  ),
  meta: true, // リクエスト/レスポンスのメタデータを含める
  msg: "HTTP {{req.method}} {{req.url}}",
  expressFormat: true,
  colorize: false,
  ignoreRoute: function (req, res) {
    return false; // すべてのルートをログ
  }
}));

// アプリケーションルート
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// エラーログ(ルートハンドラー後)
app.use(expressWinston.errorLogger({
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'errors.log' })
  ],
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.json()
  )
}));

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

プロファイリングと例外処理

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'combined.log' })
  ],
  // 例外ハンドリング
  exceptionHandlers: [
    new winston.transports.File({ filename: 'exceptions.log' })
  ],
  // Promise拒否ハンドリング
  rejectionHandlers: [
    new winston.transports.File({ filename: 'rejections.log' })
  ]
});

// プロファイリング機能
logger.profile('database-query');

// データベース操作のシミュレーション
setTimeout(() => {
  logger.profile('database-query', { message: 'データベースクエリ完了' });
}, 1000);

// タイマー機能
const profiler = logger.startTimer();
setTimeout(() => {
  profiler.done({ message: '非同期処理完了' });
}, 2000);

// 構造化ログ
logger.info('ユーザー操作ログ', {
  action: 'login',
  userId: 'user123',
  timestamp: new Date().toISOString(),
  metadata: {
    userAgent: 'Mozilla/5.0...',
    ip: '192.168.1.100',
    sessionId: 'sess_abc123'
  }
});

カスタムログレベルとフィルタリング

const winston = require('winston');

// カスタムログレベル定義
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: 'gray'
  }
};

// カスタムフィルタ(機密情報除外)
const sensitiveFilter = winston.format((info) => {
  if (info.private || info.password || info.secret) {
    return false; // ログ出力しない
  }
  return info;
});

const logger = winston.createLogger({
  levels: customLevels.levels,
  format: winston.format.combine(
    sensitiveFilter(),
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: winston.format.combine(
        winston.format.colorize({ colors: customLevels.colors }),
        winston.format.simple()
      )
    }),
    new winston.transports.File({
      filename: 'app.log',
      level: 'info'
    })
  ]
});

// カスタムレベルの使用
logger.fatal('致命的エラーが発生しました');
logger.trace('詳細なトレース情報');

// フィルタされるログ(出力されない)
logger.info('ログイン試行', {
  username: 'user123',
  password: 'secret123'  // この情報は出力されない
});