「アプリケーションログ、 どう出力する?どう調査する?」JAWS FESTA 2023 KYUSHU 発表内容書き起こし

はじめに

インフラ/バックエンド担当の森井です。 先日、JAWS FESTA 2023 KYUSHUのメイントラックで登壇する機会をいただきました。 人生初カンファレンス登壇ということで大変緊張しましたが、なんとか持ち時間通りに発表を終えることができました。

スライドは公開していますが、せっかくですのでセルフ書き起こし記事を残しておこうと思います。

speakerdeck.com

※ 用意してあったスピーカーノートから書き起こしているため、実際に会場で話した内容とは少々異なります。

jft2023.jaws-ug.jp

アプリケーションログ、 どう出力する?どう調査する?

「2020年から、フェンリル株式会社というところでエンジニアをやっております。マイブームはパン作りで、暇な土日にパン生地をこねてます。今日の懇親会参加しますので、もしおすすめの小麦粉とか教えて欲しいです。」

「どうでしょう。ちょっと誇張しているのでここまで酷くはないかもしれませんが、似たような経験にこころあたりがある方はいらっしゃいますか?」

(チラホラと手があがる)

「やはりいらっしゃいますよね。ありがとうございます。こんなとき、どんな問題がおきるでしょうか。」

「大きく2つの問題がおきてきます。一つは調査に時間がかかってしまったり、不具合が解消できないことでユーザー満足度が落ちてしまうことです。これはビジネス的な問題です。 もう一つはシニアエンジニアの勘と経験頼りになってしまうことです。プロジェクト体制としても不健全な状態になあってしまいます。 この状況、端的にいうとオブザーバビリティが低い状態になってしまっていると言えると思います」

「オブザーバビリティは「外部の出力から、内部の状態をどれだけ推測できるかを示す尺度」です。 もともとは工学の分野から出てきた言葉のようですが、近年はシステムが分散し複雑になっていることから、オブザーバビリティが重要だと言われています。尺度といいつつ、定量化することはあまりないようですが、システム設計の考慮ポイントとして取り入れていくことは有意義でしょう。

個人的には、オブザーバビリティという言葉は濁点が多くて強そうな感じがするし、小難しい、自分とは関係がないようなイメージがありました。

確かに、マイクロサービスやPaaSなど、複雑なシステムでは高度なオブザーバビリティの仕組みが必要になることもあるかもしれませんが、一般的なアプリケーションでは既存のログの仕組みやAWSの公式サービスを上手く使うことで、かなり改善することができます。

このセッションではオブザーバビリティを高めるために、ログに注目した改善策をご紹介します。 」

「ログレベルに関しては、ご存じの方も多いのではないでしょうか。 ログレベルはログの重大度を示す指標です。 DebugレベルからEmergencyレベルまで8段階あります。 各ログレベルに対しどのような行動をとるのか、環境ごとにどのレベル以上のログを出力をするのかを決めておくことが重要です。」

「次に、非構造化ログから構造化イベントログに変えていきましょう。」

(引用箇所を読み上げる)

「要約すると、検索しやすいようにするためにJSON形式で構造化しましょう。断片的なメッセージの連続ではなく、全ての記録を残すようにしましょう、ということだと思います。

この会場はAWS有識者の方が多いと思いますので、CloudTrailのイベントログがイメージしやすいと思います。

何か問題があった時にCloudTrailのイベントを調べにいくと思いますが、該当のログさえ見つければ、必要な情報は不足なく揃っているなと感じた経験をお持ちの方、多いと思います。あのようなログを、私たちの作るアプリケーションでも取り入れようという話です。」

「では、構造化ログはどのように実現できるでしょうか。 PHPでは、MonologのJsonFormatterを導入するだけで対応可能です。 Laravelなら1行書き換えるだけです。簡単です。」

「次は、構造化イベントに追加する情報を決めましょう」

(引用箇所を読み上げる)

「要約すると、ユニークな値のデータを記録しましょう。多数のキーを追加し、様々な観点で分析できるようにしましょう。 ということだと思います。」

「では、どのような情報を追加するのが良いでしょうか。 基本的なスタンスとしては、可能な限り全てのデータを記録したい。 ただし、パフォーマンス、コスト、セキュリティの観点から、特に必要と思われるデータに絞り込んでいきましょう。 一例を下部に記載しましたが、HTTPボディなどデータが大きくなりがちなデータはサマリだけ表示するなどの工夫が必要だと思います。」

「次に、トレース情報を追加しましょう。 クラウドで動かすシステムは、複数のサービスで連携してリクエストを処理します。 各サービスのログを紐づけるために、リクストごとに一意のIDであるトレースIDを記録するのがよいでしょう。 トレースIDがない場合は、時刻情報で見当をつけるしかなくなってしまいます。これでは困りますね。」

「AWSはよくできてるので、自動でトレースIDつけてくれてます。 ALBの場合はX-Amzn-Trace-Idというヘッダをつけてくれますので、これをNginxやアプリケーションサーバーに引き回すと良いでしょう。」

「イメージとしてはこんな具合です。 ALBで発行したトレースIDがWAFのログでもrequestIdとして記録されています。 これをアプリケーションの構造化イベントに追加すれば、複数箇所で出力されたろぐを紐づけることができます。」

「構造化イベントログ出力の流れはこんな形になっています。 Monologでは、withContextメソッドを呼ぶことで後続の全てのログエントリに含めるコンテキスト情報を指定することができます。」

「最終的にはリクエストごとにこんな形のログが出力される形になります。 複雑な処理であれば、もっと多くの行のログが出力されることになると思います。」

「ここまでで、非構造化ログを構造化イベントログに移行し、必要な情報が検索しやすい状態に整えることができました。 では、実際ログをどのように保管し、検索することができるでしょうか? サードパーティ製品も含めると多くの選択肢がありますが、AWSサービスをベースとした代表的な3パターンをご紹介します。」

「時間がなくなってきました。まずは Amazon Athenaです。 Athenaは、S3上のファイルに標準SQLを実行できます。 サーバーレスサービスのためインフラストラクチャの管理が不要です。」

「二つ目は、CloudWatchLogsです。 3つの中で、唯一ログ専用サービスになります。 クエリ言語を使用して分析が可能な上、UI上で表示したり、全文検索したりと使い勝手が良いです。 メトリクスフィルターを使用すると、特定の文字列が表示された際にアラームを飛ばすこともできるので、運用上のメリットも大きいです。」

「3つめは Amazon OpenSearch Service です。 OpenSearchクラスターの管理をしてくれるマネージドサービスです。 いわゆる全文検索エンジンですね。 SQLを使用した分析も可能ですし、UI上でも表示ができます。」

「機能以上に大きく違うのが料金体系です。 Athena+S3は、S3保存費用とAthenaデータスキャン量に応じて課金されます。格安です。 CloudWatch Logs は、データの収集、保存、分析それぞれ課金されます。データ量が少なければ安いですが、多くなってくるとバカにならない金額になります。 OpenSeach Serviceはインスタンス費用とEBS費用がかかります。固定で一定の料金がかかり続けるようなイメージになります。」

「アプリケーションログの文脈では、GUIで表示できて手軽に使えるCloudWatch Logsがおすすめです。 よりランニングコストを抑えたい場合はAthenaを検討すると良いでしょう。 CloudWatch Logsのパフォーマンスが物足りない場合や、ログの流量が多くデータ取り込み費用を抑えたい場合、OpenSearch特有の機能を使用したい場合はOpenSearch Serviceを検討してみてはいかがでしょうか。」

「まとめます。ログレベルを適切に設計しましょう。構造化イベントログを出力しましょう。構造化イベントログにはトレース情報も追加しましょう。ログ収集/検索サービスは、まずCloudWatch Logsを検討しましょう。ランニングコストが高い場合はAthenaを検討しましょう。パフォーマンスが物足りない場合はOpenSearch Serviceを検討しましょう。」

「皆様、よきオブザーバビリティライフを。ということで、ご清聴ありがとうございました。」