先日androidアプリのreleaseパッケージを作成して動作確認していたところ、

Log.d("Debug", hoge);

な 出力がreleaseパッケージでもされている事に気がつきました。

android のapiリファレンスには

Verbose should never be compiled into an application except during development. Debug logs are compiled in but stripped at runtime.

とかいてるので、Verboseはそもそも compile時に消し込まれ、Debugはrelease環境では表示されないと言っているように思うのですが、実際は表示されます。

まぁ ログぐらいいいんじゃ?とか思う方も居るかもしれませんが、Javaにおけるログ出力は結構なリソースを食います。
Javaにおける文字列は Stringオブジェクトであり、Stringオブジェクトの連結にはStringBuilderという別のクラスがnewされます。Javaでのnew 処理は意外と重い上に、Objectが増えればガーベッジコレクトの対象と機会が増えるので、結構馬鹿にできません。当然forループの中でのログ出力は あんまりよくないです。

リファレンスにも

that when you're building the string to pass into Log.d, the compiler uses a StringBuilder and at least three allocations occur: the StringBuilder itself, the buffer, and the String object. Realistically, there is also another buffer allocation and copy, and even more pressure on the gc. That means that if your log message is filtered out, you might be doing significant work and incurring significant overhead.

と 書かれています。(じゃあなんで消えないの!?って言いたくなりますが・・・)

いろいろ調べてみると、Logのメンバに isLoggable(String tag, Int level)というのがありました。

if ( Log.isLoggable(TAG, DEBUG) ) Log.d(TAG, "hogehoge");
と 書くと確かに思ったとうりの動きをする。
うーん、これってちょっと微妙じゃない? Log.dのdメソッドでlevelを表すわりには、そのまでDEBUGと明示しているあたりがダサイし、いちいちif書くのが面倒。

private static final boolean DEBUG = true;
if ( DEBUG ) Log.d(TAG, "hogehoge");

のように、ifの条件内をstatic finalな定数にしておくとcompile時に消し込んでくれるので無駄なif判定をしなくて済むというメリットがあるけれど、 Log.isLoggable(TAG, DEBUG)はcompile時に決しないので、見事releaseパッケージでも生き残ります。

じゃ あLog.isLoggableってのはどんな場合に意味をもつのかと言うと・・・ちゃんとandroidOSでサポートされているんですね。デフォルト では、

Verbose 非表示
Debug 非表示
Info 表示
Warn 表示
Error 表示
※あくまでisLoggableでの戻り値
AndroidのLogクラスを使っているメリットはこのログの表示設定をadbコマンドで簡単に切り替えられる点でしょうか。

$ adb shell stop
$ adb shell setprop log.tag.YourTag DEBUG
$ adb shell start
とするとタグ名さえわかっていれば、DebugレベルやVerboseレベルのログを出力できます。apkの入れ替えしなくても実機で簡単にチェックでき る点がメリットですが、タグ名がわかれば誰でもみれちゃうのがアレですね・・・
こうしてみるとタグ名はapk内で統一しておいた方が楽っぽいので すが・・・

もう一つadbコマンドにはログにまつわる便利(そうな)コマンドがあります。logcatでログを表示するさいにフィルタで きるんですね。
ログの出力形式はこんな感じでプリフィックスがついています。

I/ActivityManager( 585): Starting activity: Intent { action=android.intent.action...}
上記の例だとActivityManagerのタグがついたInfoレベルのログです。
コレをlogcatでフィルタする時は、
adb logcat ActivityManager:I MyApp:D *:S
こ んな感じで指定できます。上記の場合はActivityManagerのInfoレベル以上、MyAppのDebugレベル以上、Silentレベルすべ てが表示されます。(SilentはOSが出すハズの最高レベルのエラーログですが、一般的にはお目にかかる事はなさそうです)

ちょっと 便利そうな機能ではありますが、実はこれadbの説明ページよんでいて今知ったばかりなので実用性がどれぐらいあるかは不明です。

さて、 はじめのバグっぽい挙動以外は、いろいろ便利っぽいLogクラスですが、こういったパッケージ系のアプリの場合は、そもそもログを見る機会が限られた端末 になることが多いので、端末設定で動的にログのオンオフ出来る機能は微妙な気もします。

とくにandroidは簡単にパッケージのインストールができちゃ うので、都度static finalなログフラグを書き換えてもいいかなぁと思ったりします。
もちろん実際は面倒なのでsedで自動で書き換えてapk作成までのshellつくっ たりしちゃうと思いますけど。

まだandroidは始めたばかりなので、どちらが良いかやってみないと解らないのですが、次アプリつくる ときにはLogクラスは自作するつもりではいます。

JavaにはLog4jというすばらしいパッケージはありますが、あれは結構巨大なライブラリな ので、ちょっと躊躇してしまうんですよね。SMTP経由でLog送れるので、旨いことユーザー自信に送信してもらわなくて良いログ報告メール機能とか出来 たらすてきだとは思いますが・・・ちょっとできるか不明です。