運用・監視 レッスン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

「リトライは発生したが処理は継続できている」状態を記録するのに最も適したログレベルはどれか?