運用・監視 レッスン2
ログ設計
障害調査に役立つログレベル・構造化ログの設計と、ログを溜めすぎず活用するための工夫を学ぶ
ログレベルの使い分け
ログには重要度に応じたレベルを設定し、 目的に応じて出力する情報の量を切り替えられるようにします。
error
正常な処理が継続できない異常。即座の対応が必要になりうる。
例: 決済ゲートウェイへの接続失敗、未処理の例外
warn
処理は継続できるが、注意すべき予兆。
例: リトライの発生、キューの滞留増加
info
正常な動作の記録。ビジネスイベントの追跡に有用。
例: 注文確定、バッチ処理の開始・終了
debug
開発・調査時の詳細情報。本番では通常無効化する。
例: 関数の引数、内部状態のダンプ
構造化ログ
自由なテキストではなくJSONなどの構造化ログで出力すると、 機械的な検索・集計・フィルタリングが容易になります。
// 非構造化ログ(従来のスタイル)
console.log("2026-03-01 09:12:03 ERROR order ord-4821 payment failed: gateway_timeout");
// → 「gateway_timeoutで失敗した注文だけ」を機械的に抽出しづらい
// 構造化ログ(推奨)
logger.error("payment failed", {
orderId: "ord-4821",
reason: "gateway_timeout",
correlationId: req.correlationId,
});
// 出力: {"timestamp":"...","level":"error","msg":"payment failed",
// "orderId":"ord-4821","reason":"gateway_timeout","correlationId":"..."}Correlation ID(相関ID)
1つのリクエストに関わる全てのログに共通のIDを付与しておくと、そのIDで検索するだけで リクエストの一生分のログを時系列で追跡でき、障害調査の時間を大きく短縮できます。
何を記録し、何を記録しないか
障害調査やビジネス分析に役立つ情報を記録する一方で、機密情報の扱いには注意が必要です。
// 記録すべき情報の例
logger.info("order created", {
orderId: "ord-9931",
userId: "usr-5502",
totalAmount: 3480,
paymentMethod: "credit_card",
});
// 記録してはいけない情報(セキュリティリスク)
// NG: パスワード、カード番号全体、認証トークンそのもの
logger.info("login attempt", {
email: "user_502@example.com",
password: "hunter2", // 絶対にNGな例
});
// OK: 機密情報はマスキングするか存在有無だけ記録する
logger.info("login attempt", {
email: "u***@example.com",
hasPassword: true,
});ログを溜めすぎないための工夫
ログは資産であると同時に、保管し続ければストレージコストと検索性能を圧迫する負債にもなります。 代表的な対策は以下の通りです。
- 保持期間の設定: 重要度に応じてログの保存期間を分け、古いものは自動削除・圧縮する
- サンプリング: debugレベルなど大量に出るログは、一定割合だけ残す
- ログローテーション: 一定サイズ・期間でファイルを切り替え、古いファイルを圧縮・削除する
# ログローテーション設定の例(概念)
rotate:
filenamePattern: "logs/app-%DATE%.log"
maxSizePerFile: "20m"
retentionDays: 14
compressOldFiles: trueポイント
- ログレベル(error/warn/info/debug)を適切に使い分ける
- 構造化ログとCorrelation IDで検索性と追跡性を高める
- パスワードやカード番号などの機密情報はログに残さず、マスキングする
- 保持期間・サンプリング・ローテーションでログの肥大化を防ぐ
確認クイズ
1 / 3「リトライは発生したが処理は継続できている」状態を記録するのに最も適したログレベルはどれか?