🦓
[Express.js]WinstonとMorganでロギングを導入する
はじめに
ExpressとTypeScriptで作成されたAPIサーバーに、WinstonとMorganを使用してロギング管理を実装します。
WinstonはNode.js向けの多機能ロギングライブラリ、MorganはExpress専用のHTTPリクエストロギングミドルウェアです。
ロガーを導入することで、デバッグのスピードが大きく向上します。構造化されたログにより、エラーの発生場所や原因を素早く特定でき、問題解決までの時間を短縮できます。
アプリケーションの運用、開発、保守においてロガーを導入することがおすすめです。
ロガーとconsole.logの違い
-
console.logでは単純な出力のみですが、ロガーでは情報の重要度に応じて適切なログレベル(error、warn、info、debug等)を設定できます。 -
console.logはコンソールにのみ出力されますが、ロガーは様々な出力先(ファイル、外部サービス、データベースなど)に柔軟に出力できます。 -
console.logは単純なテキスト出力ですが、ロガーは構造化された形式(JSON等)でログを出力でき、後の検索が容易です。 - 開発環境と本番環境で異なるログ設定を適用できます。
-
console.logは同期的な処理となりますが、ロガーは非同期処理やバッファリングが可能で、アプリのパフォーマンスへの影響を最小限に抑えられます。
前提条件
- Node.js と npm がインストールされていること
- Express + TypeScriptで作成されたプロジェクト
tl;dr
- 必要なパッケージをインストールする
- Winstonの設定ファイルを作成
- Morganの設定ファイルを作成
- アプリへの統合
- ログの使用例
- 通常のログ出力
- 非同期処理でのログ出力
- ログローテーションの追加
1. 必要なパッケージをインストールする
npm install winston morgan
npm instsll -D @types/morgan
2. Winstonの設定ファイルを作成
src/config/logger.tsにWinstonの設定を作成します。
src/config/logger.ts
import winston from 'winston'
// ログレベル
const levels = {
error: 0,
warn: 1,
info: 2,
http: 3,
debug: 4,
};
// ログカラー
const colors = {
error: 'red',
warn: 'yellow',
info: 'green',
http: 'magenta',
debug: 'white',
};
// ログフォーマットの定義
const logFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json()
)
// ロガーの作成
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
transports: [
// コンソールログ
new winston.transports.Console(),
]
})
// 開発環境の場合はコンソールにも出力
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
}))
}
export default logger
2. Morganの設定
src/middleware/morgan.tsにMorganの設定を作成します。
src/middleware/morgan.ts
import morgan from 'morgan'
import logger from '../config/logger'
// Morganのストリームを定義
const stream = {
write: (message: string) => {
logger.info(message.trim())
}
}
// カスタムフォーマットの定義
const format = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" - :response-time ms'
// Morganミドルウェアの作成
const morganMiddleware = morgan(format, { stream })
export default morganMiddleware
ログのフォーマットは要件に合わせてカスタマイズすることができます。
デフォルトでは以下のように出力されます。
morgan(':method :url :status :res[content-length] - :response-time ms')
定義したMorganのフォーマット文字列は、HTTPリクエストのログを以下のような形式で出力します:
192.168.1.1 - john [10/Oct/2023:13:55:36 +0000] "GET /api/users HTTP/1.1" 200 2890 "http://example.com" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" - 50.3 ms
-
:remote-addr→192.168.1.1(クライアントのIPアドレス) -
:remote-user→john(認証されたユーザー名、ない場合は-) -
:date[clf]→10/Oct/2023:13:55:36 +0000(リクエスト時刻) -
:method→GET(HTTPメソッド) -
:url→/api/users(リクエストURL) -
:http-version→1.1(HTTPバージョン) -
:status→200(HTTPステータスコード) -
:res[content-length]→2890(レスポンスのサイズ[バイト]) -
:referrer→http://example.com(リファラー) -
:user-agent→Mozilla/5.0...(ユーザーエージェント) -
:response-time→50.3(レスポンス時間[ミリ秒])
3. アプリケーションへの統合
src/app.tsでミドルウェアを統合します。
import express from 'express'
import morganMiddleware from './middleware/morgan'
import logger from './config/logger'
const app = express()
// Morganミドルウェアの適用
app.use(morganMiddleware)
export default app
ログの使用例
1. 通常のログ出力
// 情報ログ
logger.info('User logged in', { userId: user.id })
// エラーログ
logger.error('Database connection failed', { error: err })
2. 非同期処理でのログ出力
async function fetchUserData(userId: string): Promise<User>{
try {
logger.info('Fetching user data', { userId })
const user = await User.findById(userId)
logger.info('User data retrieved successfully', { userId })
return user
} catch (error) {
logger.error('Failed to fetch user data', {
userId,
error: error.message
})
throw error
}
}
ログローテーションの追加
ログローテーションとはログファイルを一定の基準(サイズ、日付など)で分割・管理する仕組みです。
これにより、古いログの自動アーカイブ/削除が実現できます。
winston-daily-rotate-file をインストールして、ロガーに Transport を追加します。
npm install winston-daily-rotate-file
src/config/logger.ts
import winston from 'winston'
+ import DailyRotateFile from 'winston-daily-rotate-file';
// ロガーの作成
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
transports: [
// コンソールログ
new winston.transports.Console(),
+ // エラーログ
+ new DailyRotateFile({
+ filename: 'error-%DATE%.log',
+ level: 'error',
+ ...rotateFileConfig,
+ }),
+
+ // 全ログ
+ new DailyRotateFile({
+ filename: 'combined-%DATE%.log',
+ ...rotateFileConfig,
+ }),
]
})
export default logger
主な設定オプション
-
filename: ログファイルの名前パターン -
datePattern: 日付フォーマット -
maxSize: ファイルの最大サイズ -
maxFiles: 保持する最大ファイル数/期間 -
zippedArchive: 古いログの圧縮有無 -
auditFile: ログファイルの管理情報を保存
APIを叩いてログがログファイルに出力されたことを確認します。
また、logs/を.gitignoreに追加することも忘れないようにしましょう。
まとめ
ExpressアプリにWinstonとMorganを導入してロギング管理を実装してみました。
誰かの参考になれば嬉しいです。
Discussion