ログ
ログは、アプリケーションやアプライアンスから送信される一連のメッセージで、イベントに関する詳細や、時にはアプリケーションの健全性について、1 行以上の詳細情報で表されます。 通常、ログはファイルに出力されますが、分析や集計を行うコレクターに送信される場合もあります。 1 日あたり数メガバイトから 1 時間あたり数テラバイトまで、あらゆる量のログデータの生成、取り込み、管理を容易にすることを目的とした、多くの機能を備えたログアグリゲーター、フレームワーク、製品が存在します。
ログは一度に 1 つのアプリケーションから出力され、通常は 1 つのアプリケーション の スコープに関連しています。ただし、開発者は必要に応じて、複雑で細かなログを自由に設定できます。 ここでは、ログは トレース とは根本的に異なるシグナルと考えています。トレースは、複数のアプリケーションやサービスからのイベントで構成され、レスポンスレイテンシー、サービス障害、リクエストパラメータなど、サービス間の接続に関するコンテキストを含みます。
ログのデータは、一定期間にわたって集計することもできます。 例えば、統計的なデータ (前の 1 分間に処理されたリクエスト数など) を含めることができます。 構造化されたデータ、自由形式のデータ、詳細なデータ、あらゆる言語で記述できます。
ログの主な使用例は、以下を説明することです。
- イベントの状態、期間、その他の重要な統計情報
- そのイベントに関連するエラーや警告 (スタックトレース、タイムアウトなど)
- アプリケーションの起動、開始、シャットダウンメッセージ
ログは イミュータブル(不変性) であることを意図しており、多くのログ管理システムには、ログデータの改ざんを防止し、検出するメカニズムが含まれています。
ログに関する要件に関係なく、以下は私たちが特定したベストプラクティスです。
構造化ログは成功の鍵
多くのシステムは、半構造化された形式でログを出力します。例えば、Apache Web サーバーは、1 行が 1 つの Web リクエストに対応する以下のようなログを出力します:
192.168.2.20 - - [28/Jul/2006:10:27:10 -0300] "GET /cgi-bin/try/ HTTP/1.0" 200 3395 127.0.0.1 - - [28/Jul/2006:10:22:04 -0300] "GET / HTTP/1.0" 200 2216
一方、Java のスタックトレースは、複数行にまたがる 1 つのイベントで、より構造化されていない形式です:
Exception in thread "main" java.lang.NullPointerException at com.example.myproject.Book.getTitle(Book.java:16) at com.example.myproject.Author.getBookTitles(Author.java:25) at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
また、Python のエラーログイベントは以下のように表示されます:
Traceback (most recent call last):
File "e.py", line 7, in <module>
raise TypeError("Again !?!")
TypeError: Again !?!
これら 3 つの例のうち、最初の例だけが人間 と ログ集約システムの両方で簡単に解析できます。構造化ログを使用することで、ログデータを迅速かつ効果的に処理でき、人間とマシンの両方が必要な情報をすぐに見つけることができます。
最も一般的に理解されているログ形式は JSON で、イベントの各コンポーネントがキーと値のペアとして表現されます。JSON では、上記の Python の例は以下のように書き換えることができます:
{
"level", "ERROR"
"file": "e.py",
"line": 7,
"error": "TypeError(\"Again !?!\")"
}
構造化ログを使用することで、データを異なるログシステム間で移行しやすくなり、開発が簡素化され、運用上の診断が迅速に(かつエラーが少なく)行えるようになります。また、JSON を使用することで、実際のデータと共にログメッセージのスキーマが埋め込まれ、高度なログ分析システムがメッセージを自動的にインデックス化できるようになります。
ログレベルを適切に使用する
ログには、レベル を持つものとイベントのシリーズの 2 種類があります。レベルを持つログは、成功的なロギング戦略の重要なコンポーネントです。ログレベルはフレームワークによって若干異なりますが、一般的に以下のような構造になっています:
レベル | 説明 |
---|---|
DEBUG | アプリケーションのデバッグに最も役立つ、きめ細かな情報イベントです。これらは通常、開発者にとって価値があり、非常に詳細です。 |
INFO | アプリケーションの進行状況を粗いレベルで示す情報メッセージです。 |
WARN | アプリケーションにリスクを示す、潜在的に有害な状況です。これらはアプリケーションでアラームを発生させる可能性があります。 |
ERROR | アプリケーションの実行は継続できる ものの、エラーが発生しているイベントです。これらは注意が必要なアラームを発生させる可能性が高いです。 |
FATAL | アプリケーションの中断を引き起こす可能性がある、非常に深刻なエラーイベントです。 |
明示的なレベルを持たないログは暗黙的に INFO
とみなされますが、この動作はアプリケーションによって異なる場合があります。
その他の一般的なログレベルには、ニーズ、プログラミング言語、フレームワークに応じて CRITICAL
や NONE
があります。ALL
と NONE
も一般的ですが、すべてのアプリケーションスタックで見られるわけではありません。
ログレベルは、環境の健全性についてモニタリングとオブザーバビリティソリューションに知らせるために重要であり、ログデータは論理的な値を使用してこのデータを容易に表現する必要があります。
WARN
レベルで多くのデータをログに記録すると、価値の限られたデータでモニタリングシステムが満杯になり、大量のメッセージの中で重要なデータが失われる可能性があります。
標準化されたログレベル戦略を使用することで、自動化が容易になり、開発者が問題の根本原因をすばやく特定できるようになります。
ログレベルに対する標準的なアプ ローチがないと、ログのフィルタリング が大きな課題となります。
ソースに近い場所でログをフィルタリング
可能な限り、ソースに近い場所でログの量を削減してください。このベストプラクティスには以下のような理由があります:
- ログの取り込みには常に時間、コスト、リソースがかかります。
- 機密データ(個人を特定できるデータなど)を下流のシステムでフィルタリングすることで、データ漏洩のリスクを軽減できます。
- 下流のシステムは、データソースと同じ運用上の懸念事項を持っているとは限りません。例えば、アプリケーションの
INFO
ログは、CRITCAL
やFATAL
メッセージを監視するモニタリングおよびアラートシステムにとって重要ではない場合があります。 - ログシステムやネットワークに過度の負荷やトラフィックをかける必要はありません。
コス トを抑え、データ露出のリスクを減らし、各コンポーネントを重要な事項に集中させるため、ソースに近い場所でログをフィルタリングしてください。
アーキテクチャによっては、Infrastructure as Code (IaC) を使用してアプリケーションと環境の変更を一度の操作でデプロイすることをお勧めします。このアプローチでは、ログフィルターパターンをアプリケーションと共にデプロイでき、同じ厳密さと扱いを適用できます。
二重取り込みのアンチパターンを避ける
管理者がよく採用するパターンとして、1 つの場所からすべてのログを照会することを目的に、すべてのログデータを単一のシステムにコピ ーするというものがあります。 これには手作業のワークフローにおいて利点がありますが、このパターンは追加のコスト、複雑さ、障害ポイント、運用オーバーヘッドを引き起こします。
ログからメトリクスデータを収集する
ログには収集を待っているメトリクスが含まれています!ISV ソリューションや自分で作成していないアプリケーションでさえ、ワークロードの全体的な健全性について意味のある洞察を得られる貴重なデータをログに出力します。一般的な例として以下のようなものがあります:
- データベースからの低速クエリ時間
- Web サーバーのアップタイム
- トランザクション処理時間
- 時間経過による
ERROR
やWARNING
イベントの数 - アップグレード可能なパッケージの実数
静的なログファイルにロックされているデータは、あまり有用ではありません。ベストプラクティスは、重要なメトリクスデータを特定し、他のシグナルと相関付けができるメトリクスシステムに公開することです。
stdout へのログ出力
可能な限り、アプリケーションはファイルやソケットなどの固定された場所ではなく、stdout
にログを出力するべきです。これにより、ログエージェントはオブザーバビリティソリューションに適したルールに基づいてログイベントを収集およびルーティングできます。すべてのアプリケーションで実現できるわけではありませんが、これはコンテナ化されたワークロードのベストプラクティスです。
アプリケーションはログ記録の実践においてシンプルで汎用的であり、ログソリューションとの結合を緩やかに保つべきですが、ログデータの送信には依然として stdout
からファイルにデータを送信するログコレクターが必要です。重要な概念は、アプリケーションとビジネスロジックがログインフラストラクチャに依存しないようにすることです。つまり、関心の分離を実現するように努めるべきです。
アプリケーションをログ管理から切り離すことで、コードの変更なしにソリューションを適応・進化させることができ、環境に加えられた変更の Blast Radius を最小限に抑えることができます。