AndroidのTimber(ログ系)とFabric/Crashlytics(クラッシュレポート)を使ってみた。
Log.d使うのやめよう。
ログのコメントアウトが面倒くさい。
Androidでログを出力するにはLogクラスがある。
開発中は、デバッグ目的でよく利用すよねー
リリースのときに、コメントアウトを忘れるんです。
ログ系オープンソースを探してみた結果、Timberを使ってみた。
これでリリースと開発でログの出力を分けたりすることが簡単にできる。
リリース時は、クラッシュレポートのログとして利用する。
compile 'com.jakewharton.timber:timber:4.1.0'
DebugTree
アプリが起動したら、はじめにTimberの設定をしよう。出力するログのタイプを決める。
リリースとデバッグはこの設定で切り替える。DebugTreeは標準で用意されたライブラリで、リリース目的のログクラスは、独自で作って対応する。
Timber.plant(new Timber.DebugTree());
タグを付ける。
Timber.tag("マイタグ");
ログを出力する。
Timber.e(String.format("Hello, %s %s", "Tanaka", "Hanako")); 11-01 18:34:40.376 9027-9027/? E/マイタグ﹕ Hello, Tanaka Hanako
デバッグとリリースで分ける。
各Activityで初期化するのは面倒なので、Applicationクラスでアプリ起動時に1度だけ行うこと。
if (BuildConfig.DEBUG) { Timber.plant(new DebugTree()); //デバッグ } else { Timber.plant(new MyCrashReportingTree()); //リリース }
MyCrashReportingTree
private static class MyCrashReportingTree extends Timber.Tree { @Override protected void log(int priority, String tag, String message, Throwable t) { Log.d("タグ", tag); Log.d("タグ", message); if (priority == Log.ERROR) { Log.d("タグ", "エラー"); } if (priority == Log.DEBUG) { Log.d("タグ", "デバッグ"); } if (priority == Log.WARN) { Log.d("タグ", "警告"); } } }
リリース、アップデート後の不安、クラッシュレポートで解決しよう。
アプリのDL数はリアルタイムで更新されません。つまり問題があってもわかりません。クラッシュレポート機能をつけて、少しでも早くバグに気づこう。
Fabric/Crashlytics
リリース時のクラッシュレポートには、TwitterのFabricのCrashlyticsが有名みたい。Twitterアカウントを使ったログイン機能、広告、クラッシュレポート、この3つのサービスをまとめたもの。今回は、クラッシュレポートのみを使用する。
Android Studio用のFabricプラグインをダウンロード。
Safariだと、zipを自動で解凍してしまうので、Safariの設定をダウンロード後に ”開かない”という設定に変更すること。
インストール
Android studio => Preferences => Plugins => Install plugin from discを選択。
テストしてみる。
勝手にいろんなコードが追加されました。では、はたしてインストールできたのか。。。
強制終了させて、fabric経由でメールが来るのかテストしてみる。
強制終了させるテスト
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Fabric.with(this, new Crashlytics()); setContentView(R.layout.activity_main); TextView nameTv = null; nameTv.setText("ダルビッシュ〜"); }
登録したメールアドレスにNew Fatal Issue Discoveredメールが届いた。
なぜか、Gmailのプロモーションの中に入っていたw
Android StudioのFabricプラグインからも確認できる。
クラッシュした時のレポートから、ファイル名、行数、OS、画面の向き、メモリの状況などを取得できる。
開発者が独自に追加してレポートに追加することもできる。
レポートにユーザー情報を追加する。
アカウントやメールアドレスをアプリで管理している場合は、crashlyticsに情報を設定しておけば、ダッシュボードから、強制終了が起きた時に、確認することができる。もし管理してない場合は、ユーザー固有の識別子を割り当てよう。レポートが溜まってきた時に、エラーの優先度を決める時に、役に立つ。
Fabric.with(this, crashlyticsKit); setContentView(R.layout.activity_main); //識別子で管理。 crashlyticsKit.setUserIdentifier("識別"); //ユーザー名やEmailで管理。 crashlyticsKit.setUserEmail("info.yusuke.arai@gmail.com"); crashlyticsKit.setUserName("マイケル"); EditText tv = null;
API
void setBool(String key, boolean value); void setDouble(String key, double value); void setFloat(String key, float value); void setInt(String key, int value);
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Fabric.with(this, new Crashlytics()); setContentView(R.layout.activity_main); Crashlytics.setString("タグ", "UIの操作"); TextView nameTv = null; nameTv.setText("ダルビッシュ〜"); }
開発中は、クラッシュレポートの機能を無効にする
Crashlytics crashlyticsKit = new Crashlytics.Builder() .core(new CrashlyticsCore.Builder().disabled(!BuildConfig.DEBUG).build()) .build(); Fabric.with(this, crashlyticsKit);
開発中はAndroid Studioにログを出力する。リリース時はログのタイプによって処理を分ける。下の記事を参考にしました。
Crashlyticsへログを送る
Crashlytics.logException(new Exception("強制終了でないけど、ログを送るよ"));
VERBOSE、DEBUG、INFO以外は、Fabric/Crashlyticsへログを渡してあげます。例えば、サーバーと連携するような処理で接続エラーの時や、Sqliteのデータベースエラー、例外処理に仕込んでおくといいかも。
BuildConfig.DEBUGのフラグを切り替えると、開発とリリースで切り替えることができる。
import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.util.Log; import com.crashlytics.android.Crashlytics; import io.fabric.sdk.android.Fabric; import timber.log.Timber; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Fabric.with(this, new Crashlytics()); Timber.plant(new CrashlyticsTree()); } Timber.tag("タグ"); Timber.e("メッセージ"); } public class CrashlyticsTree extends Timber.Tree { private static final String CRASHLYTICS_KEY_PRIORITY = "priority"; private static final String CRASHLYTICS_KEY_TAG = "tag"; private static final String CRASHLYTICS_KEY_MESSAGE = "message"; @Override protected void log(int priority, @Nullable String tag, @Nullable String message, @Nullable Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { return; } Crashlytics.setInt(CRASHLYTICS_KEY_PRIORITY, priority); Crashlytics.setString(CRASHLYTICS_KEY_TAG, tag); Crashlytics.setString(CRASHLYTICS_KEY_MESSAGE, message); if (t == null) { Crashlytics.logException(new Exception(message)); } else { Crashlytics.logException(t); } } } }