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

1. はじめに

このコードラボでは、クラッシュとその原因となった可能性のある状況をよりよく把握できるようにする 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 コードラボで使用されているため、このセクションのタスクをすでに完了している可能性があることに注意してください。その場合は、このページの最後のステップ「Unity 用の Firebase SDK を追加する」に直接進むことができます。

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

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

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

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

Unityエディターで「Level Up with Firebase」を開きます

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

Unity のインストールと使用の詳細については、 「Unity での作業」を参照してください。

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

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

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

アプリを Firebase に登録する

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

Firebase設定ファイルを追加する

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

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

Unity 用の Firebase SDK を追加する

  1. Firebase コンソールで[Firebase Unity SDK をダウンロード]をクリックします。
  2. SDK を適当な場所に解凍します。
  3. 開いている Unity プロジェクトで、 [Assets] > [Import Package] > [Custom Package]に移動します。
  4. [パッケージのインポート]ダイアログで、解凍された SDK が含まれるディレクトリに移動し、 FirebaseAnalytics.unitypackageを選択して、 [開く]をクリックします。
  5. 表示される[Unity パッケージのインポート]ダイアログで、 [インポート]をクリックします。
  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()を呼び出して既存のStart()メソッドに Firebase の初期化を追加します。
    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 FAQで説明されています。

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

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

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

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

アンドロイド

  1. (各ビルドではなく、初期セットアップ時のみ)ビルドをセットアップします。
    1. プロジェクト ディレクトリのルートに (つまり、 Assetsディレクトリの兄弟として) Buildsという新しいフォルダーを作成し、 Androidというサブフォルダーを作成します。
    2. [ファイル] > [ビルド設定] > [プレーヤー設定] > [構成]で、[スクリプト バックエンド] を IL2CPP に設定します。
      • IL2CPP を使用すると、通常、ビルドが小さくなり、パフォーマンスが向上します。
      • IL2CPP は iOS でのみ利用可能なオプションでもあり、ここでこれを選択すると、2 つのプラットフォームの同等性が向上し、(両方をビルドすることを選択した場合) 2 つの違いのデバッグが簡単になります。
  2. アプリを構築します。 [ファイル] > [ビルド設定]で、次の手順を実行します。
    1. [シンボルの作成.zip] がオンになっていることを確認します (または、ドロップダウンが表示される場合は、 [デバッグ]を選択します)。
    2. Unity エディターから、先ほど作成したBuilds/Androidサブフォルダーに 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で、エディターHierarchyEmptyObject GameObject見つけ、次のスクリプトをそれに追加して、シーンを保存します。このスクリプトは、アプリを実行した数秒後にテスト クラッシュを引き起こします。
    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 ダッシュボードにアクセスしてください。これは、ダッシュボードの下部にある問題テーブルに表示されます。コードラボの後半では、これらのレポートを探索する方法について詳しく学習します。
  5. イベントが Crashlytics にアップロードされたことを確認したら、アタッチしたEmptyObject GameObjectを選択し、 CrashlyticsTesterコンポーネントのみを削除して、シーンを保存して元の状態に戻します。

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

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

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

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

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

デバッグ メニューのメソッド本体をプレビューして理解する

このコードラボの後半では、事前構成されたデバッグ Crashlytics メソッドのメソッド本体を作成します。ただし、 「Level Up with Firebase Unity」プロジェクトでは、メソッドはDebugMenu.csで定義され、そこから呼び出されます。

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

DebugMenu.csを開き、次のメソッドを見つけます。

Crashlytics の問題を生成し、注釈を付ける方法:

  • CrashNow
  • LogNonfatalError
  • LogStringsAndCrashNow
  • SetAndOverwriteCustomKeyThenCrash
  • SetLogsAndKeysBeforeANR

デバッグを支援するために Analytics イベントをログに記録する方法:

  • LogProgressEventWithStringLiterals
  • LogIntScoreWithBuiltInEventAndParams

このコードラボの後のステップでは、これらのメソッドを実装し、ゲーム開発で発生する可能性のある特定の状況に対処するのにそれらのメソッドがどのように役立つかを学びます。

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. [今すぐクラッシュ]ボタンをタップし、このコードラボの次のステップに進み、クラッシュ レポートを表示して解釈する方法を確認します。

7. Firebase コンソールの問題レポートを理解する

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

  1. [今すぐクラッシュ]ボタンをタップし、アプリを再起動します。
  2. Crashlytics ダッシュボードに移動します。ダッシュボードの下部にある問題テーブルまで下にスクロールします。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 ダッシュボードに移動すると、このコードラボの最後のステップで見たものと同様のものが表示されるはずです。
  7. ただし、今回は、イベント タイプフィルターを[非致命的]に制限して、先ほどログに記録したエラーなど、致命的ではないエラーのみが表示されるようにします。
    a39ea8d9944cbbd9.png

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

複数のパスから、セッションごとに何百回、あるいは何千回も呼び出されるコード行が、なぜ突然例外を生成したりクラッシュしたりするのかを考えたことはありますか? 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. [文字列をログして今すぐクラッシュ]ボタンをタップし、アプリを再起動します。
  5. Crashlytics ダッシュボードに戻り、 「問題」テーブルにリストされている最新の問題をクリックします。再度、以前の問題と同様の内容が表示されるはずです。
    7aabe103b8589cc7.png
  6. ただし、イベントの概要内の[ログ]タブをクリックすると、次のようなビューが表示されます。
    4e27aa407b7571cf.png

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

少数の値または構成に設定された変数に対応するクラッシュをよりよく理解したいとします。いつでも、調べている変数と考えられる値の組み合わせに基づいてフィルタリングできると便利かもしれません。

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

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

これらのキーは、プログラムの最後に記録された状態の台帳であるだけでなく、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 ダッシュボードに戻り、 「問題」テーブルにリストされている最新の問題をクリックします。再度、以前の問題と同様の内容が表示されるはずです。
  6. ただし今回は、イベント概要[キー]タブをクリックしてCurrent Time含むキーの値を表示できるようにします。
    7dbe1eb00566af98.png

カスタム ログではなくカスタム キーを使用するのはなぜですか?

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

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

11. (Android のみ) カスタム キーとログを使用して ANR を理解および診断する

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

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

このメソッドでは、 Crashlytics.LogExceptionCrashlytics.Log 、およびCrashlytics.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. Analytics イベントを散在させてレポートをさらに充実させる

次のメソッドも [デバッグ] メニューから呼び出すことができますが、問題自体を生成するのではなく、ゲームの動作をより深く理解するための別の情報源として Google Analytics を使用します。

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

  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 つを 1 回以上押して、上記の機能を呼び出します。
    • ログ文字列イベント
    • ログインイベント
  5. 「今すぐクラッシュ」ボタンを押します。
  6. ゲームを再起動して、クラッシュ イベントを Firebase にアップロードします。
  7. Analytics イベントのさまざまな任意のシーケンスをログに記録し、(先ほどと同様に) Crashlytics がレポートを作成するイベントをゲームに生成させると、次のように Crashlyticsイベント概要[ログ]タブに追加されます。
    d3b16d78f76bfb04.png

13. 今後の展開

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

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

次のステップ

Remote Config を使用して Unity ゲームをインストルメントするコードラボに進み、Unity での Remote Config と A/B テストの使用について学習します。