ログ管理でウキウキAndroid Life
2014/11/20 Mercari Inc. 今井智章
自己紹介• 株式会社メルカリ Android エンジニア(2014.3~)
!
!
!
!
!
!
• 以前はSIerでインフラ系DBエンジニア(たまにjava開発)2
twitter: tomoaki_imai github: tomoima525 qiita: tomoima525
フリマアプリの機能開発、USアプリ開発
Agenda
• ウキウキするエラー系ログ管理のお話
• ウキウキする分析系ログ管理のお話
3
エラーログとは
ユーザーの声にならない 叫び
ユーザーの声として上がるのはほんの一部です
エラーログ、見ましょう バグ、駆逐しましょう
メルカリでのエラー監視
• Google Developer Console
• Crashlytics
• 自前でのリアルタイムエラーレポート
8
Google Developer Console• apkをGoogle playに上げる時に時のアレ
• アプリがクラッシュし、ユーザーがエラーレポート送付してくれた内容が見られる
- なので大体氷山の一角
• Crashlyticsで拾えないネイティブクラッシュが確認できる
9
Crashlytics
• 言わずと知れた有名エラーレポートサービス
• 導入が超簡単。レポートも詳細。
• テスト配信機能も有り
• 最近はアカウント作成待ちの行列ができてる10
使い方• 一番最初に起動するActivityで呼ぶだけ
!
!
• クラッシュ時の情報を添付することもできる
11
@Override public void onCreate(Bundle savedInstanceState) { com.crashlytics.android.Crashlytics.start(this); }
com.crashlytics.android.Crashlytics.setString( "last_activity", this.getClass().getName());
使い方
!
• メルカリではdev,stg,productでパッケージ名を分け、それぞれでログ監視
• テスト、QAレベルでエラーログを拾う12
自前エラーレポート• エラー用Apiでクラッシュ時に送信
• より詳細に状況を把握し、バグ解消に活かす
• 以下の3点が実装のポイント
13
①予期せずエラーが起きてもレポートを飛ばす仕組み
②既知のバグの発生条件を調査できる仕組み
③レポートを貯めて送る仕組み
仕組み①予期せずエラーが起きてもレポートを飛ばす仕組み
14
public class ThisApplication extends Application implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread thread, final Throwable ex) { new Thread() { public void run() { ByteArrayOutputStream b = new ByteArrayOutputStream(); ex.printStackTrace(new PrintStream(b)); String errMessage = b.toString(); gotoErrorActivityAndReport(errMessage); //エラーレポートをAPIに送る System.exit(1); }; }.start(); }
Applicationクラス内でThread.UncaughtExceptionHandlerを実装
仕組み②既知のバグの発生条件を調査できる仕組み
15
try { return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); } catch (OutOfMemoryError ooe) { postErrorReport(getCurrentActivity().getClass().getName() , options.inSampleSize , options.outWidth , reqWidth); //画像処理系で画像サイズ情報を送信 }
すでにErrorが発生すると判明している部分で、try/catchし、 catch内で調査に必要な情報を送る
public void sendErrorReportInternal(final JSONObject errReport) { String errMessage = JSONUtil.getString(errReport, S.message); if (errMessage != null) { Api.process(errReport, new responseListener<JSONObject>() { @Override public void onResponse(JSONObject response) {} @Override public void onErrorResponse(JSONObject response) { ErrorStore.addErrReport(errReport); //プリファレンスに保存 } //以下略
仕組み③レポートを貯めて送る仕組み
16
レポートをプリファレンスに保存し、レポート送信前にクラッシュ したり通信できなくても、次回起動時に送付できるようにする
public void onResume() { sendOldErrorReports(); //以前のレポートがあった場合は送信 }
まとめ• Crashlyticsで届かないかゆいところに自前エラーレポートを組み合わせるとバグ駆逐が捗る
17
Google Analytics Crashlytics 自前
ログのわかりやすさ △ ◯ ◎
カスタマイズ性 ☓ △ ◎
導入容易性 default ◯ △
ウキウキしましたか?
続いて、分析ログの話
What is 分析ログ?• インストール率, DAU, 売上, A/B testing etc. を分析するためのログ
• 用途によって様々なサービスがある
20
分析ログ(サービス)の問題点
• それぞれの分析サービスのApiに標準性がない
- 似たようなトラッキングコードをひたすら書かされる
- その割にApiごとに癖があって実装に困る
- 新しいライブラリを追加する度に導入の調査が必要になる
21
そうだ、Segment 使おう
Segment• 複数の分析サービスを統合できる
• シンプルなAPIで導入が簡単
• けどかなり詳細に分析データを送信できる!
23
Segment• SegmentのApiで多くの分析サービスをカバー
• ユーザーはコンソールから分析サービスを選ぶだけ
24
実装例• 事前準備
- アカウントの用意
- https://segment.com/
- プロジェクトの作成
- 組織単位でproject作成が可能
- API keyを確認
25
こちらにまとめました↓http://qiita.com/tomoima525/items/bab086d341b686b9f8a1
実装例• gradleのdependencies追加(Android Studio)
!
!
!
!
• AndroidManifest.xmlへの設定
26
dependencies { compile('com.segment.analytics.android:core:+@aar') { transitive = true } //必要なライブラリのみ追加 compile 'com.google.android.gms:play-services:+' compile ‘com.mixpanel.android:mixpanel-android:+@aar' }
<uses-permission android:name=“android.permission.INTERNET”/> //必須 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.GET_TASKS"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Eclipse向けにjarファイルもある(ただしバージョンは古い)
実装例• res/values/analytics.xmlにAPI keyを設定
27
<resources> <string name="analytics_write_key">YOUR_WRITE_KEY</string> <string name="analytics_logging">true</string> <integer name="analytics_queue_size">20</integer> </resources>
analytics_write_key: コンソールで確認したApi key analytics_logging: logに送信データが出力 うまく確認できなかった。iOSでは見られる? analytics_queue_size: 一度のキューで送信するイベントの数 キューがたまらなくても一定時間で送信されている様子
代表的なトラッキングメソッド• identify
Analytics.with(context).identify(new Traits().putName(“Moge Hoge")); Analytics.with(context).identify( new Traits().putEmail([email protected]"));
Analytics.with(context).identify(user_id);
アプリユーザーのIDを設定する
複数のTraits(特徴)を設定することも可能
Traitsはage, gender等多数あり、分析サービスに応じて設定可能
https://segment.com/docs/api/tracking/identify/#special-traits
代表的なトラッキングメソッド• Alias
会員登録前後のIDを紐つけ、会員登録前のイベントを関連付けする
※Segmentのエンジニア曰く統合がまだ十分でないらしく、 今後も変わる可能性あり
Analytics.with(context).alias(uuid, new_id); //会員登録前のIDがない場合はSegment内のanonymousIDが紐付けられる
Mixpanel、Flurryといった分析サービスで利用
代表的なトラッキングメソッド• Track
あらゆるイベントにプロパティ、トラック名をつけてトラックする
プロパティは任意のキーを設定できる
Properties properties = new Properties() .putValue("price", price) .putValue("category", CategoryId); //オブジェクト型を設定できる Analytics.with(context).track(trackName, properties);
デバッガー• コンソールデバッガーで値を確認できる
{ "messageId": "09361829-c11c-451d-83d0-d1e2e9fc9063", "type": "track", "anonymousId": "d6c327e4-76c3-418c-907e-4b0eba89ec40", // 中略 "timestamp": "2014-11-19T11:34:54.000Z", "event": "Eat Curry", "properties": { "price": 1200, "category": "Indian", "condition": "good", }, }
値はjsonでも取得可能
分析• コンソールから分析サービスを選ぶ
それぞれの分析サービスに必要なIDやkeyを設定するだけ
分析ツールの追加があってもライブラリ、ソースコードの追加は(ほとんど)不要
スッキリ
ちょっと注意
• 成長中のサービスなので、開発がさかん
• Documentの記述が食い違っている所もある
• ソースコードが食い違っている所がある(!)
• オープンソースなのでおかしかったら迷わずPR
35
まとめ
• エラーログ管理はカスタマイズするとバグ駆逐が捗ってウキウキ
• 分析ログはSegment使うと楽ちん、スッキリでウキウキ
36
メルカリではAndroid/iOSエンジニア募集中!