どの会社でもログやアラートのガイドラインというのを書いているなぁという感じなので、もうここでまとめて参照させたい、という意図で公開してみる。
ログ
ログレベル
基本的には以下の4つで利用することを推奨。
| レベル | 説明 | 
|---|---|
| DEBUG | 開発用。 デバッグは各種デバッガーでやることが多いのであまり出番はないかも。 | 
| INFO | どこまで正常に終了したかを確認したり、アクセスログなど簡易的に追いたい指標として利用。 処理が長くなりがちなバッチ処理で利用することが多い。 有用ではないログが大量に出る感じでなければ手軽に使ってOK。 | 
| WARN | 即時対応が必要ではないエラーのログに利用。 例えば、単位時間あたりに閾値以上の回数エラーが発生すると異常とみなすようなときなど。 | 
| ERROR | 即時対応が必要なエラーの場合に証跡を残すために利用。 別途SentryやDatadogなどエラー管理ツールに送信するとベター。ログと連動させて自動で送信しても良いが、意図せず通知してしまうことも多いので運用上注意が必要。 | 
MonoLogの場合は他にもNOTICE ALERT CRITICAL EMERGENCY のログレベルがあるが、使い分けが難しく運用しきれない可能性が高いため、まずは上記の4つで運用する。細かく分けないと厳しいケースが出てきたら改めて検討する。
ログの出力内容
- [MUST] 個人情報を載せないように注意する- アクセスログにはクエリパラメータも載るのでURL設計にも注意する(例えば ?name=山田太郎のようにクエリパラメータに個人情報が載らないようにする)
 
- アクセスログにはクエリパラメータも載るのでURL設計にも注意する(例えば 
- [MUST] バッチ処理など長めの処理は障害発生時に「どこまで成功したのか」がわかりづらいため、INFOログを適度に残しておく。
- [SHOULD] ログ出力のコスト(CPU/IOや転送料金など)が増えたり、本質的なログが見えづらくなることもあるので、Webアプリケーションなどログが増えそうな箇所には不要なログはできるだけ載せない。
- [SHOULD] アプリケーションログを出力した場所(クラス、ファイル名、行番号)も入れておくと障害時に追いやすい
- [SHOULD] Exceptionのエラーログはスタックトレースも残す- スタックトレースに引数の値が載らないように設定する。PHPの場合は以下のような感じでiniを設定する。
 
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
- [SHOULD] アプリケーションログにはトレースIDを入れておくと1リクエスト単位でまとめてログを確認できるので追いやすいので推奨- 分散システムの場合は必須。
- DatadogやNewRelicが自動でつけてくれたりする。
 
ログフォーマット
外部ツール連携性が高いので構造化ログ(JSONL)にする。
Laravelログの例
{
    "message": "some message",
    "context": {},
    "level": 200,
    "level_name": "INFO",
    "channel": "production",
    "datetime": "2024-11-26T16:08:19.585601+09:00",
    "extra": {
        "platform": "laravel",
        "url": "/home",
        "ip": "x.x.x.x",
        "http_method": "GET",
        "server": "example.com",
        "referrer": "https://example.com/referrer",
        "file": "/var/www/html/app/Models/Hoge.php",
        "line": 123,
        "class": "App\\Models\\Hoge",
        "callType": "->",
        "function": "fuga",
        "uid": "8a1d991"
    }
}
| 属性 | 説明 | 必須 | 
|---|---|---|
| message | メッセージ | ◯ | 
| context | コンテキスト(Laravel) | ◯ | 
| level | レベル(数字) | ◯ | 
| level_name | レベル(文字列) | ◯ | 
| channel | チャンネル | ◯ | 
| datetime | ログ出力日時 | ◯ | 
| extra.url | リクエストURL | - | 
| extra.ip | IPアドレス | - | 
| extra.http_method | HTTPメソッド | - | 
| extra.referrer | リファラ | - | 
| extra.file | ログ出力したファイル | - | 
| extra.line | ログ出力した行番号 | - | 
| extra.class | ログ出力したクラス名 | - | 
| extra.function | ログ出力したメソッド名 | - | 
| extra.uid | トレースID | ◯ | 
ログ出力場所
コンテナ化している場合はCloudWatch Logsに連携する。アプリケーションでは標準出力に出せばOK。
アラート
どんなアラートを出すか
「どこ」で「何」 が発生したか。次に行うアクションはなにか? を書くと良い。
特にアクションは大事で、内容はwikiに書いてそのURLを記載するだけでも良い。
アラートの意味を早く正確に把握できるように、とにかく認知負荷を下げるようなアラート文言を入れておくと良い。
どこに通知するか
Slackなどのチームで見れるようなChatツールに通知しておくと良い。メールだと気づかない…。通知するときは必要に応じてメンションできると良い。
何を使って通知するか
Slackであれば
- SlackのAPI(incoming webhookなど)を直接叩く
- Sentryなどのエラー管理ツール経由
といった選択肢がありそう。
Sentryは意図しないエラー(想定外のException)が発生したときに送信しておくと良い。意図しないエラー内容を確認し、不具合であれば修正するし、WARNINGレベルのエラーなどスルーしても良い内容であれば、そのように修正してSentryに通知されなようにするので、エラー管理ツールとして機能する。
一方で、想定内のエラーで一部手動のオペレーションが必要なのでSlack通知する、といったものはSentry管理にしないほうが良く、SlackのAPIを直接叩くほうが良いだろう。Sentryの設定によっては2回目以降の同じエラーは通知されなかったり、そもそもエラーというより「手動オペレーションが必要」というただの通知なので性質が違う。
オオカミ少年アラートに注意する
アラートが来たときに、ちゃんと精査してスルーしてOKなのか(INFO)、連続して発生していたらヤバいのか(WARNING)、不具合で修正が必要なのか(ERROR)を把握し、対応する必要がある。
もし、INFOレベルなのに通知されていたらオオカミ少年アラート化し、チームの認知負荷が上がる。オンボーディングも「このエラーは無視して良いですよ」みたいな意味不明な暗黙的な運用が発生してしまう。
また、大量のエラーに埋もれてしまって、新規のエラーを見逃してしまう可能性もある。