
はじめに
こんにちは、ZOZOTOWN開発本部 ZOZOTOWN開発1部 Android2ブロックの高橋です。普段はZOZOTOWNのAndroidアプリ開発を担当しています。
アプリ開発において、Google Analyticsなどのイベントトラッキング機能はプロダクトの改善のための重要な機能です。しかし、「正しいデータが送信されているか」だけでなく「正しいタイミングで送信されているか」の検証が難しいという課題もあります。ZOZOTOWNのAndroidアプリ開発においても課題となっていました。本記事では、この課題を解決するために開発したAndroid Studioプラグインと、その技術選定・設計についてご紹介します。
目次
背景・課題
イベントの実装時やPRレビュー時、以下のような作業に時間を取られていました。
1. 値の妥当性確認
これまでイベントのデバッグにはLogcatに出力されるログを使用していました。GAに関してはFirebase DebugViewでリアルタイムにイベントを確認できますが、GAと自社ログなど複数の送信先を横断的に確認できません。Logcatであれば送信先を問わず確認できますが、大量のログの中から目的のイベントログを見つけ出すには、タグやキーワードでフィルタリングしながら目視で探す必要がありました。GAや自社ログなど送信先ごとにフォーマットも異なるため、それぞれの形式を把握しておく必要もあります。
そのような環境で、必須パラメータの過不足や値の妥当性をテキストとして流れるログから目視で確認するのは手間がかかります。
2. 「どの操作で発火したか」の再現・確認
特に難しかったのが、イベント発火のタイミング確認です。
イベントが短期間で複数発生するケースでは、ログを見ただけでは「どの操作がどのイベントに対応するのか」を判別するのが困難でした。一定期間内に一度しか発火しないイベントであれば操作との対応は明らかですが、リスト画面でのスクロールや連続タップなど、似たイベントが立て続けに発火する場面では確認が難しくなります。
3. PRレビューでの説明
「このイベントは正しいタイミングで送信されていますか?」というレビュー指摘に対して、ログのテキストだけで説明するのは困難です。「画面Aから画面Bへ遷移したときに発火する」という仕様を操作と紐づけて証明する手段がありませんでした。
求められる解決策
これらの課題を解決するには、以下の要件を満たすツールが必要でした。
- 複数の送信先のイベントを統一的に可視化できる
- イベントの発火タイミングを実際の操作映像と紐づけて確認できる
- 確認した結果を他のメンバーに共有できる
- 開発フローに組み込みやすい(IDEとの統合)
解決アプローチ: Android Studioプラグインの開発
課題を解決するため、3つの機能を統合したAndroid Studioプラグインを開発しました。

| 機能 | 役割 |
|---|---|
| イベントビューア | ログをパースし、時系列のノード型UIで可視化 |
| DVR(Digital Video Recorder)・アーカイブ再生 | キャプチャ映像とイベントの同期記録・再生 |
| セッションの共有 | キャプチャしたセッションをファイルとして共有 |
イベントビューアは、Logcatから取得したログをパースし、イベントをノード型のUIで時系列に表示します。UIはFirebase DebugViewのイベント表示を参考にしています。GAや自社ログなど送信先が異なるイベントも統一的に可視化でき、各ノードにはイベント名と送信先が表示されます。ノードをクリックすると、画面右のパネルにイベントのパラメータが表示されます。
DVRおよびアーカイブ再生は、イベントビューアとキャプチャ映像を時刻ベースで結びつける機能です。ノードをクリックすると、そのイベントが発火する直前からの操作映像を再生できます。これにより「どの操作がどのイベントを発火させたか」を視覚的に検証できます。
| イベント発火時のエフェクト | 映像再生時のイベント到達エフェクト |
|---|---|
|
|
新しいイベントが追加されると青白いグロー・アニメーションで視覚的に強調されるため、リアルタイムにどのイベントが発火したかを把握できます。また、映像の再生位置がイベントのタイムスタンプに到達すると、該当ノードが赤いグロー・アニメーションでハイライトされます。
キャプチャしたセッションはエクスポート・インポート可能になっており、他の開発者の環境でもセッションを視覚的に確認できます。
処理フロー
プラグインの動作をイベント発火からDVR再生までの流れで説明します。
1. ログの取得とパース
端末と接続すると、ADB経由でLogcatストリームの取得を開始します。取得したログは、ログフォーマットに応じたパーサが自動選択・適用されます。
パースの結果、各ログ行はタイムスタンプ(デバイス時刻)やイベント名、パラメータなどを持つ構造化されたイベントオブジェクトに変換されます。
2. イベントの保存とUI表示
パースされたイベントはインメモリのイベントストアに蓄積されます。UIはリアクティブなデータフローを通じてイベント追加を検知し、画面を更新します。
3. 画面キャプチャの取得
ログ収集と並行して画面キャプチャが行われます。映像ストリームをデコードし、各フレームにタイムスタンプを付与しバッファに保存します。
4. ノードクリックによるDVR再生
ユーザーがタイムライン上のイベントノードをクリックすると、そのイベントのタイムスタンプを録画開始からの経過時間(相対時刻)に変換し、一定時間だけ巻き戻した位置から映像の再生を開始します。これにより、「どの操作がこのイベントを発火させたか」を視覚的に確認できます。
イベントログの収集
イベントログを収集する方法として、以下の選択肢を検討しました。
| 方式 | 概要 | 採用判断 |
|---|---|---|
| adb reverse経由 | アプリからホストへ直接送信 | 不採用 |
| Logcat経由 | ADBでLogcatストリームを取得 | 採用 |
adb reverseを使用すると、アプリから直接ホスト(開発マシン)にデータを送信できます。この方式であれば、Logcatのパースが不要になり、構造化されたデータをそのまま受け取れるメリットがあります。しかし、この方式には以下の課題がありました。
- アプリ側の改修が必要:ログ送信用のHTTPクライアントやソケット通信の実装が必要
- 既存のログ出力との二重管理:Logcatへの出力と並行して別の送信処理を実装することになる
一方、Logcat経由の方式には以下の利点があります。
- アプリ側の改修が不要:既存のログ出力をそのまま利用できる
- 既存資産の活用:すでにログ出力の仕組みが整っているアプリであれば、追加実装なしで利用可能
アプリ側を改修せずにプラグイン単体で動作させる設計方針を優先し、Logcat経由の方式を採用しました。
イベントの統合・可視化
複数の送信先のイベントを統一的に扱うため、パーサレジストリパターンを採用しました。各ログフォーマットに対応するパーサは、共通のインタフェースを実装します。
interface LogParser { val id: String val displayName: String fun canParse(rawLine: String): Boolean fun parse(rawLine: String, deviceSerial: String? = null): LogEvent? }
パーサレジストリはログを受け取ると登録されたパーサを順番にチェックし、最初にマッチしたパーサでパースを行います。
class ParserRegistry { private val parsers = mutableListOf<LogParser>() private var defaultParser: LogParser = DefaultLogcatParser() init { // 各フォーマット用パーサを登録(登録順にマッチングされる) register(GoogleAnalyticsLogParser()) register(InternalBusinessLogParser()) } fun register(parser: LogParser) { parsers.add(parser) } fun detectParser(rawLine: String): LogParser { return parsers.firstOrNull { it.canParse(rawLine) } ?: defaultParser } }
detectParserは登録されたパーサを順に試行し、最初にマッチしたパーサを返します。どのパーサにもマッチしない場合はdefaultParser(標準のLogcat形式パーサ)にフォールバックする設計です。
この設計により、新しいログフォーマットへの対応はパーサクラスを1つ追加・登録するだけで完了します。
画面キャプチャ
端末の画面をキャプチャする方法として、以下の選択肢を検討しました。
| 方式 | 概要 | 採用判断 |
|---|---|---|
| MediaProjection API | Android APIによる画面キャプチャ | 不採用 |
| scrcpy | USB/TCP経由のミラーリングツール | 採用 |
MediaProjection APIはAndroidアプリ内から画面をキャプチャできるAPIです。しかし、この方式を採用するとデバッグ対象のアプリ自体に手を加えるか、別途キャプチャ用アプリを用意する必要があります。
scrcpyはオープンソースのミラーリングツールで、以下の点で要件に合致しました。
- リアルタイム性:ミラーリングと同程度の低遅延で画面を取得できる
- アプリ側の改修が不要:ADB経由で動作するため、デバッグ対象アプリへの変更が不要
- 組み込みやすさ:プロトコルが公開されており、プラグインへの統合が可能
上記の特性を考慮し、scrcpyを採用しました。
本プラグインではscrcpyのH.264ストリームを利用しています。受信したストリームをデコードしてRAW RGB形式のフレームを出力し、リアルタイムのミラーリング表示とDVR用の録画バッファの両方に供給しています。
DVR:イベントとの同期
本プラグインの核となる機能がDVRです。端末のキャプチャ映像とイベントを時刻ベースで同期し、後から任意の時点の映像を再生できます。
イベントと映像の同期を実現するうえで、2つの課題を解決する必要があります。
1つ目は、イベントと映像のタイムスタンプの基準を揃えることです。イベントのタイムスタンプはデバイスの絶対時刻で記録されますが、映像フレームのタイムスタンプはホスト側で録画開始からの経過時間(相対時刻)として付与されます。イベントのタイムスタンプも同じ相対時刻へ変換するために、録画開始時のデバイス時刻を記録しておき、イベントのタイムスタンプからこの値を引くことで録画開始からの経過時間を算出しています。
2つ目は、映像パイプラインの遅延です。scrcpyでキャプチャされたフレームは、デバイス上でのエンコード → ホストへの転送 → デコードという過程を経てホストに到達します。フレームのタイムスタンプはホスト受信時に付与するため、実際のキャプチャ時刻よりパイプライン遅延の分だけ遅れた値になります。この遅延を補正するために、録画開始から最初のフレームが到着するまでの時間をもとにパイプライン遅延を推定し、各フレームのタイムスタンプからその分を差し引いています。
これらの補正により、DVR再生時にイベントと映像のタイミングがある程度正確に一致するようになります。
セッションのエクスポート・インポート
DVR機能により「操作映像とイベントの同期再生」が可能になりましたが、これだけではキャプチャした本人のマシンでしか確認できません。イベントトラッキング関連の不具合が発生した際に、その状況を他のメンバーに正確に伝えるには、キャプチャしたセッションそのものを共有できる仕組みが必要でした。そこで、イベントとキャプチャ映像をまとめてエクスポート・インポートできる機能を実装しました。
エクスポートされるファイルは.edbという拡張子を持つZIPアーカイブです。内部にはJSON形式のセッションメタデータとシリアライズされたイベント、映像フレーム群が格納されます。
セッションメタデータにはアーカイブ再生で必要な時刻同期パラメータ(録画開始時のデバイス時刻、映像パイプライン遅延の推定値など)を含めています。これにより、別のマシンでインポートした場合でも、元のキャプチャ環境と同じ精度でイベントと映像の同期再生が可能になります。
PRレビューでの活用
この機能により、以下のようなワークフローが可能になります。
- 開発者:イベント発火の再現手順を実行しながらキャプチャを行う
- 開発者:セッションを
.edbファイルとしてエクスポートし、PRやIssueに添付する - レビュアー:
.edbファイルをインポートし、アーカイブ再生で操作映像とイベントの同期再生を確認する
従来は「ある操作をしたときに特定のイベントが発火しています」とテキストで説明するしかありませんでした。エクスポート・インポート機能により、レビュアーは自分の環境で操作映像を再生しながらイベントの発火タイミングを直接確認できます。端末やアプリの準備も不要なため、レビューの負荷も軽減されます。
実装について
本プラグインの実装は、その大部分をAIとの協業で進めました。開発者が利用可能な技術の候補を挙げ、そこから先の技術選定はAIとのディスカッションを通じて行いました。例えば画面キャプチャの方式では、候補となる技術をそれぞれ試作し、要件に合わないとわかれば別の方式に切り替えるというサイクルを短期間で回せました。設計面でもAIにアイデアを出してもらい、開発者が将来の機能追加などを考慮して判断するという進め方です。IntelliJ Platform SDKの使い方やscrcpyプロトコルの解析など、ドキュメントを読み込んで実装に落とし込む作業はAIが得意とする領域でした。
効果
イベントがノード型UIで可視化されたことで、Logcatに出力されるテキストを目で追う手間がなくなり、イベントの検証効率が向上しました。
また、DVR・アーカイブ再生機能により、これまで困難だった「イベント発火タイミングの検証」が可能になりました。具体的には以下のことが行えます。
- イベントノードをクリックするだけで、発火時の操作映像を再生できる
- 「画面遷移時に発火」「ボタンタップ時に発火」といった仕様の検証が視覚的に行える
- セッションの共有機能により、操作映像を見せながら「この操作でこのイベントが発火しています」と説明できる
同種イベントが連続発火するケースでも、どの操作がどのイベントに対応するかを明確に示せるようになりました。
今後の展望
現状では「アプリがログを出力した」ことは確認できますが、「実際にサーバへ送信された」ことは確認できません。実際の通信ログも取り込めるようになれば、エンドツーエンドでの検証が可能になります。
まとめ
本記事では、イベントトラッキングのデバッグ課題を解決するために開発したAndroid Studioプラグインを紹介しました。本プラグインは社内利用を前提としているため現時点で一般公開の予定はありませんが、イベントトラッキングのデバッグに課題を感じている方の参考になれば幸いです。
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。