Androidでログ出力を行う際、実際には出力されないログレベルの場合でも複雑な文字列の作成をしてしまっていることがあります。そこで、文字列を遅延評価して不要な文字列作成コストを削減する方法を解説します。
ログ出力のパフォーマンス問題
以下のコードで複雑なデータ構造を文字列化しているとします。
.d("TAG", "現在の状態: ${getComplexState()}") Log
getComplexState()
関数が複雑な処理を行って文字列を生成する場合、ログ出力が行われなくても常にこの処理が実行されてしまいます。
遅延評価とは
遅延評価とは、処理が必要になるまでその実行を遅らせるというプログラミング方法のことです。Kotlinではlazy
デリゲートを使って簡単に遅延評価できます。
val message by lazy { "現在の状態: ${getComplexState()}" }
.d("TAG", message) Log
この場合、message
変数が実際に使われるまでgetComplexState()
関数の呼び出しと文字列の作成は行われません。つまり、ログが出力されない場合はgetComplexState()
関数は呼び出されず、無駄な処理をしなくて済みます。
遅延評価ログ関数を作る
遅延評価を簡単に行うためのログ関数を作成してみます。以下のコードはラムダ式を使って遅延評価を行う関数の例です。
inline fun logDebug(tag: String, message: () -> String) {
if (Log.isLoggable(tag, Log.DEBUG)) {
.d(tag, message())
Log}
}
この関数ではログレベルがDEBUG以上の場合にのみ文字列が評価されます。
文字列を遅延評価できるログライブラリ
既存のログライブラリの中には文字列を遅延評価できるものもあります。
Timber
Androidでは非常に有名なログライブラリです。
.d("現在の状態: %s", getComplexState()) Timber
この場合getComplexState()
自体は常に実行されてしまうため、getComplexState()
は文字列を作成するのではなく、データオブジェクトを返すだけにしたほうがいいです。文字列のフォーマットは必要な場合のみ行われます。
Timber: https://github.com/JakeWharton/timber
KmLogging
KmLoggingはKotlin Multiplatform対応の軽量なログライブラリです。こちらはラムダ式を使ってログメッセージを渡すことができます。
KmLogging: https://github.com/LighthouseGames/KmLogging
.debug { "現在の状態: ${getComplexState()}" } KmLogging
ラムダ式の中に複雑な処理を閉じ込めてしまえば必要以上に実行コストを使うことは無くなります。
Napier
NapierもKotlin Multiplatform対応のログライブラリです。文字列の遅延評価もサポートしています。
.d { "現在の状態: ${getComplexState()}" } Napier
Napier: https://github.com/LighthouseGames/KmLogging
まとめ
ログの文字列を遅延評価することでパフォーマンスの向上が期待できます。ついつい何も考えずにログに出力しがちですが、繰り返しが多い部分などは遅延評価を使うことによって本番コードの軽量化を図りましょう。