高度な Crashlytics 機能を使用して Unity ゲームのクラッシュについて理解する

1. はじめに

この Codelab では、Crashlytics の高度な機能の使用方法を学びます。これにより、クラッシュとその原因となった状況をより詳しく把握できるようになります。

サンプルゲーム「MechaHamster: Level Up with Firebase Edition」に新機能を追加します。このサンプルゲームは、従来の Firebase ゲームである MechaHamster の新しいバージョンであり、組み込みの Firebase 機能のほとんどが削除されています。そのため、代わりに Firebase の新しい使い方を実装できます。

ゲームにデバッグ メニューを追加します。このデバッグ メニューでは、作成するメソッドを呼び出し、Crashlytics のさまざまな機能を実行できます。これらのメソッドでは、自動クラッシュ レポートにカスタムキー、カスタムログ、致命的でないエラーなどでアノテーションを付ける方法がわかります。

ゲームを構築したら、デバッグ メニューを使用して結果を調べて、実際の環境でのゲームの動作に関する独自のビューを把握します。

学習内容

  • Crashlytics によって自動的にキャッチされるエラーの種類。
  • 意図的に記録できるその他のエラー。
  • これらのエラーに情報を追加して、理解しやすくする方法。

必要なもの

  • Unity(最小推奨バージョン 2019 以降)と以下のいずれかまたは両方:
    • iOS ビルドサポート
    • Android ビルドサポート
  • (Android のみ)Firebase CLI(クラッシュ レポートのシンボルのアップロードに使用)

2. 開発環境を設定する

以下のセクションでは、Level Up with Firebase コードをダウンロードして Unity で開く方法について説明します。

この「Level Up with Firebase」サンプルゲームは、他のいくつかの Firebase と Unity の Codelab でも使用されているため、このセクションのタスクをすでに完了している可能性があります。その場合は、このページの最後の手順「Unity 用の Firebase SDK の追加」に進んでください。

コードをダウンロードする

コマンドラインから、この Codelab の GitHub リポジトリのクローンを作成します。

git clone https://github.com/firebase/level-up-with-firebase.git

Git がインストールされていない場合は、リポジトリを ZIP ファイルとしてダウンロードすることもできます。

Unity エディタで [Level Up with Firebase] を開きます。

  1. Unity Hub を起動し、[Projects] タブで [Open] の横にあるプルダウン矢印をクリックします。
  2. [ディスクからプロジェクトを追加] をクリックします。
  3. コードが含まれているディレクトリに移動し、[OK] をクリックします。
  4. プロンプトが表示されたら、使用する Unity エディタのバージョンとターゲット プラットフォーム(Android または iOS)を選択します。
  5. プロジェクト名(level-up-with-firebase)をクリックすると、Unity エディタでプロジェクトが開きます。
  6. エディタが自動的に開かない場合は、Unity Editor の [Project] タブで [Assets] > [Hamster] の順に選択して MainGameScene を開きます。
    ff4ea3f3c0d29379.png

Unity のインストールと使用について詳しくは、Unity の操作をご覧ください。

3. Firebase を Unity プロジェクトに追加する

Firebase プロジェクトを作成する

  1. Firebase コンソールで [プロジェクトを追加] をクリックします。
  2. 新しいプロジェクトを作成するには、目的のプロジェクト名を入力します。
    これにより、プロジェクト ID(プロジェクト名の下に表示)もプロジェクト名に基づいたものに設定されます。必要に応じて、プロジェクト ID の編集アイコンをクリックして、さらにカスタマイズできます。
  3. Firebase の利用規約が表示されたら、内容を読み、同意します。
  4. [続行] をクリックします。
  5. [このプロジェクトで Google アナリティクスを有効にする] オプションを選択し、[続行] をクリックします。
  6. 使用する既存の Google アナリティクス アカウントを選択するか、[新しいアカウントを作成] を選択して新しいアカウントを作成します。
  7. [プロジェクトの作成] をクリックします。
  8. プロジェクトが作成されたら、[続行] をクリックします。

アプリを Firebase に登録する

  1. Firebase コンソールで、プロジェクトの概要ページの中央にある Unity アイコンをクリックして設定ワークフローを開始します。すでに Firebase プロジェクトにアプリを追加している場合は、[アプリを追加] をクリックしてプラットフォームのオプションが表示されます。
  2. Apple(iOS)と Android の両方のビルド ターゲットを登録する場合に選択します。
  3. Unity プロジェクトのプラットフォーム固有の ID を入力します。この Codelab では、次のように入力します。
  4. (省略可)Unity プロジェクトのプラットフォーム固有のニックネームを入力します。
  5. [アプリの登録] をクリックして、[構成ファイルのダウンロード] セクションに進みます。

Firebase 構成ファイルを追加する

[アプリの登録] をクリックすると、2 つの構成ファイル(ビルド ターゲットごとに 1 つの構成ファイル)をダウンロードするよう求められます。Unity プロジェクトが Firebase に接続するには、これらのファイル内の Firebase メタデータが必要です。

  1. 使用可能な両方の構成ファイルをダウンロードします。
    • Apple(iOS)の場合: GoogleService-Info.plist をダウンロードします。
    • Android の場合: google-services.json をダウンロードします。
  2. Unity プロジェクトで [Project] ウィンドウを開き、両方の構成ファイルを [Assets] フォルダに移動します。
  3. Firebase コンソールの設定ワークフローで [次へ] をクリックし、「Firebase SDK for Unity の追加」に進みます。

Unity 用の Firebase SDK を追加する

  1. Firebase コンソールで [Firebase Unity SDK をダウンロード] をクリックします。
  2. 適切な場所で SDK を解凍します。
  3. 開いている Unity プロジェクトで、[Assets] > [Import Package] > [Custom Package] を選択します。
  4. [Import package] ダイアログで、解凍した SDK を含むディレクトリに移動し、[FirebaseAnalytics.unitypackage] を選択して [Open] をクリックします。
  5. 表示された [Import Unity Package] ダイアログで、[Import] をクリックします。
  6. 上記の手順を繰り返して FirebaseCrashlytics.unitypackage をインポートします。
  7. Firebase コンソールに戻り、設定ワークフローで [次へ] をクリックします。

Firebase SDK を Unity プロジェクトに追加する方法について詳しくは、Unity のその他のインストール オプションをご覧ください。

4. Unity プロジェクトで Crashlytics を設定する

Unity プロジェクトで Crashlytics を使用するには、いくつかの設定手順を追加する必要があります。もちろん、SDK を初期化する必要があります。ただし、Firebase コンソールでシンボリケートされたスタック トレースを表示できるようにシンボルをアップロードし、強制的にテスト クラッシュを発生させて Firebase がクラッシュ イベントを取得する必要があります。

Crashlytics SDK を初期化する

  1. Assets/Hamster/Scripts/MainGame.cs に、次の using ステートメントを追加します。
    using Firebase.Crashlytics;
    using Firebase.Extensions;
    
    最初のモジュールでは Crashlytics SDK のメソッドを使用できます。2 番目のモジュールには C# Tasks API の拡張機能が含まれています。両方 using ステートメントがないと、次のコードは機能しません。
  2. 引き続き MainGame.cs で、InitializeFirebaseAndStartGame() を呼び出して Firebase の初期化を既存の Start() メソッドに追加します。
    void Start()
    {
      Screen.SetResolution(Screen.width / 2, Screen.height / 2, true);
      InitializeFirebaseAndStartGame();
    }
    
  3. 再び MainGame.csInitializeFirebaseAndStartGame() を探し、アプリ変数を宣言して、メソッドの実装を次のように上書きします。
    public Firebase.FirebaseApp app = null;
    
    // Begins the firebase initialization process and afterwards, opens the main menu.
    private void InitializeFirebaseAndStartGame()
    {
      Firebase.FirebaseApp.CheckAndFixDependenciesAsync()
      .ContinueWithOnMainThread(
        previousTask => 
        {
          var dependencyStatus = previousTask.Result;
          if (dependencyStatus == Firebase.DependencyStatus.Available) {
            // Create and hold a reference to your FirebaseApp,
            app = Firebase.FirebaseApp.DefaultInstance;
            // Set the recommended Crashlytics uncaught exception behavior.
            Crashlytics.ReportUncaughtExceptionsAsFatal = true;
            InitializeCommonDataAndStartGame();
          } else {
            UnityEngine.Debug.LogError(
              $"Could not resolve all Firebase dependencies: {dependencyStatus}\n" +
              "Firebase Unity SDK is not safe to use here");
          }
        });
    }
    

ここに初期化ロジックを配置すると、Firebase の依存関係が初期化される前にプレーヤーの操作が行われなくなります。

未処理の例外を致命的として報告することのメリットと影響については、Crashlytics に関するよくある質問をご覧ください。

プロジェクトをビルドしてシンボルをアップロードする

シンボルの作成とアップロードの手順は、iOS アプリと Android アプリで異なります。

iOS+(Apple プラットフォーム)

  1. [Build Settings] ダイアログで、プロジェクトを Xcode ワークスペースにエクスポートします。
  2. アプリをビルドします。
    Apple プラットフォームの場合、Firebase Unity Editor プラグインによって Xcode プロジェクトが自動的に構成され、ビルドごとに Crashlytics 互換のシンボル ファイルが生成されて Firebase サーバーにアップロードします。このシンボル情報は、Crashlytics ダッシュボードでシンボリケートされたスタック トレースを表示するために必要です。

Android

  1. (ビルドごとの設定ではなく、初期設定時のみ)ビルドを設定します。
    1. プロジェクト ディレクトリのルートに(つまり、Assets ディレクトリの兄弟として)Builds という新しいフォルダを作成し、Android というサブフォルダを作成します。
    2. [File] > [Build Settings] > [Player Settings] > [Configuration] で、[Scripting Backend] を IL2CPP に設定します。
      • 一般に、IL2CPP ではビルドが小さくなり、パフォーマンスが向上します。
      • IL2CPP は、iOS で唯一使用できるオプションでもあります。ここで選択すると、2 つのプラットフォームのパリティが向上し、2 つのプラットフォームの違いをデバッグしやすくなります(両方をビルドする場合)。
  2. アプリをビルドします。[File] > [Build Settings] で、以下の手順を行います。
    1. [Createsymbel.zip] がオンになっていることを確認します(プルダウンが表示された場合は [デバッグ] を選択します)。
    2. 作成した Builds/Android サブフォルダに Unity エディタから直接 APK をビルドします。
  3. ビルドが完了したら、Crashlytics 互換のシンボル ファイルを生成し、Firebase サーバーにアップロードする必要があります。このシンボル情報は、Crashlytics ダッシュボードにネイティブ ライブラリのクラッシュのシンボリケートされたスタック トレースを表示するために必要です。

    次の Firebase CLI コマンドを実行して、このシンボル ファイルを生成してアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
    • FIREBASE_APP_ID: Firebase Android アプリ ID(パッケージ名ではありません)。この値は、前にダウンロードした google-services.json ファイルにあります。mobilesdk_app_id 値です。
      Firebase Android アプリ ID の例: 1:567383003300:android:17104a2ced0c9b9b
    • PATH/TO/SYMBOLS: ビルドの完了時に Builds/Android ディレクトリに生成された、圧縮されたシンボル ファイルのパス(例: Builds/Android/myapp-1.0-v100.symbols.zip)。

強制的にテスト クラッシュを発生させて設定を完了する

Crashlytics の設定を完了し、Firebase コンソールの Crashlytics ダッシュボードで初期データを表示するには、強制的にテスト クラッシュを発生させる必要があります。

  1. MainGameScene のエディタの [Hierarchy] で EmptyObjectGameObject を探し、次のスクリプトを追加してシーンを保存します。このスクリプトは、アプリの実行から数秒後にテスト クラッシュを発生させます。
    using System;
    using UnityEngine;
    
    public class CrashlyticsTester : MonoBehaviour {
        // Update is called once per frame
        void Update()
        {
            // Tests your Crashlytics implementation by
            // throwing an exception every 60 frames.
            // You should see reports in the Firebase console
            // a few minutes after running your app with this method.
            if(Time.frameCount >0 && (Time.frameCount%60) == 0)
            {
                throw new System.Exception("Test exception; please ignore");
            }
        }
    }
    
  2. アプリをビルドし、ビルドの完了後にシンボル情報をアップロードします。
    • iOS: Firebase Unity Editor プラグインが、シンボル ファイルをアップロードするように Xcode プロジェクトを自動的に構成します。
    • Android: Firebase CLI の crashlytics:symbols:upload コマンドを実行して、シンボル ファイルをアップロードします。
  3. アプリを実行します。アプリを実行したら、デバイスログを監視し、CrashlyticsTester で例外がトリガーされるのを待ちます。
    • iOS: Xcode の下部ペインでログを確認します。
    • Android: ターミナルで adb logcat コマンドを実行してログを表示します。
  4. Crashlytics ダッシュボードにアクセスして、例外を確認してください。問題は、ダッシュボードの下部にある [問題] テーブルに表示されます。これらのレポートの確認方法については、この Codelab の後半で詳しく説明します。
  5. イベントが Crashlytics にアップロードされたことを確認したら、添付した EmptyObject GameObject を選択し、CrashlyticsTester コンポーネントのみを削除してから、シーンを保存して元の状態に戻します。

5. デバッグ メニューを有効にして理解する

ここまでで、Unity プロジェクトに Crashlytics を追加して設定を完了し、Crashlytics SDK が Firebase にイベントをアップロードすることを確認しました。次に、Unity プロジェクトにメニューを作成します。これにより、ゲームでより高度な Crashlytics 機能を使用する方法を実演できます。Level Up with Firebase Unity プロジェクトには、非表示のデバッグ メニューがすでに用意されています。このメニューを表示し、機能を記述します。

デバッグ メニューを有効にする

Unity プロジェクトに [デバッグ] メニューにアクセスするためのボタンがありますが、現在有効になっていません。MainMenu プレハブからアクセスするには、ボタンを有効にする必要があります。

  1. Unity Editor で、MainMenu という名前のプレハブを開きます。4148538cbe9f36c5.png
  2. プレハブ階層で、DebugMenuButton という名前の無効なサブオブジェクトを見つけて選択します。816f8f9366280f6c.png
  3. DebugMenuButton を含むテキスト フィールドの左上にあるチェックボックスをオンにして、DebugMenuButton を有効にします。8a8089d2b4886da2.png
  4. プレハブを保存します。
  5. エディタまたはデバイスでゲームを実行します。メニューにアクセスできるようになるはずです。

デバッグ メニューのメソッド本体のプレビューと理解

この Codelab の後半では、事前構成済みのデバッグ Crashlytics メソッドのメソッド本文を記述します。ただし、Level Up with Firebase Unity プロジェクトでは、メソッドは DebugMenu.cs で定義され、DebugMenu.cs から呼び出されます。

これらのメソッドの一部は Crashlytics メソッドを呼び出してエラーをスローしますが、Crashlytics がこれらのエラーをキャッチできるかどうかは、これらのメソッドを最初に呼び出すかどうかに依存しません。代わりに、これらのメソッドによって追加された情報によって、エラーを自動的に検出することによって生成されるクラッシュ レポートが強化されます。

DebugMenu.cs を開き、次のメソッドを探します。

Crashlytics の問題を生成してアノテーションを付ける方法:

  • CrashNow
  • LogNonfatalError
  • LogStringsAndCrashNow
  • SetAndOverwriteCustomKeyThenCrash
  • SetLogsAndKeysBeforeANR

デバッグに役立つアナリティクス イベントをロギングする方法:

  • LogProgressEventWithStringLiterals
  • LogIntScoreWithBuiltInEventAndParams

この Codelab の後のステップでは、これらのメソッドを実装し、ゲーム開発で起こりうる特定の状況に対処するのにどのように役立つかを学びます。

6. 開発中にクラッシュ レポートを確実に提供

これらのデバッグ方法を実装してクラッシュ レポートに与える影響を確認する前に、Crashlytics へのイベントの報告方法を理解しておいてください。

Unity プロジェクトの場合、ゲームのクラッシュと例外のイベントがすぐにディスクに書き込まれます。ゲームをクラッシュさせないキャッチされなかった例外(ゲームロジックでキャッチされなかった C# 例外など)については、Unity プロジェクトで Crashlytics を初期化する Crashlytics.ReportUncaughtExceptionsAsFatal プロパティを true に設定して、Crashlytics SDK で致命的なイベントとして報告できます。これらのイベントは、エンドユーザーがゲームを再起動しなくても、Crashlytics にリアルタイムで報告されます。ネイティブ クラッシュは常に致命的なイベントとして報告され、エンドユーザーがゲームを再起動すると送信されます。

また、Crashlytics 情報を Firebase に送信する方法は、ランタイム環境の種類ごとに異なりますが、次のような違いに注意してください。

iOS シミュレータ:

  • Crashlytics 情報は、シミュレータから Xcode を接続解除した場合にのみ報告されます。Xcode がアタッチされている場合は、アップストリームでエラーが検出されるため、情報が配信されなくなります。

モバイル実機(Android と iOS):

  • Android 固有: ANR は Android 11 以降でのみ報告されます。ANR と致命的でないイベントは、次回の実行時に報告されます。

Unity エディタ:

CrashNow() のボタンをタップしてゲームのクラッシュをテストする

ゲームで Crashlytics をセットアップすると、Crashlytics SDK はクラッシュとキャッチされなかった例外を自動的に記録し、分析のために Firebase にアップロードします。レポートは Firebase コンソールの Crashlytics ダッシュボードに表示されます。

  1. これが本当に自動であることを示すために、DebugMenu.cs を開き、次のようにメソッド CrashNow() を上書きします。
    void CrashNow()
    {
        TestCrash();
    }
    
  2. アプリをビルドします。
  3. (Android のみ)次の Firebase CLI コマンドを実行して、シンボルをアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. [Crash Now] ボタンをタップし、この Codelab の次のステップに進んで、クラッシュ レポートを表示および解釈する方法を確認します。

7. Firebase コンソールの問題レポートについて

クラッシュ レポートを最大限に活用するには、もう少し知識が必要です。作成する各メソッドでは、Crashlytics レポートにさまざまな種類の情報を追加する方法が示されています。

  1. [Crash Now] ボタンをタップして、アプリを再起動します。
  2. Crashlytics ダッシュボードに移動します。ダッシュボードの下部にある [Issues] テーブルまで下にスクロールします。Crashlytics は、同じ根本原因を持つすべてのイベントを「問題」にグループ化します。
  3. [問題] の表にある新しい問題をクリックします。これにより、Firebase に送信された各イベントに関するイベントの概要が表示されます。

    次のようなスクリーンキャップが表示されます。[イベントの概要] には、クラッシュにつながった呼び出しのスタック トレースが目立つように表示されています。40c96abe7f90c3aa.png

追加のメタデータ:

もう 1 つの便利なタブは [Unity のメタデータ] タブです。このセクションでは、物理的な特徴、CPU のモデルや仕様、GPU の各種指標など、イベントが発生したデバイスの属性を確認できます。

このタブの情報が役立つ例を次に示します。
ゲームで特定の外観を実現するためにシェーダーを多用しているものの、すべてのスマートフォンにこの機能をレンダリングできる GPU が搭載されていないとします。[Unity メタデータ] タブの情報を見ると、どの機能を自動的に利用できるようにするか、完全に無効にするかを判断する際に、アプリでどのハードウェアをテストするべきかがわかります。

お使いのデバイスでバグやクラッシュが発生することはないかもしれませんが、Android デバイスは多種多様であるため、対象デバイスの特定の「アクセス ポイント」をより深く理解するのに役立ちます

41d8d7feaa87454d.png

8. 例外のスロー、キャッチ、ロギング

多くの場合、デベロッパーがランタイム例外を適切にキャッチして処理しても、どのような状況で発生しているかをメモしておくとよいでしょう。Crashlytics.LogException はまさにこの目的に使用できます。つまり、Firebase に例外イベントを送信して、Firebase コンソールで問題をさらにデバッグできるようになります。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs で、using ステートメントに次を追加します。
    // Import Firebase
    using Firebase.Crashlytics;
    
  2. 引き続き DebugMenu.cs で、次のように LogNonfatalError() を上書きします。
    void LogNonfatalError()
    {
        try
        {
            throw new System.Exception($"Test exception thrown in {nameof(LogNonfatalError)}");
        }
        catch(System.Exception exception)
        {
            Crashlytics.LogException(exception);
        }
    }
    
  3. アプリをビルドします。
  4. (Android のみ)次の Firebase CLI コマンドを実行して、シンボルをアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  5. [致命的でないエラーをログに記録] ボタンをタップして、アプリを再起動します。
  6. Crashlytics ダッシュボードに移動すると、この Codelab の最後のステップで説明したようなダッシュボードが表示されます。
  7. ただし、今回は [Event type] フィルタを [Non-fatals] に制限して、先ほどログに記録したような致命的でないエラーのみが表示されるようにします。
    a39ea8d9944cbbd9.png

9. プログラムの実行フローをより深く理解するために、文字列を Crashlytics に記録

複数のパスから 1 セッションあたり数百、あるいは数千回も呼び出されるコード行が、突然例外やクラッシュを発生させる原因を突き止めたいと思ったことはありませんか?IDE でコードを順に確認して値を詳しく確認するのは良いことですが、もしこれがごく一部のユーザーだけに当てはまるとしたらどうでしょうか。さらに悪いことに、何をしてもこのクラッシュを再現できない場合はどうすればよいでしょうか。

そのような状況では、背景情報を把握しておくことが大きな変化をもたらします。Crashlytics.Log を使用すると、必要なコンテキストを書き出すことができます。これらのメッセージは、今後何が起こるかについて、将来の自分を示唆するヒントと考えてください。

ログにはさまざまな用途がありますが、一般的には、コールの順序や不在が極めて重要な情報である場合にログが役立ちます。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs で、次のように LogStringsAndCrashNow() を上書きします。
    void LogStringsAndCrashNow()
    {
        Crashlytics.Log($"This is the first of two descriptive strings in {nameof(LogStringsAndCrashNow)}");
        const bool RUN_OPTIONAL_PATH = false;
        if(RUN_OPTIONAL_PATH)
        {
            Crashlytics.Log(" As it stands, this log should not appear in your records because it will never be called.");
        }
        else
        {
            Crashlytics.Log(" A log that will simply inform you which path of logic was taken. Akin to print debugging.");
        }
        Crashlytics.Log($"This is the second of two descriptive strings in {nameof(LogStringsAndCrashNow)}");
        TestCrash();
    }
    
  2. アプリをビルドします。
  3. (Android のみ)次の Firebase CLI コマンドを実行して、シンボルをアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. [Log Strings and Crash Now] ボタンをタップして、アプリを再起動します。
  5. Crashlytics ダッシュボードに戻り、[Issues] テーブルに表示されている最新の問題をクリックします。ここでも、以前の問題と似た画面が表示されます。
    7aabe103b8589cc7.png
  6. ただし、[イベントの概要] で [ログ] タブをクリックすると、次のようなビューが表示されます。
    4e27aa407b7571cf.png

10. カスタムキーの書き込みと上書き

たとえば、少数の値や構成に設定された変数に対応するクラッシュについて理解を深めたいとします。いつでも、変数と候補となる値の組み合わせに基づいてフィルタリングできると便利です。

Crashlytics では、任意の文字列をログに記録するだけでなく、クラッシュしたプログラムの正確な状態を知ることが有益である場合に、別のデバッグ形式としてカスタムキーを提供しています。

セッションに対して設定できる Key-Value ペアです。蓄積される純粋に付加的なログとは異なり、変数または条件の最新ステータスのみを反映するようにキーを上書きできます。

これらのキーは、プログラムについて最後に記録された状態の台帳になるだけでなく、Crashlytics の問題の強力なフィルタとしても使用できます。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs で、次のように SetAndOverwriteCustomKeyThenCrash() を上書きします。
    void SetAndOverwriteCustomKeyThenCrash()
    {
        const string CURRENT_TIME_KEY = "Current Time";
        System.TimeSpan currentTime = System.DateTime.Now.TimeOfDay;
        Crashlytics.SetCustomKey(
            CURRENT_TIME_KEY,
            DayDivision.GetPartOfDay(currentTime).ToString() // Values must be strings
            );
    
        // Time Passes
        currentTime += DayDivision.DURATION_THAT_ENSURES_PHASE_CHANGE;
    
        Crashlytics.SetCustomKey(
            CURRENT_TIME_KEY,
            DayDivision.GetPartOfDay(currentTime).ToString()
            );
        TestCrash();
    }
    
  2. アプリをビルドします。
  3. (Android のみ)次の Firebase CLI コマンドを実行して、シンボルをアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. [カスタムキーとクラッシュを設定] ボタンをタップして、アプリを再起動します。
  5. Crashlytics ダッシュボードに戻り、[Issues] テーブルに表示されている最新の問題をクリックします。ここでも、以前の問題と似た画面が表示されます。
  6. ただし、今回は [イベント サマリー] の [キー] タブをクリックして、Current Time を含むキーの値を表示します。
    7dbe1eb00566af98.png

カスタムログではなくカスタムキーを使用する理由は何ですか。

  • ログは順次データを保存するのに適していますが、最新の値のみが必要な場合はカスタムキーのほうが適しています。
  • Firebase コンソールでは、[Issues] テーブルの検索ボックスのキーの値で問題を簡単にフィルタできます。

ただし、ログと同様に、カスタムキーにも上限があります。Crashlytics では、最大 64 個の Key-Value ペアがサポートされています。このしきい値に達すると、それ以上値が保存されなくなります。各 Key-Value ペアのサイズは最大 1 KB です。

11. (Android のみ)カスタムキーとログを使用して ANR を把握、診断する

Android デベロッパーにとってデバッグが難しい問題の 1 つに、アプリケーション応答なし(ANR)エラーがあります。ANR は、アプリが入力に 5 秒以上応答しなかった場合に発生します。この場合、アプリがフリーズしたか、動作が非常に遅くなっています。ユーザーにはダイアログが表示され、[待機] または [アプリを閉じる] を選択できます。

上記の ANR のリンク先に記載されているように、ANR はユーザー エクスペリエンスに悪影響を与えるため、Google Play ストアでのアプリの見つけやすさに影響する可能性があります。ANR は複雑で、スマートフォンモデルによって動作が大きく異なるマルチスレッド コードによって引き起こされることが多いため、デバッグ中に ANR を再現することは、ほぼ不可能ではないにしても非常に困難であることがよくあります。したがって、通常は分析的かつ推定的にアプローチするのが最善のアプローチです。

この方法では、Crashlytics.LogExceptionCrashlytics.LogCrashlytics.SetCustomKey を組み合わせて使用して、問題の自動ロギングを補完し、詳細情報を提供します。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs で、次のように SetLogsAndKeysBeforeANR() を上書きします。
    void SetLogsAndKeysBeforeANR()
    {
        System.Action<string,long> WaitAndRecord =
        (string methodName, long targetCallLength)=>
        {
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            const string CURRENT_FUNCTION = "Current Async Function";
    
            // Initialize key and start timing
            Crashlytics.SetCustomKey(CURRENT_FUNCTION, methodName);
            stopWatch.Start();
    
            // The actual (simulated) work being timed.
            BusyWaitSimulator.WaitOnSimulatedBlockingWork(targetCallLength);
    
            // Stop timing
            stopWatch.Stop();
    
            if(stopWatch.ElapsedMilliseconds>=BusyWaitSimulator.EXTREME_DURATION_MILLIS)
            {
              Crashlytics.Log($"'{methodName}' is long enough to cause an ANR.");
            }
            else if(stopWatch.ElapsedMilliseconds>=BusyWaitSimulator.SEVERE_DURATION_MILLIS)
            {
              Crashlytics.Log($"'{methodName}' is long enough it may cause an ANR");
            }
        };
    
        WaitAndRecord("DoSafeWork",1000L);
        WaitAndRecord("DoSevereWork",BusyWaitSimulator.SEVERE_DURATION_MILLIS);
        WaitAndRecord("DoExtremeWork",2*BusyWaitSimulator.EXTREME_DURATION_MILLIS);
    }
    
  2. アプリをビルドします。
  3. 次の Firebase CLI コマンドを実行して、シンボルをアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. [ログとキーを設定] → [ANR] ボタンをタップして、アプリを再起動します。
  5. Crashlytics ダッシュボードに戻り、[問題] テーブルで新しい問題をクリックして、イベントの概要を表示します。呼び出しが正常に終了すると、次のように表示されます。
    876c3cff7037bd07.png

    Firebase は、アプリが ANR をトリガーした主な理由として、スレッドのビジー待機を特定しました。
  6. [イベントの概要] の [ログ] タブのログを見ると、完了として記録された最後のメソッドが DoSevereWork であることがわかります。
    5a4bec1cf06f6984.png
    一方、開始としてリストされている最後のメソッドは DoExtremeWork です。これは、このメソッドの実行中に ANR が発生し、DoExtremeWork がログに記録される前にゲームが終了したことを示します。

    89d86d5f598ecf3a.png

メリット

  • ANR の再現は非常に困難であるため、推定で見つけるにはコード領域と指標に関する豊富な情報を取得できることが非常に重要です。
  • カスタムキーに保存された情報を使用して、実行に時間がかかった非同期スレッドと、ANR をトリガーする危険性のある非同期スレッドを把握できます。この種の関連する論理データと数値データにより、コード内の最適化が最も必要な場所がわかります。

12. アナリティクス イベントを分散させてレポートをさらに充実させる

以下のメソッドも [Debug] メニューから呼び出すことができますが、問題自体を生成するのではなく、ゲームの動作をより深く理解するための別の情報源として Google アナリティクスを使用します。

この Codelab で作成した他のメソッドとは異なり、これらのメソッドは他のメソッドと組み合わせて使用する必要があります。これらのメソッドは、他のメソッドを実行する前に、任意の順序で(デバッグメニューの対応するボタンを押して)呼び出します。特定の Crashlytics の問題の情報を調べると、アナリティクス イベントの順序付きログが表示されます。ゲームでこのデータを使用すると、アプリの計測方法に応じて、プログラム フローやユーザー入力の組み合わせをより深く理解できます。

  1. Assets/Hamster/Scripts/States/DebugMenu.cs で、次のメソッドの既存の実装を上書きします。
    public void LogProgressEventWithStringLiterals()
    {
          Firebase.Analytics.FirebaseAnalytics.LogEvent("progress", "percent", 0.4f);
    }
    
    public void LogIntScoreWithBuiltInEventAndParams()
    {
          Firebase.Analytics.FirebaseAnalytics
            .LogEvent(
              Firebase.Analytics.FirebaseAnalytics.EventPostScore,
              Firebase.Analytics.FirebaseAnalytics.ParameterScore,
              42
            );
    }
    
  2. ゲームをビルドしてデプロイし、デバッグ メニューに入ります。
  3. (Android のみ)次の Firebase CLI コマンドを実行して、シンボルをアップロードします。
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. 以下のボタンを 1 回以上押して、上記の関数を呼び出します。
    • ログ文字列イベント
    • ログイン イベント
  5. [Crash Now] ボタンを押します。
  6. ゲームを再起動して、Firebase にクラッシュ イベントをアップロードします。
  7. アナリティクス イベントの任意のシーケンスをログに記録した後、Crashlytics がレポートを生成するイベントをゲームで生成すると(先ほどと同様に)、Crashlytics の [イベントの概要] の [ログ] タブに追加されます。
    d3b16d78f76bfb04.png

13. 今後

これで、自動生成されるクラッシュ レポートを補完するための、より優れた理論的基盤が得られます。この新しい情報により、現在の状態、過去のイベントの記録、既存の Google アナリティクス イベントを使用して、結果につながった一連のイベントとロジックをより的確に分類できます。

Android 11(API レベル 30)以降をターゲットとするアプリの場合は、GWP-ASan を組み込むことを検討してください。GWP-ASan は、use-after-freeheap-buffer-overflow のバグなどのネイティブ メモリエラーによるクラッシュのデバッグに役立つ、ネイティブ メモリ アロケータ機能です。このデバッグ機能を利用するには、GWP-ASan を明示的に有効にする必要があります。

次のステップ

Codelab「Unity ゲームを Remote Config で計測する」に進み、Unity で Remote Config と A/B Testing を使用する方法について学習します。