続いて、アプリケーションのソースを確認する。すべてのソースコードに目を通す時間はないので、適当に2、3のクラスをピックアップして確認したところ、ログを出力するコードがリスト2のようにif文で囲まれていなかった。
リスト2 ログ出力部分のソース(抜粋) |
// start generating log message |
これでは、ログレベルを変更したとき、出力する必要のないログメッセージ生成処理が行われてしまう。ログレベルを変更してもパフォーマンスの低下を招いてしまう。
通常、パフォーマンスを考慮しリスト3のように、書き込み処理をif文で囲むことを推奨する。
リスト3 推奨されるログ出力方法 |
if (logger.isDebugEnabled){ |
さらに気になった点が、“logger.debug(log)”の記述しかなく、どうもログレベルがdebugレベルしかないと考えられた。
パフォーマンスを劣化させている原因をいくつか発見したので、開発者へ再度ヒアリングを行う。問題を引き起こしている部分をまとめると、以下である。
現段階で仮定である“ログレベルがdebugしかない”点を開発者に確認したところ、やはりdebugしかないとのこと。
これだけの原因があれば、付け焼き刃での対策ではパフォーマンスを改善できないと考え、以下の対策を施してもらうことにして、いったん引き揚げることにした。
結局、そのプロジェクトは提示した対策をすべて実施しログ設計まで手戻りを発生させることでトラブルを解決した。
今回はこのように、ログ出力にまつわるパフォーマンス・トラブルをハックした事例を紹介してきた。
読者の皆さんは、「たかがログでパフォーマンスがそこまで劣化するのか?」と思われるかもしれないが、筆者の経験上ログ出力方法が原因でパフォーマンス・トラブルが発生するシステムは数多く存在する。
そこで最後に、ログに関するパフォーマンスTipsを紹介する。
いま考えているログは何に取得するか、という目的を意識すると、ログ自体が必要かどうか判別しやすくなる。
ログ設計指針が重要視され過ぎで、セキュリティ対策の一環や迅速なトラブル対応のために、ありとあらゆるログを取得するといったプロジェクトをまれに目にする。しかしそうなると、逆にログが膨大な量になり、解析に時間がかかるうえ、運用が開始されると結局、重要なログメッセージのみを監視するということになりがちである。
ログ自体を出力しなければ、ログ出力がパフォーマンストラブルを引き起こすこともないだろう。
本当に必要なログが決まったら、出力するログの目的に応じたログレベルやログ出力方式を決定する。
Java EE開発で多く用いられるCommon Logging+Log4Jでは、軽微な情報出力から並べて、trace/debug/info/warn/error/fatalの6つのログレベルが存在する。ログレベルと出力内容は開発者が決定するものであるが、表1に各レベルに対応したログ出力例を示す。
ログレベル | 使用例 | |
---|---|---|
trace | ループ内部の変数などdebugの中でも大量に出力されるメッセージ。あまり使用しているプロジェクトはない | |
debug | セッション内部の情報やユーザーHTTPリクエスト、レスポンス情報など、開発中やテスト用のデバッグメッセージ | |
info | アプリケーションの起動情報やログインしたユーザーIDなど毎回出力させたいメッセージ | |
warn | 2重ログイン禁止のシステムで2重ログイン要求が発生したなど想定内の問題が発生したが、リクエストを処理し運用に問題ないメッセージ | |
error | DBサーバが無応答など、ユーザーからの処理を処理できない場合 | |
fatal | アプリケーションがこれ以上動作できない場合など。あまり使用しているプロジェクトはない | |
また、ログ出力方式も性能に大きな影響を与える。ログファイルをパッケージごとやログレベルごとに分散することで、ログファイルのロック開放待ちを抑制できる。
ログの使用目的を熟慮したうえで、ログ出力が必要なときはネットワーク経由での非同期ログ出力を検討する。
Log4Jで非同期でのログ出力をサポートするAsyncAppenderクラスは使用方法が特殊なだけでなく、パフォーマンスを低下させる可能性もあるため、採用する前にはテストを行うことを推奨する。
org.apache.log4j.AsyncAppenderクラス
ログレベル、ログ出力方式が決定したら、最後は実装時に行うTipsを解説する。
ログレベルをキチンと設計すると、ログ出力のための文字列連結などが不必要になるケースが多く出てくる。パフォーマンスを向上させるために、最大の効果が得られることは無駄な処理をしないことである。そのため、リスト3のようにログ出力の処理を削除するためにif文で囲む。
リスト3(再掲) 推奨されるログ出力方法 |
if (logger.isDebugEnabled){ |
ログ出力に関するパフォーマンス・トラブルは結合テストなどの後ろの工程で発覚することが多く、改修をするとアプリケーション全体に影響が及び、大きな手戻りになることも多い。そのため、ログ設計を行うときは「そのログ本当に必要ですか?」と自問自答しながら、熟慮してほしい。
佐藤 聖規(さとう まさのり)
NTTデータ 基盤システム事業本部所属。
入社よりWebシステムのパフォーマンステストやトラブルシューティングの社内支援業務を担当してきた。現在は、開発生産性を向上させるツール開発に従事するかたわら、トラブルシューティングする機会をうかがっている。
Copyright © ITmedia, Inc. All Rights Reserved.