awesome-logging-standards について
一貫した構造と有用なコンテキストを備え、秘密情報や個人情報の露出を避けるログの追加・レビュー向けスキルです。ログ方針の策定、計装、サービスのデバッグに取り組む開発者を対象とします。
主な機能
- 01検索可能な構造化ログの推奨
- 02ログレベル統一の指針
- 03イベントへの運用コンテキスト追加
- 04秘密情報と個人情報の記録防止
- 05検証手順とレビュー項目
活用シーン
- 01サービスエンドポイントへのログ追加
- 02情報漏えい後のログレビュー
- 03プロジェクトのログ規約策定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ khasky/ awesome-agent-skills --skill awesome-logging-standardsインストール後、Claude Code で/awesome-logging-standardsを入力して読み込まれたか確認します。
同梱ファイルevals/eval_queries.json
詳細
ロギング標準
シークレットや個人識別情報を漏らさず、運用上の問題をデバッグできるよう、一貫したロギングを適用します。
有効にするタイミング
- ログ文の追加やリファクタリングを行うとき
- ユーザーが「ロギング」「ログ形式」「何を記録すべきか」について尋ねたとき
- プロジェクトのロギング標準を定義またはレビューするとき
- ログが不十分だった、またはデータが漏えいしたインシデントの後
基本原則
- 計測の前に「正常に動作している状態」を定義します — オンコール担当者が実際に尋ねる2〜4個の質問(「このユーザーのチェックアウトは成功したか?」「どの依存先が遅いか?」など)を書き出し、すべてのシグナルがそのいずれかに対応するようにします。運用上の疑問に答えないログ行はノイズです。
- 構造化します — ログを検索・解析できるよう、長い文章よりもキーと値のフィールド(例:JSON)を優先します。アプリ全体で同じ構造(タイムスタンプ、レベル、メッセージ、フィールド)を使います。
- カーディナリティ — メトリクスやラベルのインデックスキー/ラベルキーに、上限のない値(user_id、email、完全なURL、生のエラーテキスト)を決して使わないでください。カーディナリティが急増するため、こうした値はイベントフィールドとして保持します。原因(特定のホストのCPU使用率)ではなく、症状(ユーザーに見える障害)に対してアラートを設定します。
- テレメトリ自体を検証します — 計測を追加した後、障害を発生させ、ログやメトリクスだけからその障害を見つけられることを確認します。未検証の可観測性は、気付かれないまま誤っていることがあります。たとえば、メトリクス生成より前にサンプリングすると、何も問題がないように見える一方で、リクエスト率のメトリクスがサンプリング率分だけずれます。
- レベル — 一貫して使います:ERROR(障害、例外)、WARN(回復可能な問題、非推奨事項)、INFO(重要なビジネスイベント、リクエストの概要)、DEBUG(詳細な処理フロー。本番環境では無効化するかサンプリングします)。
- コンテキスト — 利用可能な場合は、request_id、trace_id、または correlation_id を含めます。ポリシー上安全で許可されている場合に限り、user_id、order_id などを含めます。必要かつコンプライアンスに準拠する場合を除き、完全な個人識別情報(メールアドレス、電話番号、住所)を平文で記録しないでください。
- シークレットを記録しません — パスワード、トークン、API キー、完全なカード番号を決して記録しないでください。マスキングするか、省略します。デバッグ目的の場合も、ポリシーで許可されている場合に限り、マスキングするか下4桁のみを表示します。
- 一か所に集約します — プロジェクトのロギングライブラリ(Winston、Pino、log4j、structlog など)を使い、プラットフォームが収集するものと同じパイプライン(例:stdout)に出力します。
作業手順
- 既存の慣例を確認します — プロジェクトではどの形式(JSON、プレーンテキスト)を使っていますか?どのライブラリやレベルを使っていますか?それに合わせます。
- レベルを選びます — 失敗には error、回復可能な問題や非推奨事項には warn、重要なアクション(リクエスト完了、注文作成)には info、詳細な処理フローには debug を使います。負荷の高い処理経路で、項目ごとの冗長なログに info を使わないでください。debug またはサンプリングした info を使います。
- コンテキストを追加します — リクエスト ID、操作名、所要時間、ステータスを含めます。識別子(user_id、order_id)はポリシーで許可されている場合に限ります。ロガーが構造化フィールドに対応している場合は、メッセージ文字列に埋め込まず、構造化フィールドを使います。
- マスキングします — シークレットを含めず、メッセージやフィールドに完全な個人識別情報を含めません。デバッグのために機密情報を記録する必要がある場合は、マスキングまたはサンプリングを使い、その方法を文書化します。
- 検証します — ログが stdout または設定済みの出力先に送られること、形式を解析できること、サンプル出力にシークレットや個人識別情報が含まれないことを確認します。
記録する内容
| カテゴリ | レベル | 内容 | 記録しない内容 |
|---|---|---|---|
| リクエストの開始/終了 | INFO | method、path(クエリ文字列を除去)、status、duration_ms、request_id | 本文、トークンを含むヘッダー、クエリ文字列、トークンや個人識別情報を含むパスのセグメント |
| エラー | ERROR | メッセージ、エラー種別、スタック(サーバー側)、request_id | 完全なリクエスト/レスポンス、シークレット |
| 回復可能な問題 | WARN | メッセージ、コンテキスト(例:再試行回数)、request_id | |
| 重要なビジネスイベント | INFO | イベント名、関連 ID(安全な場合は order_id、user_id)、結果 | 完全なペイロード、個人識別情報 |
| 外部呼び出し | INFO または DEBUG | サービス、操作、duration_ms、結果(成功/失敗) | 完全なリクエスト/レスポンス、認証情報 |
| 詳細な処理フロー | DEBUG | ステップ、状態、ID | シークレット、個人識別情報 |
形式(構造化)
JSON(本番環境での利用を推奨):
{
"timestamp": "2024-03-15T10:30:00.123Z",
"level": "info",
"message": "Request completed",
"request_id": "abc-123",
"method": "GET",
"path": "/api/orders",
"status": 200,
"duration_ms": 45
}
フィールド:一貫性のある名前(プロジェクトに応じて snake_case または camelCase)を優先してください。変数データはメッセージ文字列だけに含めるのではなく、フィールドに格納し、ログを検索できるようにしてください。
ワイドイベント(標準的なログ行)
リクエスト駆動型のサービスでは、ログ行を分散させるよりも、サービスごとにリクエストごとのコンテキストが豊富な構造化イベントを1つ記録することを推奨します。
- ミドルウェアがイベントを作成し、タイミング、ステータス、出力を管理します。失敗時にもイベントが記録されるよう、
finallyで出力してください。ハンドラーは処理の過程で業務フィールドのみを追加します。 - すべてのイベントに環境フィールドを含めてください。コミットハッシュ、サービスのバージョン、リージョンなどです。
- 仕組みだけでなく、業務上のコンテキストを記録してください。「チェックアウトに失敗した」よりも「プレミアム顧客の2,499ドルのチェックアウトに失敗した」のほうが適切です。
- 手順ごとに分散したログ行はデバッグレベルに留めてください。何が起きたかを検索できる記録には、ワイドイベントを使います。
良い例と悪い例
良い例:
logger.info({ request_id, method, path, status, duration_ms }, 'Request completed');
logger.error({ err, request_id }, 'Payment failed');
logger.debug({ order_id, step: 'validation' }, 'Validating order');
悪い例:
console.log('User ' + user.email + ' did something'); // PII in log
logger.info('Token: ' + token); // Secret in log
logger.error('Error: ' + err); // May include stack or internal detail in message; use structured field
ルール
- リクエストやレスポンスの全内容、または環境変数を丸ごと出力するログを追加しないでください。デバッグに必要な場合は、秘匿化やサンプリングを提案してください。
- プロジェクトにログやプライバシーに関するポリシー(保持期間、PII、シークレット)がある場合は、それに従ってください。
- 書き込む前に
pathをサニタイズしてください。クエリ文字列を削除し、トークン、メールアドレス、その他の個人識別子を含むパスセグメントをルートテンプレート(/users/:id)に置き換えてください。完全なアドレスが不要な場合は、クライアント IP をマスキングしてください(IPv4 の末尾オクテット、IPv6 の末尾80ビットをゼロにします)。 - データではなくイベントを記録してください。ユーザー ID とともに「ユーザーがメールアドレスを変更した」と記録し、変更前後の値は決して記録しないでください。
- 監査ログ(機密情報の読み取り、管理者の操作)はアプリケーションログと分離し、独自の保持期間とアクセスリストを設定してください。アプリログは広く参照可能で短期間保持しますが、監査ログはそのどちらにも該当しません。
- コードベースの他の部分と同じライブラリ、形式を使用してください。十分な理由なく、別のログシステムを導入しないでください。
チェックリスト
- 適切なレベル(error/warn/info/debug)
- コンテキストを含める(該当する場合は request_id、operation、duration)
- メッセージやフィールドにシークレットや完全な PII を含めない
- ロガーが対応している場合は構造化形式(フィールド)を使用する
- プロジェクトのライブラリと形式に準拠している
アンチパターン
| アンチパターン | より良い方法 |
|---|---|
| リクエストやレスポンスの全内容を記録する | メソッド、パス、ステータス、所要時間を記録し、本文やヘッダーは秘匿化するか省略する |
| サーバーコードで console.log を使う | プロジェクトのロガーをレベルと構造化フィールド付きで使用する |
| すべてをメッセージに埋め込む | 構造化フィールド(request_id、order_id など)を使用する |
| ループの各反復で info レベルのログを記録する | debug を使用するか、サンプリングする(例:N回ごと) |
本番環境の処理経路に残ったデバッグ用出力(print()、fmt.Println、console.log) | 削除するか、フィールド付きの logger.debug に変換する |
| 「後で秘匿化する」 | 最初から秘匿化するか、省略してください。シークレットを記録しないでください |
コメント
Markdown 対応