ログは開発・運用するアプリケーションやサービスを理解するために欠かせないものですが、ログを効果的に活用するには、大量のログデータをデータベースやファイルに収集するだけでは不十分です。

適切に構造化されたログは、複雑なシステムを理解するのに役に立ち、そのパフォーマンスを向上させる方法を発見しあるいはエラーに関する重要な洞察を提供することができます。
スタック全体のログを設定するのは大変なことでうまくいかないと盲点となることがあります。
何をログに記録するか、どの程度詳細に記録するか。また、データが多すぎるとコストが高くなるかを決定する必要があります。

本記事ではスタックを可視化し、エラーのトラブルシューティングを迅速に行い、ユーザーの体験をよりよく理解するのに役立つログのベストプラクティスをいくつか紹介します。

ログを取る対象を決定する

ログは、ソフトウェアアプリケーション、オペレーションシステム、およびインフラストラクチャに関するイベントデータを保存するファイルです。ログは、標準出力やファイルにテキストを書き込むことで生成され、タイムスタンプ、エラーメッセージ、スタックトレースなど、いつ、なぜ、何が起こったかを示すデータポイントを含むことができます。

ログを最大限活用するためには、ログに記録する内容を慎重に選択する必要があります。調査時に事象や根本原因を特定するために必要なメタデータを全て含める必要があります。

ログに含まれる情報は、目的があって、すぐに役に立つものでなければいけません。使用データ、ユーザーイベント、アプリケーションのエラーや例外など、チームとって価値のあるデータであるべきです。また、ログに保存された情報は、問題を理解し、意思決定を行うために必要な情報を提供する必要があります。

一般的なロギングシナリオを想定した計画

ログはバグのトラブルシューティングに役立つだけでなく、パフォーマンスプロファイリングや統計情報の収集など、スタックをより理解するのに役立ちます。

ログを構成する際には、どのデータ・ポイントが価値をもたらすのかを決定するのに役立つ、一般的なシナリオを覚えておいてください。例えば、アプリケーションの詳細なログは、パフォーマンスとメモリリークなどの潜在的な問題についての洞察を提供するかもしれません。ユーザーのインタラクションに関する他のデータと比較して、ログはユーザーエクスペリエンスに関する重要な洞察を与えることができます。

これらのことは全て、意思決定をする際に重要になる可能性があり、シナリオに応じて変化することも忘れないでください。

意思決定を促す意味のあるメッセージを記録

ログメッセージは、それが提供する情報が価値あるものであり、人々の意思決定に役立つ場合にのみ有用です。サードパーティーのインフラストラクチャは、詳細な情報を取得する傾向にありますが、ソフトウェアアプリケーションの場合は、エラーやイベントが発生した理由を診断するのに役立つ情報を決定し、必要なアクションを取ることができるようにする必要があります。

ログメッセージは、アプリケーションエラーの場合、コードの一行で何が起こっているかを伝える必要があります。

例えば、トランザクションが失敗した場合、ログメッセージは次のようになります。

Transaction Failed: Could not create user ${path/to/file:line-number}

このメッセージにより、トランザクションが失敗した理由や、どのコードの行を見直すべきか容易に発見することができます。

エラーコードのテキストまたは番号を出力するだけでなく、ログに短い説明を追加すると、トラブルシューティングの際にチームメイトの時間を節約することができます。

ログメッセージはシンプルで簡潔なものにする 

ログメッセージに十分な情報を含めることは重要ですが、過剰にならないように注意してください。

不要なデータは、ストレージの容量を増やし、ログ検索を遅くし、問題のデバックを困難にします。
ログメッセージは有用で簡潔なものでなければならず、最も重要なデータのみを収集するようにしなければなりません。

ログをフォーマットする際には、環境に関するあらゆる詳細を含めずに、エラーのデバックに必要な情報を収集するようにします。例えば、あるAPIが失敗した場合、その API からのエラーメッセージを記録しておくとよいでしょう。しかし、アプリケーションのメモリに関する詳細は省略することを検討してください。

タイムスタンプを忘れずに

全てのログメッセージにタイムスタンプを含めることを忘れないでください。タスクの頻度に基づいて、追跡する時間を選択しますが、全てのログにタイムスタンプを含めるようにします。タスクによっては、ミリ秒単位まで時間を追跡する必要もあれば、分時、日単位まで追跡すれば良い場合もあります。

ベストプラクティスとして、スタックまたは組織全体に標準を適用して、ログをメトリクスやイベントなどの他の遠隔測定データタイプと関連づけます。

わかりやすいロギングフォーマットを使用する

ログメッセージは読みやすく、アクションや洞察を促進する重要な情報を提供する必要があります。
経験則上、限られたメンバーしか理解できないような曖昧なメッセージや非記述的なメッセージは避けるべきです。ログの形式は解析が容易で、一貫したログ構造を維持する必要があります。これによりログの収集と集計が容易になります。例えば  New Relic log management などのツールではカスタムのログ解析ルールを簡単に定義できますが、ログデータが読み取れないと解析ルールが機能しません。

Log format examples

### Example of an NGINX access log that is difficult to parse:
127.180.71.3 - - [10/May/2022:08:05:32 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)"


### Example of the same log information in a parsable log format:
{
  "remote_addr":"93.180.71.3",
  "time":"1586514731",
  "method":"GET",
  "path":"/downloads/product_1",
  "version":"HTTP/1.1",
}

この2つのログは非常によく似た情報を示していますが、最初のものはパーツが明確に区分けされていないため、読みにくいです。一方2つ目の例では、ログ全体を読まなくても、必要な情報を簡単に探し出すことができます。全てのアプリケーションでログ形式を標準化することは、ログの解析、収集、他のプラットフォームでの活用が容易になることも意味します。

このように、正しい情報でログを取得・構築することは、コスト削減、エラーの迅速な診断、さらには待機中の時間短縮につながります。