ストーリー
高橋アーキテクトはうなずいた。
ログレベルの定義
// ログレベルの優先度(低い → 高い)
enum LogLevel {
TRACE = 10, // 最も詳細なデバッグ情報
DEBUG = 20, // デバッグ用の情報
INFO = 30, // 通常の動作記録
WARN = 40, // 注意が必要な状態
ERROR = 50, // エラー発生(処理は継続可能)
FATAL = 60, // 致命的エラー(プロセス停止)
}
各レベルの使い分けガイドライン
| レベル | 用途 | 例 | 環境 |
|---|---|---|---|
| TRACE | 変数の値、ループの各ステップ | "Entering function X with params..." | 開発のみ |
| DEBUG | デバッグに役立つ内部状態 | "Cache hit for key: user_42" | 開発/ステージング |
| INFO | 正常な業務イベント | "Order ORD-001 created successfully" | 全環境 |
| WARN | 異常だが処理は継続可能 | "Retry attempt 2/3 for payment" | 全環境 |
| ERROR | エラー発生、個別処理が失敗 | "Payment failed: timeout" | 全環境 |
| FATAL | 致命的エラー、プロセス停止 | "Database connection lost, shutting down" | 全環境 |
よくある間違いと正しい使い方
// 間違い: すべてをINFOで出す
logger.info('Starting to process order'); // OK
logger.info('Order validation passed'); // DEBUG が適切
logger.info('SQL query: SELECT * FROM ...'); // TRACE が適切
logger.info('Payment failed: timeout'); // ERROR が適切
// 正しい使い分け
logger.debug('Order validation passed', { orderId });
logger.trace({ query: sql, params }, 'Executing SQL query');
logger.info({ orderId, amount }, 'Order created successfully');
logger.warn({ orderId, attempt: 2 }, 'Payment retry');
logger.error({ orderId, error: err.message }, 'Payment failed');
環境ごとのログレベル設定
// 環境変数でログレベルを制御
const logConfig: Record<string, string> = {
development: 'debug',
staging: 'debug',
production: 'info',
};
const logger = pino({
level: process.env.LOG_LEVEL || logConfig[process.env.NODE_ENV || 'development'],
});
// 本番環境で一時的にDEBUGレベルに変更する仕組み
// 環境変数の動的変更 or 設定ファイルのホットリロード
interface DynamicLogConfig {
defaultLevel: string;
overrides: {
service?: string;
module?: string;
level: string;
expiresAt: string; // 自動で元に戻る
}[];
}
| 環境 | 推奨レベル | 理由 |
|---|---|---|
| 開発 | DEBUG/TRACE | デバッグ情報が必要 |
| ステージング | DEBUG | 本番に近い環境でのデバッグ |
| 本番 | INFO | ノイズを減らし、コストを抑える |
| 障害調査時 | DEBUG(一時的) | 詳細情報が必要な場合のみ |
フィルタリングとサンプリング
大量のログを効率的に管理するための戦略です。
// サンプリング: 正常系ログは一部だけ記録
import pino from 'pino';
const logger = pino({
level: 'info',
hooks: {
logMethod(inputArgs, method, level) {
// ERRORとWARNは100%記録、INFOは10%サンプリング
if (level >= 40) {
method.apply(this, inputArgs);
} else if (Math.random() < 0.1) {
method.apply(this, inputArgs);
}
},
},
});
// フィルタリング: 特定の条件でログを除外
function shouldLog(logEntry: LogContext): boolean {
// ヘルスチェックのログは除外
if (logEntry.path === '/health') return false;
// 静的ファイルのアクセスログは除外
if (logEntry.path?.startsWith('/static/')) return false;
// Kubernetes のプローブは除外
if (logEntry.userAgent?.includes('kube-probe')) return false;
return true;
}
ログローテーションと保持ポリシー
// ログの保持期間設計
interface LogRetentionPolicy {
level: string;
retentionDays: number;
storageClass: string;
}
const retentionPolicies: LogRetentionPolicy[] = [
{ level: 'ERROR', retentionDays: 90, storageClass: 'hot' },
{ level: 'WARN', retentionDays: 30, storageClass: 'warm' },
{ level: 'INFO', retentionDays: 14, storageClass: 'warm' },
{ level: 'DEBUG', retentionDays: 3, storageClass: 'cold' },
];
| レベル | 保持期間 | ストレージ | コスト |
|---|---|---|---|
| ERROR/FATAL | 90日 | ホット(即時検索可能) | 高 |
| WARN | 30日 | ウォーム(数秒で検索可能) | 中 |
| INFO | 14日 | ウォーム | 中 |
| DEBUG | 3日 | コールド(復元に時間がかかる) | 低 |
まとめ
| ポイント | 内容 |
|---|---|
| ログレベル | TRACE〜FATALの6段階を正しく使い分ける |
| 環境別設定 | 本番はINFO以上、開発はDEBUG以上 |
| フィルタリング | ヘルスチェック等のノイズを除外 |
| 保持ポリシー | レベルに応じた保持期間とストレージ階層 |
チェックリスト
- 6つのログレベルの使い分けを説明できる
- 環境ごとのログレベル設定の考え方を理解した
- フィルタリングとサンプリングの戦略を把握した
- ログローテーションと保持ポリシーを設計できる
次のステップへ
次は「集約と検索」を学びます。ELK StackやCloudWatch Logsを使ったログの集中管理方法を見ていきましょう。
推定読了時間: 30分