ログ設計書の位置づけ

ログは「過去の出来事の記録」だ。適切なログがないと障害調査は「推測」に頼ることになる。設計段階でログの種別・レベル・フォーマット・保持期間を定義し、全員が統一した実装を行うことが重要だ。

⚠️ ログ設計のアンチパターン

開発者ごとにフォーマットが異なる・DEBUG ログが本番に残る・個人情報をそのままログ出力する・ログローテーション設定がない — これらはよくある問題だ。設計書で統一ルールを定めておく。

ログ種別定義

ログ種別説明出力先保持期間
アクセスログ全HTTPリクエスト・レスポンス情報/var/log/app/access.log90日
アプリケーションログ業務処理・エラー・警告/var/log/app/app.log365日
バッチログバッチ処理の開始・終了・処理件数/var/log/batch/batch.log365日
監査ログ認証・重要データの参照・更新操作DB: audit_logs テーブル5年
パフォーマンスログレスポンスタイム・DB実行時間/var/log/app/perf.log30日

ログレベル定義

レベル用途本番出力
ERROR即時対応が必要な障害DB接続失敗・外部IF障害・未捕捉例外
WARN監視が必要な警告(即時対応不要)リトライ発生・レスポンス遅延・在庫残少
INFO業務的な重要イベント注文確定・バッチ開始/終了・ログイン成功
DEBUG開発・調査用の詳細情報✗(開発環境のみ)SQL クエリ詳細・変数値・処理ステップ
TRACE最詳細のデバッグ情報メソッド入出力・ループ処理詳細

ログフォーマット定義

ログフォーマット定義(JSON 構造化ログ)
■ 採用フォーマット: JSON(構造化ログ)
  理由: CloudWatch Logs / Datadog / Elasticsearch で自動パースが可能

■ フィールド定義
{
  "timestamp":  "2026-06-18T12:00:00.123Z",    // ISO8601 UTC
  "level":      "ERROR",                         // ERROR/WARN/INFO/DEBUG
  "logger":     "com.example.order.OrderService",// クラス名
  "traceId":    "abc123def456",                  // リクエスト単位の一意ID
  "userId":     12345,                           // 操作ユーザーID(null可)
  "errorCode":  "BIZ_001",                       // エラーコード(エラー時のみ)
  "message":    "在庫引当失敗 product_id=456",   // ログメッセージ
  "context": {                                   // 追加コンテキスト
    "product_id": 456,
    "requested": 10,
    "available": 3
  },
  "elapsed_ms": 23                               // 処理時間(ms)
}

■ 禁止事項
- パスワード・APIキーの出力
- クレジットカード番号・銀行口座番号
- マイナンバー・パスポート番号
- メールアドレス(アクセスログを除く)
  ※ ユーザーIDのみで追跡可能にする設計とする

出力先・保持期間

項目設定値
ログローテーション日次(00:00)にローテーション
圧縮ローテーション後に gzip 圧縮
最大ファイルサイズ500MB(超過時は即時ローテーション)
リモート転送CloudWatch Logs に 15分以内に転送
アラート閾値ERROR が 1分間に 10件以上で PagerDuty 通知

必須出力ログ一覧

イベントレベル必須フィールド
ログイン成功INFOuserId, ip, user_agent
ログイン失敗WARNloginId(マスク), ip, reason
注文確定INFOuserId, orderId, totalAmount
外部IF呼び出しINFOifId, elapsed_ms, status
バッチ開始INFObatchId, targetDate
バッチ終了INFObatchId, processedCount, elapsed_ms
未捕捉例外ERRORerrorCode, exception, stackTrace

Python Tips — structured logging の実装

Python — JSON 構造化ログの設定と使い方
"""
Python での JSON 構造化ログ実装
pip install python-json-logger
"""
import logging, json
from datetime import datetime, timezone
from pythonjsonlogger import jsonlogger

class AppJsonFormatter(jsonlogger.JsonFormatter):
    def add_fields(self, log_record, record, message_dict):
        super().add_fields(log_record, record, message_dict)
        log_record["timestamp"] = datetime.now(timezone.utc).isoformat()
        log_record["level"] = record.levelname
        log_record["logger"] = record.name
        # traceId はリクエストスコープから取得(例: contextvars)
        import contextvars
        trace_id = contextvars.copy_context().get(
            contextvars.ContextVar("trace_id"), None)
        if trace_id:
            log_record["traceId"] = trace_id.get(None)

def setup_logging(level=logging.INFO):
    handler = logging.StreamHandler()
    handler.setFormatter(AppJsonFormatter(
        "%(timestamp)s %(level)s %(logger)s %(message)s"
    ))
    logging.basicConfig(level=level, handlers=[handler])

setup_logging()
logger = logging.getLogger("com.example.order.OrderService")

# 使い方
logger.info("注文確定", extra={"userId": 12345, "orderId": 67890, "totalAmount": 11000})
logger.warning("在庫残少", extra={"product_id": 456, "remaining": 5})
logger.error("在庫引当失敗",
             extra={"errorCode": "BIZ_001",
                    "context": {"product_id": 456, "requested": 10, "available": 3}})

# 出力例
# {"timestamp": "2026-06-18T03:00:00.123Z", "level": "INFO",
#  "logger": "com.example.order.OrderService",
#  "message": "注文確定", "userId": 12345, "orderId": 67890, "totalAmount": 11000}

レビューチェックリスト

#チェック項目
1ログ種別・出力先・保持期間が定義されているか
2ログレベルの判断基準が定義されているか
3ログフォーマットが統一されているか(JSON推奨)
4個人情報・機密情報のログ出力禁止が明記されているか
5ローテーション・圧縮・保持期間が定義されているか
6アラート通知の閾値・通知先が定義されているか