不具合調査が楽しくなる!? 1msにこだわったデバッグログライブラリ『Footprint』


はじめに

Androidの不具合調査って大変ですよね。
デバッグログを仕込んで、到達を確認したり、変数の値を確認したり。
そんな不具合調査に使うLog.d(String tag, String msg)が使いづらいと思ったので、『Footprint』というデバッグログライブラリを作成した、と言う話です。
ちなみに「1msにこだわった」とは、開発者がデバッグログを仕込む時間を減らすという意味です。
早速始めていきます。

Log.dの問題点

嫌いな(使いづらい)ところを上げてみます。
たくさんあります。

  • TAGを毎回作るのが面倒
  • TAGを毎回引数に入れるのが面倒
  • Stringしか入らない
    • プリミティブ型を入れるときにはString.valueOf()をしなきゃいけない
  • 複数の変数を入れたいときにはいちいち「+」で文字列連結しなきゃいけない
  • Logって打ちづらい。Loが特に。薬指と中指が縦に並べづらい
    • LiveTemplateでlogdってあるけど結局タグ入れなきゃいけない
  • たくさん仕込むと吐かれたときにどこに仕込んだのか分からないから、どこで仕込んだのかもメッセージに入れなきゃならない

上記の中で一つでも共感できるものがある人は、Footprintを使うと幸せになれます。(きっと)

Footprint

Footprintが目指したのは、開発者にとって使いやすく、使っていて気持ち良いデバッグログライブラリです。
そのために、メソッドやその名、引数の取り方などを工夫しています。
gradleに記述するだけで使えます。
こちらで公開しています。(リンクに飛ぶ前に是非下も読んでもらえると嬉しいです!)
https://github.com/morayl/Footprint

基本仕様

Footprint.leave();

と打つだけで、あなたは幸せになれます。
具体的に言うと、上記を打つだけで、それを仕込んだ場所に応じて、

[クラス名#メソッド名:行番号]

がlogcatに出力されます。

  • TAGはFootprintというタグで出力されるため考えなくて良い
  • Footprint.leave()するだけで上の3要素が出るので、どこに仕込んだのか記載する必要がない

つまり、疎通確認だけなら、Footprint.leave();を1行コピーして貼り付けまくれば簡単にデバッグ出来るのです。
AndroidStudioなら、Ctrl+Cで行コピーして、Ctrl+Vしまくるだけ。
ね、簡単でしょう?

使いやすさにこだわったleave()

Footprintはいかにストレスなくログを仕込めるかを目指しています。
leave()は何でも受け入れます。

// そのまま打ってよし
Footprint.leave();// [ExampleActivity#onCreate:25]

// メッセージを入れてもよし
Footprint.leave("You can leave message.");
// [ExampleActivity#onCreate:29] You can leave message.

// 引数はObjectなので、intでもlongでもbooleanでもそのまま突っ込めばOK
Footprint.leave(100);// [ExampleActivity#onCreate:29] 100

// 可変長引数なので好きなだけ入れてOK(Log.dみたいに「+」で繋ぐ必要なし)
Footprint.leave("You can leave multiple params like", this.getClass(), 5, false, "test");
// [ExampleActivity#onCreate:33] You can leave multiple params like  class com.morayl.footprintexample.ExampleActivity 5 false test

様々な利用シーンに対応した豊富なメソッド

ログで吐きたい内容は人それぞれ。
Footprintは多くのログ出力に対応します。

一言メソッド紹介
// ★データクラスの全値を見やすく出したいですよね
Footprint.json(dataClass);// (json形式で引数のものを出力します)
/*
[ExampleActivity#onClick:107]
 {
   "value3": 5,
   "value4": true,
   "value1": "test",
   "value2": "test2"
 }
*/

// ★Toastでログを出したいことってありますよね
Footprint.toast(context, "message");

// ★ただToastってすぐ消えちゃってデバッグにならないことってありますよね
Footprint.toast(context, "message", 5000);// 約5秒間トーストを表示

// ★logcatに出力できない長いデータを表示したいことありますよね
Footprint.dialog(context, dataClass);// ダイアログでjson化されたdataClassを表示します

// ★ExceptionのstackTraceをlogcatで見たいときってありますよね
Footprint.stackTrace(t);
/*
[ExampleActivity#onClick:75] java.lang.NullPointerException
at com.morayl.footprintexample.ExampleActivity.onClick(ExampleActivity.java:73)
at android.view.View.performClick(View.java:4307)
at android.view.View$PerformClick.run(View.java:17507)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5159)
at java.lang.reflect.Method.invokeNative(Native Method)
*/

// ★アプリを見ていないときでも画面にログを吐きたいことありますよね
Footprint.notify(ExampleActivity.this, "メッセージ");
// 通知のBitTextStyleで、出力時刻(ミリ秒まで)とクラスメソッド名行番号、メッセージを表示します

// ★複数のパラメータを見やすく出したいときありますよね
boolean isCorrect = true;
boolean wasCorrect = false;
Footprint.keyAndValues("isCorrect", isCorrect, "wasCorrect", wasCorrect);
/*
 [ExampleActivity#onClick:133]
  ・isCorrect : true
  ・wasCorrect : false
 */

// ★クラスのフィールドを見たいときもある・・・かもしれません
Footprint.fields(this);
/*
 [ExampleActivity#onClick:141]
  {
    "nameValuePairs": {
    "field1": "field1",
    "field3": "com.morayl.footprintexample.ExampleActivity$DataClass@418187e0",
    "field2": "true"
  }
 */

細かすぎる(?)こだわり

デバッグログを仕込むことに時間がかかったり、気分が良くなかったりしてはもったいないです。
Footprintは打ちやすさにもこだわりました。

  • クラス名の頭の「F」はホームポジションから動かず打て、大文字にするためのShiftとの同時押しも容易
  • メソッド名は、「s, d, f, t, j, k, l, n」。全てホームポジションまたはその近くにあり、打つときの指の移動距離は最小限

カスタマイズ

カスタマイズと言っては大げさですが、Footprintは状況に応じた設定が可能です。

// ログの有効無効を設定できます
Footprint.setEnable(boolean);

// ログのタグを「Footprint」から変更できます(需要があるかは不明)
Footprint.setLogTag(string);

注意点

leave()に始まるほとんどのメソッドで、クラス名などを見るために内部でstackTraceを回しているため、かなり多い回数UIスレッドでfor文ループするところに入れたりすると、動作が遅くなる可能性があります。(デバッグなので問題ないとは思いますが)
setEnableで有効無効を変えることは出来ますが、基本的にデバッグコードを入れたままアプリをリリースするのは良くないと思うので、debugCompileで利用することを推奨しています。(あくまで個人的な推奨なので、compileでsetEnable利用でも問題ありません)
debugImplementationでデバッグ時に利用し、最終的にコミットするときにはデバッグコードを削除する運用にすることで、releaseビルドした時にコードが残っていればエラーになるので、うっかり入ってしまうこともなく、強制的にデバッグのみで利用出来るように制限できます。

利用方法

gradleに記述するだけです。
詳しくはこちら
https://github.com/morayl/Footprint#download

repositories {
    maven { url 'http://raw.github.com/morayl/Footprint/master/repository' }
}
dependencies {
    debugImplementation 'com.morayl:footprint:最新のバージョン'
    // リリースビルドにも入れたいよ!という方は以下でもOK
    // implementation 'com.morayl:footprint:最新のバージョン'
}

最後に

個人的にLog.dを使うのが苦痛だったので、「こんなのあったらいいな」というものをたくさん詰めてライブラリにしてみました。
ですので、私としては楽しくデバッグ出来るようになりました。
完全に自分向けに作っているので足りない視点があるかもしれませんが、気になった方はぜひご利用いただき、感想を聞かせていただけると幸いです。
ここまでお読みいただきありがとうございました!