structured-logging について
structured-loggingは、検索しやすいJSONログの設計と分散サービスの障害調査を支援する開発スキルです。ログライブラリではなく、フィールド、コンテキスト伝播、レベル、性能に関する指針を示します。
主な機能
- 01JSONキー・バリュー形式
- 02必須フィールドと相関ID
- 03ログレベルの選択指針
- 04サービス間のコンテキスト伝播
- 05機密データとノイズの回避
- 06性能とサンプリングの指針
活用シーン
- 01サービス間でイベントフィールドを統一
- 02分散システム内のリクエストを追跡
- 03障害や想定内イベントのレベルを選択
- 04インシデント調査でログを確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ majesticlabs-dev/ majestic-marketplace --skill structured-loggingインストール後、Claude Code で/structured-loggingを入力して読み込まれたか確認します。
詳細
構造化ロギング
基本理念
- ログは書き込みではなく、検索に最適化します。デバッグを意識して設計してください
- 分散システムでは、相関 ID のないログは役に立ちません
- 「誰に影響があったか?何が失敗したか?いつ?なぜ?」に 5 分以内に答えられない場合は、ロギングを改善する必要があります
構造化形式
文字列補間は使わず、常にキーと値のペア(JSON)を使用してください。
{
"event": "payment_failed",
"user_id": "123",
"reason": "insufficient_funds",
"amount": 99.99,
"timestamp": "2025-01-24T20:00:00Z",
"level": "error",
"service": "billing",
"request_id": "req_abc123"
}
必須フィールド
すべてのログイベントには、必ず次の項目を含めてください。
| フィールド | 形式 | 例 |
|---|---|---|
timestamp | タイムゾーン付き ISO 8601 | 2025-01-24T20:00:00Z |
level | debug、info、warn、error | info |
event | snake_case、過去形 | user_login_succeeded |
request_id または trace_id | UUID またはプレフィックス付き ID | req_abc123 |
service | サービス/アプリ名 | api-gateway |
environment | prod、staging、dev | prod |
カーディナリティの高いフィールド
利用できる場合は次の項目を含めてください。インシデント発生時にログを検索しやすくなります。
| カテゴリ | フィールド |
|---|---|
| 識別情報 | user_id、org_id、account_id |
| トレーシング | request_id、trace_id、span_id |
| ドメイン | order_id、transaction_id、job_id |
ルール: 特定のエンティティに問題を絞り込むのに役立つ、ドメイン固有の識別子を探してください。
ログレベル
| レベル | 使用する場面 | 例 |
|---|---|---|
debug | 詳細なローカル開発情報。本番環境では無効にします | 変数の値、ループの反復 |
info | 記録する価値のある通常の処理 | ユーザー操作、ジョブの完了、デプロイ |
warn | 想定外だが処理済み | リトライの開始、フォールバックの有効化 |
error | 失敗し、対応が必要 | 例外、リクエストの失敗、タイムアウト |
アンチパターン: 想定内の状態をエラーとして記録しないでください(パスワードの誤りは info であり、error ではありません)。
コンテキストの伝播
分散システムでは、次の点に従ってください。
- ID を継承する — 下流サービスには、上流から相関 ID を渡す必要があります
- 境界を越えて引き継ぐ — HTTP ヘッダー、メッセージキュー、非同期ジョブ
- ミドルウェアで注入する — ミドルウェア/インターセプターを通じて、すべてのログにコンテキストを自動的に注入します
[Client] --request_id--> [API Gateway] --request_id--> [Service A] --request_id--> [Service B]
| | |
(logs) (logs) (logs)
↓ ↓ ↓
All queryable by single request_id
非同期ジョブ: バックグラウンド処理では、元のリクエストコンテキストを保存し、復元してください。
ログに記録する内容
| 記録する内容 | 記録しない内容 |
|---|---|
| リクエストの開始/終了と所要時間 | 機密データ(パスワード、トークン、PII、カード情報) |
| 状態遷移(作成 → 支払い済み → 発送済み) | タイトなループ内のログ |
| 外部サービスの呼び出しとレイテンシ+ステータス | デバッグに役立つ情報のない成功ケース |
| 認証/認可イベント | 重複するインフラログ(LB がすでに記録しているもの) |
| ジョブの開始、完了、失敗 | |
| リトライの試行、サーキットブレーカーの状態変化 |
命名規則
| パターン | 例 |
|---|---|
フィールド名:snake_case | user_id。userId や user-id は使いません |
| イベント:過去形の動詞 | payment_completed。complete_payment は使いません |
| 役立つ場合はドメインプレフィックスを付ける | auth.login_failed、billing.invoice_created |
チームでの合意: フィールド名は一度定義し、すべてのサービスで一貫して使用してください。
パフォーマンス
| 懸念事項 | 対策 |
|---|---|
| 大量に出力される debug ログ | 本番環境ではサンプリングする |
| ホットパスでのロギング | 避けるか、非同期アペンダーを使用する |
| I/O のオーバーヘッド | 書き込みをバッファリングしてバッチ処理する |
| 動的な詳細度 | 実行時に設定可能なログレベルを使用する |
言語別の実装
| 言語 | ライブラリ | 備考 |
|---|---|---|
| Python | structlog | majestic-data/etl-core-patterns を参照 |
| Ruby/Rails | Rails.event (8.1+)、semantic_logger | majestic-rails/dhh-coder/structured-events を参照 |
| Node.js | JSON フォーマッターを使用した pino、winston | |
| Go | slog (stdlib)、zerolog | |
| Java | JSON エンコーダーを使用した logback |
ログに記録するかどうかの判断表
| シナリオ | 判断 | 理由 |
|---|---|---|
| ユーザーが誤ったパスワードを入力した | info | 想定される動作であり、エラーではありません |
| 決済ゲートウェイがタイムアウトした | error + 再試行 | 対応が必要で、ユーザーに影響します |
| キャッシュミス | debug | パフォーマンス分析にのみ役立ちます |
| ユーザーがアカウントを作成した | info | 記録する価値のあるビジネスイベントです |
| 10000 回のループのうち 5000 回目の反復 | ログに記録しない | ノイズが増え、デバッグに役立ちません |
| 外部 API が 500 を返した | warn または error | 再試行やフォールバックの動作によって異なります |
| バックグラウンドジョブが開始した | info | ジョブのデバッグに役立ちます |
| 再試行後にバックグラウンドジョブが失敗した | error | 調査が必要です |
インシデントのデバッグチェックリスト
ログを設計する際、次の質問に答えられることを確認してください。
- 誰 — 特定のユーザー/組織/アカウントに絞り込めますか?
- 何 — 失敗した操作を正確に特定できますか?
- いつ — 特定の時間帯に絞り込めますか?
- なぜ — エラーのコンテキスト(理由、上流の原因)が記録されていますか?
- どこ — 相関 ID を使ってサービス間を追跡できますか?
インシデント発生後: あればよかったと思うログを追加してください。
コメント
Markdown 対応