特別な理由がない限り、標準出力または標準エラー出力に出力する。
使う側がパイプやリダイレクトなどをして、出力先を柔軟に変更できるため。
例
- Dockerや Amazon CloudWatch の標準のログドライバーなどは標準出力を読み込む
- syslogで管理したいときは
logger
コマンドにパイプする - 開発中はそのままターミナルに表示したり、ファイルにリダイレクトしたりする
参考
ログレベル | 説明 | 例 |
---|---|---|
FATAL | システム全体に影響する例外(異常系) | システムの異常終了 |
ERROR | 処理を継続できない例外(異常系) | 外部システムとの通信失敗、予期しないエラー |
WARN | 処理を継続できる例外(準正常系) | 廃止されたAPIの利用、バリデーションエラー、権限不足 |
INFO | 重要なイベントや情報 | アカウントの操作、金銭の操作、システムの設定・環境・バージョン |
DEBUG | デバッグの目的で使用する情報 | リクエスト・レスポンスの値、関数の開始・終了、発行したSQL |
TRACE | DEBUGレベルよりさらに詳細な情報 | 変数の値、関数の各ステップ |
本番環境ではINFO以上、開発環境ではDEBUG以上、開発者のローカル環境ではTRACE以上を出力する。
特別な理由がない限り、各行をJSONにする。(JSONL)
構造化することでログを読み込むツールがパースしやすくなり、特定の値でフィルタしたり抽出したりしやすくなるため。
JSONはサポートしているツールが多い。
なお、複数行にまたがるログはパースしにくいのでNG。
特にスタックトレースは複数行になることが多く、1行で出力する方法は言語によって異なる。
- タイムスタンプ
- ログレベル
- トレースID
- 関連エンティティのID
- ユーザIDや更新対象のIDなど
- 関数名とその名前空間
- メッセージ
メッセージは自由だが、以下に注意する。
- NginxなどのWebサーバ側で出力している情報は冗長なので出力しない
- 流出時に悪用される可能性があるため、機密情報や認証情報などは以下のいずれかの対応をする
- 出力しない
- マスクして出力する
- たとえばJSONなどで構造化されている情報のうち、一部の値だけが機密情報にあたる場合など
- ハッシュ化して出力する
- たとえば法務観点や契約事項のためにログに残す必要がある場合など
トレースIDは一連の処理で出力したログを紐づけるためのID。
Web系だとリクエストIDと呼ぶことも多い。
以下のようにするとシステムをまたいでトレースできるようになる。
- リクエスト元がIDを発行
- リクエストヘッダの
X-Request-ID
などに設定してリクエスト - リクエスト先のログに設定されたIDを含める
Nginxであれば $request_id
という変数でリクエストごとにIDを発行できる。
https://github.com/Foo-x/snippets/blob/master/nginx/reqid.conf
上のリンク先のように設定すると以下の挙動になる。
- Nginxへのリクエストヘッダに
X-Request-ID
が含まれていれば、転送先へのリクエストヘッダにX-Request-ID
を同じIDで設定して送る - Nginxへのリクエストヘッダに
X-Request-ID
が含まれていなければ、IDを発行し、転送先へのリクエストヘッダにX-Request-ID
をそのIDで設定して送る - 転送先に送ったIDをアクセスログに含める