Winston
Node.js向けの最も汎用性の高いロギングライブラリ(17,000+ GitHub スター)。豊富なtransportとフォーマットオプション、高度なカスタマイズ性を提供。未捕捉例外の自動追跡、複数のクラウドロギングサービス(AWS CloudWatch、logz.io等)への送信が可能。
GitHub概要
winstonjs/winston
A logger for just about everything.
スター23,801
ウォッチ223
フォーク1,831
作成日:2010年12月29日
言語:JavaScript
ライセンス:MIT License
トピックス
なし
スター履歴
データ取得日時: 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' // この情報は出力されない
});