Comprender los bloqueos de un juego de Unity utilizando funciones avanzadas de Crashlytics,Comprender los bloqueos de un juego de Unity utilizando funciones avanzadas de Crashlytics

1. Introducción

En este codelab, aprenderá a utilizar funciones avanzadas de Crashlytics que le brindarán una mejor visibilidad de los bloqueos y las circunstancias que podrían haberlos causado.

Agregarás nuevas funciones a un juego de muestra, MechaHamster: Level Up with Firebase Edition . Este juego de muestra es una nueva versión del clásico juego de Firebase, MechaHamster, que elimina la mayor parte de su funcionalidad integrada de Firebase, lo que te brinda la oportunidad de implementar nuevos usos de Firebase en su lugar.

Agregarás un menú de depuración al juego. Este menú de depuración llama a los métodos que crearás y te permite ejercitar las diferentes funcionalidades de Crashlytics. Estos métodos le mostrarán cómo anotar sus informes automáticos de fallos con claves personalizadas, registros personalizados, errores no fatales y más.

Después de desarrollar el juego, utilizarás el menú de depuración e inspeccionarás los resultados para comprender la vista única que brindan sobre cómo se ejecuta tu juego en la naturaleza.

lo que aprenderás

  • Los tipos de errores que Crashlytics detecta automáticamente.
  • Errores adicionales que se pueden registrar intencionalmente.
  • Cómo agregar más información a estos errores para que sean más fáciles de entender.

Lo que necesitarás

  • Unity (versión mínima recomendada 2019+) con uno o ambos de los siguientes:
    • Soporte de compilación de iOS
    • Soporte de compilación de Android
  • (Solo para Android) Firebase CLI (utilizado para cargar símbolos para informes de fallos)

2. Configure su entorno de desarrollo

Las siguientes secciones describen cómo descargar el código Level Up with Firebase y abrirlo en Unity.

Tenga en cuenta que este juego de muestra Level Up with Firebase lo utilizan varios otros codelabs de Firebase + Unity, por lo que es posible que ya haya completado las tareas de esta sección. Si es así, puede ir directamente al último paso de esta página: "Agregar SDK de Firebase para Unity".

Descarga el código

Clona el repositorio GitHub de este codelab desde la línea de comando:

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

Alternativamente, si no tienes git instalado, puedes descargar el repositorio como un archivo ZIP .

Abra Level Up con Firebase en el editor de Unity

  1. Inicie Unity Hub y, desde la pestaña Proyectos , haga clic en la flecha desplegable junto a Abrir .
  2. Haga clic en Agregar proyecto desde el disco .
  3. Navegue hasta el directorio que contiene el código y luego haga clic en Aceptar .
  4. Si se le solicita, seleccione una versión del editor de Unity para usar y su plataforma de destino (Android o iOS).
  5. Haga clic en el nombre del proyecto, subir de nivel con firebase , y el proyecto se abrirá en el editor de Unity.
  6. Si su editor no lo abre automáticamente, abra MainGameScene en Activos > Hamster en la pestaña Proyecto del Editor de Unity.
    ff4ea3f3c0d29379.png

Para obtener más información sobre la instalación y el uso de Unity, consulte Trabajar en Unity .

3. Agrega Firebase a tu proyecto de Unity

Crear un proyecto de Firebase

  1. En Firebase console , haz clic en Agregar proyecto .
  2. Para crear un nuevo proyecto, ingrese el nombre del proyecto deseado.
    Esto también establecerá el ID del proyecto (que se muestra debajo del nombre del proyecto) en algo basado en el nombre del proyecto. Opcionalmente, puede hacer clic en el icono de edición en el ID del proyecto para personalizarlo aún más.
  3. Si se te solicita, revisa y acepta los términos de Firebase .
  4. Haga clic en Continuar .
  5. Seleccione la opción Habilitar Google Analytics para este proyecto y luego haga clic en Continuar .
  6. Seleccione una cuenta de Google Analytics existente para usar o seleccione Crear una cuenta nueva para crear una cuenta nueva.
  7. Haga clic en Crear proyecto .
  8. Cuando se haya creado el proyecto, haga clic en Continuar .

Registra tu aplicación con Firebase

  1. Aún en Firebase console , desde el centro de la página de descripción general del proyecto, haz clic en el ícono de Unity para iniciar el flujo de trabajo de configuración o, si ya agregaste una aplicación a tu proyecto de Firebase, haz clic en Agregar aplicación para mostrar las opciones de la plataforma.
  2. Seleccione para registrar los objetivos de compilación de Apple (iOS) y Android.
  3. Ingrese los ID específicos de la plataforma de su proyecto de Unity. Para este codelab, ingresa lo siguiente:
  4. (Opcional) Ingrese los apodos específicos de la plataforma de su proyecto de Unity.
  5. Haga clic en Registrar aplicación y luego vaya a la sección Descargar archivo de configuración .

Agregar archivos de configuración de Firebase

Después de hacer clic en Registrar aplicación , se le pedirá que descargue dos archivos de configuración (un archivo de configuración para cada objetivo de compilación). Tu proyecto de Unity necesita los metadatos de Firebase en estos archivos para conectarse con Firebase.

  1. Descargue ambos archivos de configuración disponibles:
    • Para Apple (iOS) : descargue GoogleService-Info.plist .
    • Para Android : descargue google-services.json .
  2. Abra la ventana Proyecto de su proyecto de Unity, luego mueva ambos archivos de configuración a la carpeta Activos .
  3. De vuelta en Firebase console, en el flujo de trabajo de configuración, haga clic en Siguiente y continúe con Agregar SDK de Firebase para Unity.

Agregar SDK de Firebase para Unity

  1. Haga clic en Descargar Firebase Unity SDK en Firebase console.
  2. Descomprima el SDK en algún lugar conveniente.
  3. En su proyecto Unity abierto, navegue hasta Activos > Importar paquete > Paquete personalizado .
  4. En el cuadro de diálogo Importar paquete , navegue hasta el directorio que contiene el SDK descomprimido, seleccione FirebaseAnalytics.unitypackage y luego haga clic en Abrir .
  5. En el cuadro de diálogo Importar paquete de Unity que aparece, haga clic en Importar .
  6. Repita los pasos anteriores para importar FirebaseCrashlytics.unitypackage .
  7. Regrese a Firebase console y, en el flujo de trabajo de configuración, haga clic en Siguiente .

Para obtener más información sobre cómo agregar SDK de Firebase a proyectos de Unity, consulte Opciones adicionales de instalación de Unity .

4. Configura Crashlytics en tu proyecto de Unity

Para usar Crashlytics en proyectos de Unity, deberá realizar algunos pasos de configuración más. Por supuesto, necesitarás inicializar el SDK. Pero también deberá cargar sus símbolos para poder ver los seguimientos de pila simbolizados en Firebase console y deberá forzar una prueba de bloqueo para asegurarse de que Firebase reciba sus eventos de bloqueo.

Inicializar el SDK de Crashlytics

  1. En Assets/Hamster/Scripts/MainGame.cs , agregue las siguientes instrucciones using :
    using Firebase.Crashlytics;
    using Firebase.Extensions;
    
    El primer módulo le permite usar métodos del SDK de Crashlytics y el segundo contiene algunas extensiones de la API de tareas de C# . Sin ambos using declaraciones, el siguiente código no funcionará.
  2. Aún en MainGame.cs , agregue la inicialización de Firebase al método Start() existente llamando a InitializeFirebaseAndStartGame() :
    void Start()
    {
      Screen.SetResolution(Screen.width / 2, Screen.height / 2, true);
      InitializeFirebaseAndStartGame();
    }
    
  3. Y nuevamente, en MainGame.cs , busque InitializeFirebaseAndStartGame() , declare una variable de aplicación y luego sobrescriba la implementación del método de esta manera:
    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");
          }
        });
    }
    

Colocar la lógica de inicialización aquí evita la interacción del jugador antes de que se inicialicen las dependencias de Firebase.

Los beneficios y efectos de informar excepciones no controladas como fatales se analizan en las preguntas frecuentes de Crashlytics .

Construye tu proyecto y sube símbolos

Los pasos para crear y cargar símbolos son diferentes para las aplicaciones de iOS y Android.

iOS+ (plataforma Apple)

  1. Desde el cuadro de diálogo Configuración de compilación , exporte su proyecto a un espacio de trabajo de Xcode.
  2. Crea tu aplicación.
    Para las plataformas Apple, el complemento Firebase Unity Editor configura automáticamente su proyecto Xcode para generar y cargar un archivo de símbolos compatible con Crashlytics en los servidores de Firebase para cada compilación. Esta información de símbolos es necesaria para ver seguimientos de pila simbolizados en el panel de Crashlytics.

Androide

  1. (solo durante la configuración inicial, no para cada compilación) Configure su compilación:
    1. Cree una nueva carpeta llamada Builds en la raíz del directorio de su proyecto (es decir, como un hermano de su directorio de Activos ) y luego cree una subcarpeta llamada Android .
    2. En Archivo > Configuración de compilación > Configuración del reproductor > Configuración , establezca Scripting Backend en IL2CPP.
      • IL2CPP generalmente hace que las compilaciones sean más pequeñas y tengan mejor rendimiento.
      • IL2CPP también es la ÚNICA opción disponible en iOS y seleccionarla aquí permite que las dos plataformas tengan una mejor paridad y simplifique las diferencias de depuración entre las dos (si elige compilar ambas).
  2. Crea tu aplicación. En Archivo > Configuración de compilación , complete lo siguiente:
    1. Asegúrese de que Crear símbolos.zip esté marcado (o si se presenta con un menú desplegable, seleccione Depurar ).
    2. Cree su APK directamente desde Unity Editor en la subcarpeta Builds/Android que acaba de crear.
  3. Una vez que haya finalizado la compilación, deberá generar un archivo de símbolos compatible con Crashlytics y cargarlo en los servidores de Firebase. Esta información de símbolos es necesaria para ver seguimientos de pila simbolizados para fallas de la biblioteca nativa en el panel de Crashlytics.

    Genere y cargue este archivo de símbolos ejecutando el siguiente comando de Firebase CLI :
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
    • FIREBASE_APP_ID : el ID de tu aplicación Firebase para Android (no el nombre de tu paquete). Busque este valor en el archivo google-services.json que descargó anteriormente. Es el valor mobilesdk_app_id .
      Ejemplo de ID de aplicación de Firebase para Android: 1:567383003300:android:17104a2ced0c9b9b
    • PATH/TO/SYMBOLS : la ruta del archivo de símbolos comprimido generado en el directorio Builds/Android cuando finalizó la compilación (por ejemplo: Builds/Android/myapp-1.0-v100.symbols.zip ).

Forzar un bloqueo de prueba para finalizar la configuración

Para terminar de configurar Crashlytics y ver los datos iniciales en el panel de Crashlytics de Firebase console, debes forzar una prueba de bloqueo.

  1. En MainGameScene, busque el objeto vacío GameObject en el editor Hierarchy , agréguele el siguiente script y luego guarde la escena. Este script provocará un bloqueo de la prueba unos segundos después de ejecutar su aplicación.
    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. Cree su aplicación y cargue información de símbolos una vez finalizada la compilación.
    • iOS : el complemento Firebase Unity Editor configura automáticamente su proyecto Xcode para cargar su archivo de símbolos.
    • Android : ejecute el comando crashlytics:symbols:upload Firebase CLI para cargar su archivo de símbolos.
  3. Ejecute su aplicación. Una vez que su aplicación se esté ejecutando, observe el registro del dispositivo y espere a que se active la excepción desde CrashlyticsTester .
    • iOS : vea los registros en el panel inferior de Xcode.
    • Android : vea registros ejecutando el siguiente comando en la terminal: adb logcat .
  4. ¡Visite el panel de Crashlytics para ver la excepción! Lo verá en la tabla Problemas en la parte inferior del panel. Más adelante en el codelab, aprenderá más sobre cómo explorar estos informes.
  5. Una vez que hayas confirmado que el evento se cargó en Crashlytics, selecciona el GameObject vacío al que lo adjuntaste, elimina solo el componente CrashlyticsTester y luego guarda la escena para restaurarla a su condición original.

5. Habilite y comprenda el menú de depuración.

Hasta ahora, agregó Crashlytics a su proyecto de Unity, finalizó la configuración y confirmó que el SDK de Crashlytics está cargando eventos en Firebase. Ahora crearás un menú en tu proyecto de Unity que demostrará cómo usar la funcionalidad Crashlytics más avanzada en tu juego. El proyecto Level Up with Firebase Unity ya tiene un menú de depuración oculto que harás visible y para el que escribirás la funcionalidad.

Habilitar el menú de depuración

El botón para acceder al menú Depurar existe en su proyecto de Unity, pero no está habilitado actualmente. Debes habilitar el botón para acceder desde el prefabricado MainMenu :

  1. En Unity Editor, abra el prefabricado llamado MainMenu . 4148538cbe9f36c5.png
  2. En la jerarquía prefabricada, busque el subobjeto deshabilitado llamado DebugMenuButton y luego selecciónelo. 816f8f9366280f6c.png
  3. Habilite DebugMenuButton marcando la casilla en la esquina superior izquierda a la izquierda del campo de texto que contiene DebugMenuButton . 8a8089d2b4886da2.png
  4. Guarde la casa prefabricada.
  5. Ejecute el juego en el editor o en su dispositivo. El menú ahora debería ser accesible.

Obtenga una vista previa y comprenda los cuerpos de los métodos para el menú de depuración.

Más adelante en este codelab, escribirás cuerpos de métodos para algunos métodos de depuración preconfigurados de Crashlytics. Sin embargo, en el proyecto Level Up with Firebase Unity, los métodos se definen y se llaman desde DebugMenu.cs .

Si bien algunos de estos métodos llamarán a métodos de Crashlytics y arrojarán errores, la capacidad de Crashlytics para detectar estos errores no depende de llamar a esos métodos primero. Más bien, los informes de fallos generados a partir de la detección automática de errores se mejorarán con la información agregada por estos métodos.

Abra DebugMenu.cs y luego busque los siguientes métodos:

Métodos para generar y anotar problemas de Crashlytics:

  • CrashNow
  • LogNonfatalError
  • LogStringsAndCrashNow
  • SetAndOverwriteCustomKeyThenCrash
  • SetLogsAndKeysBeforeANR

Métodos para registrar eventos de Analytics para ayudar en la depuración:

  • LogProgressEventWithStringLiterals
  • LogIntScoreWithBuiltInEventAndParams

En pasos posteriores de este codelab, implementará estos métodos y aprenderá cómo ayudan a abordar situaciones específicas que pueden ocurrir en el desarrollo de juegos.

6. Garantizar la entrega de informes de fallos en desarrollo.

Antes de comenzar a implementar estos métodos de depuración y ver cómo afectan los informes de fallos, asegúrese de comprender cómo se informan los eventos a Crashlytics.

Para proyectos de Unity, los eventos de fallas y excepciones en tu juego se escriben inmediatamente en el disco. Para las excepciones no detectadas que no bloquean el juego (por ejemplo, excepciones de C# no detectadas en la lógica del juego), puedes hacer que el SDK de Crashlytics las informe como eventos fatales configurando la propiedad Crashlytics.ReportUncaughtExceptionsAsFatal en true cuando inicializas Crashlytics en tu proyecto de Unity. . Estos eventos se informan a Crashlytics en tiempo real sin necesidad de que el usuario final reinicie el juego. Tenga en cuenta que los fallos nativos siempre se informan como eventos fatales y se envían cuando un usuario final reinicia el juego.

Además, tenga en cuenta las siguientes pequeñas (pero significativas) diferencias entre cómo los distintos entornos de ejecución envían información de Crashlytics a Firebase:

Simulador de iOS:

  • La información de Crashlytics se informa si y solo si desconecta Xcode del simulador. Si se adjunta Xcode, detecta los errores en sentido ascendente, impidiendo la entrega de información.

Dispositivos físicos móviles (Android e iOS):

  • Específico de Android: los ANR solo se informan en Android 11+. Los ANR y los eventos no fatales se informan en la siguiente ejecución.

Editor de unidad:

Prueba a bloquear tu juego con solo tocar un botón en CrashNow()

Después de configurar Crashlytics en tu juego, el SDK de Crashlytics registra automáticamente los fallos y las excepciones no detectadas y los sube a Firebase para su análisis. Y los informes se muestran en el panel de Crashlytics en Firebase console.

  1. Para demostrar que esto es realmente automático: abra DebugMenu.cs y luego sobrescriba el método CrashNow() de la siguiente manera:
    void CrashNow()
    {
        TestCrash();
    }
    
  2. Crea tu aplicación.
  3. (Solo Android) Cargue sus símbolos ejecutando el siguiente comando de Firebase CLI:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Toque el botón Crash Now y continúe con el siguiente paso de este codelab para descubrir cómo ver e interpretar el informe de fallos.

7. Comprender los informes de problemas en Firebase console

Cuando se trata de ver sus informes de fallos, hay un poco más que necesita saber sobre cómo aprovecharlos al máximo. Cada uno de los métodos que escriba mostrará cómo agregar diferentes tipos de información a los informes de Crashlytics.

  1. Toque el botón Crash Now y luego reinicie su aplicación.
  2. Vaya al panel de Crashlytics . Desplácese hacia abajo hasta la tabla Problemas en la parte inferior del panel, donde Crashlytics agrupa los eventos que tienen la misma causa raíz en "problemas".
  3. Haga clic en el nuevo problema que aparece en la tabla Problemas . Al hacer esto, se muestra el resumen del evento sobre cada evento individual que se envió a Firebase.

    Deberías ver algo como la siguiente captura de pantalla. Observe cómo el resumen del evento presenta de manera destacada el seguimiento de la pila de la llamada que provocó el bloqueo. 40c96abe7f90c3aa.png

Metadatos adicionales

Otra pestaña útil es la pestaña Metadatos de Unity . Esta sección le informa sobre los atributos del dispositivo en el que ocurrió el evento, incluidas las características físicas, el modelo/especificaciones de la CPU y todo tipo de métricas de GPU.

A continuación se muestra un ejemplo en el que la información de esta pestaña podría resultar útil:
Imagine que su juego hace un uso intensivo de sombreadores para lograr una apariencia determinada, pero no todos los teléfonos tienen GPU que sean capaces de representar esta función. La información en la pestaña Metadatos de Unity puede brindarle una mejor idea de qué hardware debe probar su aplicación al decidir qué funciones poner a disposición automáticamente o deshabilitar por completo.

Si bien es posible que nunca ocurra un error o falla en su dispositivo, debido a la enorme diversidad de dispositivos Android existentes, ayuda a comprender mejor los "puntos de acceso" particulares de los dispositivos de su audiencia.

41d8d7feaa87454d.png

8. Lanzar, capturar y registrar una excepción

A menudo, como desarrollador, incluso si su código detecta y maneja adecuadamente una excepción de tiempo de ejecución, es bueno tener en cuenta qué ocurrió y bajo qué circunstancias. Crashlytics.LogException se puede utilizar exactamente para este propósito: enviar un evento de excepción a Firebase para que puedas depurar aún más el problema en Firebase console.

  1. En Assets/Hamster/Scripts/States/DebugMenu.cs , agregue lo siguiente a las instrucciones using :
    // Import Firebase
    using Firebase.Crashlytics;
    
  2. Aún en DebugMenu.cs , sobrescriba LogNonfatalError() de la siguiente manera:
    void LogNonfatalError()
    {
        try
        {
            throw new System.Exception($"Test exception thrown in {nameof(LogNonfatalError)}");
        }
        catch(System.Exception exception)
        {
            Crashlytics.LogException(exception);
        }
    }
    
  3. Crea tu aplicación.
  4. (Solo Android) Cargue sus símbolos ejecutando el siguiente comando de Firebase CLI:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  5. Toque el botón Registrar error no fatal y luego reinicie su aplicación.
  6. Vaya al panel de Crashlytics y debería ver algo similar a lo que vio en el último paso de este codelab.
  7. Esta vez, sin embargo, restrinja el filtro de tipo de evento a No fatales para que solo vea errores no fatales, como el que acaba de registrar.
    a39ea8d9944cbbd9.png

9. Registre cadenas en Crashlytics para comprender mejor el flujo de ejecución del programa.

¿Alguna vez ha intentado descubrir por qué una línea de código que se llama desde múltiples rutas, cientos, si no miles, de veces por sesión, puede generar repentinamente una excepción o un bloqueo? Si bien podría ser bueno revisar el código en un IDE y observar los valores más de cerca, ¿qué pasa si esto sucede solo entre un porcentaje cada vez más pequeño de sus usuarios? Peor aún, ¿qué harías si no puedes replicar este accidente sin importar lo que hagas?

En situaciones como esta, tener algo de contexto puede marcar una gran diferencia. Con Crashlytics.Log , tienes la capacidad de escribir el contexto que necesitas. Piensa en estos mensajes como pistas para tu yo futuro sobre lo que podría estar pasando.

Si bien los registros se pueden utilizar de innumerables maneras, normalmente son más útiles para registrar situaciones en las que el orden y/o la ausencia de llamadas es un dato de vital importancia.

  1. En Assets/Hamster/Scripts/States/DebugMenu.cs , sobrescriba LogStringsAndCrashNow() de la siguiente manera:
    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. Crea tu aplicación.
  3. (Solo Android) Cargue sus símbolos ejecutando el siguiente comando de Firebase CLI:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Toque el botón Log Strings and Crash Now y luego reinicie su aplicación.
  5. Vuelva al panel de Crashlytics y haga clic en el problema más reciente que figura en la tabla Problemas . Nuevamente deberías ver algo similar a los números anteriores.
    7aabe103b8589cc7.png
  6. Sin embargo, si hace clic en la pestaña Registros dentro de un resumen de eventos , obtendrá una vista como esta:
    4e27aa407b7571cf.png

10. Escribe y sobrescribe una clave personalizada.

Supongamos que desea comprender mejor un bloqueo correspondiente a variables establecidas en una pequeña cantidad de valores o configuraciones. Sería bueno poder filtrar, según la combinación de variables y posibles valores que esté viendo, en un momento dado.

Además de registrar cadenas arbitrarias, Crashlytics ofrece otra forma de depuración cuando es beneficioso conocer el estado exacto de su programa cuando falla: claves personalizadas.

Estos son pares clave-valor que puede configurar para una sesión. A diferencia de los registros que se acumulan y son puramente aditivos, las claves se pueden sobrescribir para reflejar únicamente el estado más reciente de una variable o condición.

Además de ser un libro de contabilidad del último estado registrado de su programa, estas claves se pueden utilizar como filtros potentes para problemas de Crashlytics.

  1. En Assets/Hamster/Scripts/States/DebugMenu.cs , sobrescriba SetAndOverwriteCustomKeyThenCrash() de la siguiente manera:
    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. Crea tu aplicación.
  3. (Solo Android) Cargue sus símbolos ejecutando el siguiente comando de Firebase CLI:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Toque el botón Establecer clave personalizada y bloqueo y luego reinicie su aplicación.
  5. Vuelva al panel de Crashlytics y haga clic en el problema más reciente que figura en la tabla Problemas . Nuevamente deberías ver algo similar a los números anteriores.
  6. Esta vez, sin embargo, haga clic en la pestaña Claves en el resumen del evento para que pueda ver el valor de las claves, incluida Current Time :
    7dbe1eb00566af98.png

¿Por qué querrías utilizar claves personalizadas en lugar de registros personalizados?

  • Los registros son buenos para almacenar datos secuenciales, pero las claves personalizadas son mejores si solo desea el valor más reciente .
  • En Firebase console, puedes filtrar fácilmente los problemas por los valores de las claves en el cuadro de búsqueda de la tabla de problemas .

Sin embargo, al igual que los registros, las claves personalizadas tienen un límite. Crashlytics admite un máximo de 64 pares clave-valor. Una vez alcanzado este umbral, no se guardan valores adicionales. Cada par clave-valor puede tener un tamaño de hasta 1 KB.

11. (Solo Android) Utilice claves y registros personalizados para comprender y diagnosticar un ANR

Una de las clases de problemas más difíciles de depurar para los desarrolladores de Android es el error La aplicación no responde (ANR). Los ANR ocurren cuando una aplicación no responde a una entrada durante más de 5 segundos. Si esto sucede, significa que la aplicación se congeló o va muy lentamente. Se muestra un cuadro de diálogo a los usuarios y pueden elegir entre "Esperar" o "Cerrar aplicación".

Los ANR son una mala experiencia para el usuario y (como se menciona en el enlace ANR anterior) pueden afectar la visibilidad de su aplicación en Google Play Store. Debido a su complejidad, y debido a que a menudo son causados ​​por código multiproceso con comportamiento muy diferente en diferentes modelos de teléfono, reproducir ANR durante la depuración suele ser muy difícil, si no casi imposible. Como tal, abordarlos analítica y deductivamente suele ser el mejor enfoque.

En este método, usaremos una combinación de Crashlytics.LogException , Crashlytics.Log y Crashlytics.SetCustomKey para complementar el registro automático de problemas y brindarnos más información.

  1. En Assets/Hamster/Scripts/States/DebugMenu.cs , sobrescriba SetLogsAndKeysBeforeANR() de la siguiente manera:
    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. Crea tu aplicación.
  3. Cargue sus símbolos ejecutando el siguiente comando de Firebase CLI:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Toque el botón etiquetado Establecer registros y claves → ANR y luego reinicie su aplicación.
  5. Vuelva al panel de Crashlytics y luego haga clic en el nuevo problema en la tabla Problemas para ver el resumen del evento . Si la llamada se realizó correctamente, debería ver algo como esto:
    876c3cff7037bd07.png

    Como puede ver, Firebase identificó la espera ocupada en el hilo como la razón principal por la que su aplicación activó un ANR.
  6. Si observa los registros en la pestaña Registros del resumen de eventos , verá que el último método registrado como completo es DoSevereWork .
    5a4bec1cf06f6984.png

    Por el contrario, el último método listado como inicial es DoExtremeWork , lo que indica que el ANR ocurrió durante este método y el juego se cerró antes de que pudiera iniciar sesión en DoExtremeWork .

    89d86d5f598ecf3a.png

¿Por qué hacer esto?

  • Reproducir ANR es increíblemente difícil, por lo que poder obtener información valiosa sobre el área del código y las métricas es increíblemente importante para descubrirlo deductivamente.
  • Con la información almacenada en las claves personalizadas, ahora sabe qué subproceso asíncrono tardó más en ejecutarse y cuáles estaban en peligro de activar ANR. Este tipo de datos lógicos y numéricos relacionados le mostrarán en qué parte de su código es más necesario optimizar.

12. Intercalar eventos de Analytics para enriquecer aún más los informes

Los siguientes métodos también se pueden llamar desde el menú de depuración, pero en lugar de generar problemas por sí mismos, utilizan Google Analytics como otra fuente de información para comprender mejor el funcionamiento de tu juego.

A diferencia de los otros métodos que has escrito en este codelab, debes utilizar estos métodos en combinación con los demás. Llame a estos métodos (presionando su botón correspondiente en el menú de depuración) en el orden arbitrario que desee antes de ejecutar uno de los demás. Luego, cuando examine la información del problema específico de Crashlytics, verá un registro ordenado de eventos de Analytics. Estos datos se pueden utilizar en un juego para comprender mejor una combinación de flujo de programa o entrada del usuario, dependiendo de cómo haya instrumentado su aplicación.

  1. En Assets/Hamster/Scripts/States/DebugMenu.cs , sobrescriba las implementaciones existentes de los siguientes métodos:
    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. Crea e implementa tu juego y luego ingresa al menú de depuración .
  3. (Solo Android) Cargue sus símbolos ejecutando el siguiente comando de Firebase CLI:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Presione al menos uno de los siguientes botones una o más veces para llamar a las funciones anteriores:
    • Evento de cadena de registro
    • Evento de inicio de sesión
  5. Presione el botón Crash Now .
  6. Reinicia tu juego para que cargue el evento de bloqueo en Firebase.
  7. Cuando registras varias secuencias arbitrarias de eventos de Analytics y luego haces que tu juego genere un evento del cual Crashlytics crea un informe (como lo acabas de hacer), se agregan a la pestaña Registros del Resumen de eventos de Crashlytics de esta manera:
    d3b16d78f76bfb04.png

13. Avanzando

Y con eso, debería tener una mejor base teórica sobre la cual complementar sus informes de fallos generados automáticamente. Esta nueva información le permite utilizar el estado actual, los registros de eventos pasados ​​y los eventos existentes de Google Analytics para desglosar mejor la secuencia de eventos y la lógica que llevaron a su resultado.

Si su aplicación está orientada a Android 11 (nivel de API 30) o superior, considere incorporar GWP-ASan , una función de asignación de memoria nativa útil para depurar fallas causadas por errores de memoria nativa, como errores use-after-free y heap-buffer-overflow . Para aprovechar esta función de depuración, habilite explícitamente GWP-ASan .

Próximos pasos

Continúe con el codelab Instrumentar su juego Unity con Remote Config , donde aprenderá cómo usar Remote Config y las pruebas A/B en Unity.

,

1. Introducción

En este codelab, aprenderá a utilizar funciones avanzadas de Crashlytics que le brindarán una mejor visibilidad de los bloqueos y las circunstancias que podrían haberlos causado.

Agregarás nuevas funciones a un juego de muestra, MechaHamster: Level Up with Firebase Edition . Este juego de muestra es una nueva versión del clásico juego de Firebase, MechaHamster, que elimina la mayor parte de su funcionalidad integrada de Firebase, lo que te brinda la oportunidad de implementar nuevos usos de Firebase en su lugar.

Agregarás un menú de depuración al juego. Este menú de depuración llama a los métodos que crearás y te permite ejercitar las diferentes funcionalidades de Crashlytics. Estos métodos le mostrarán cómo anotar sus informes automáticos de fallos con claves personalizadas, registros personalizados, errores no fatales y más.

Después de desarrollar el juego, utilizarás el menú de depuración e inspeccionarás los resultados para comprender la vista única que brindan sobre cómo se ejecuta tu juego en la naturaleza.

lo que aprenderás

  • Los tipos de errores que Crashlytics detecta automáticamente.
  • Errores adicionales que se pueden registrar intencionalmente.
  • Cómo agregar más información a estos errores para que sean más fáciles de entender.

Lo que necesitarás

  • Unity (versión mínima recomendada 2019+) con uno o ambos de los siguientes:
    • Soporte de compilación de iOS
    • Soporte de compilación de Android
  • (For Android Only) The Firebase CLI (used to upload symbols for crash reports)

2. Set up your development environment

The following sections describe how to download the Level Up with Firebase code and open it in Unity.

Note that this Level Up with Firebase sample game is used by several other Firebase + Unity codelabs, so you might have already completed the tasks in this section. If so, you can go directly to the last step on this page: "Add Firebase SDKs for Unity".

Download the code

Clone this codelab's GitHub repository from the command line:

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

Alternatively, if you do not have git installed, you can download the repository as a ZIP file .

Open Level Up with Firebase in the Unity editor

  1. Launch the Unity Hub and, from the Projects tab, click the dropdown arrow next to Open .
  2. Click Add project from disk .
  3. Navigate to the directory that contains the code, and then click OK .
  4. If prompted, select a Unity editor version to use and your target platform (Android or iOS).
  5. Click on the project name, level-up-with-firebase , and the project will open in the Unity editor.
  6. If your editor does not automatically open it, open MainGameScene in Assets > Hamster in the Project tab of the Unity Editor.
    ff4ea3f3c0d29379.png

For more information about installing and using Unity, see Working in Unity .

3. Add Firebase to your Unity project

Create a Firebase project

  1. In the Firebase console , click Add project .
  2. To create a new project, enter the desired project name.
    This will also set the project ID (displayed below the project name) to something based on the project name. You can optionally click the edit icon on the project ID to further customize it.
  3. If prompted, review and accept the Firebase terms .
  4. Click Continue .
  5. Select the Enable Google Analytics for this project option, and then click Continue .
  6. Select an existing Google Analytics account to use or select Create a new account to create a new account.
  7. Click Create project .
  8. When the project has been created, click Continue .

Register your app with Firebase

  1. Still in the Firebase console , from the center of the project overview page, click the Unity icon to launch the setup workflow or, if you've already added an app to your Firebase project, click Add app to display the platform options.
  2. Select to register both the Apple (iOS) and Android build targets.
  3. Enter your Unity project's platform-specific ID(s). For this codelab, enter the following:
    • For Apple (iOS) : Enter com.google.firebase.level-up in the iOS bundle ID field.
    • For Android : Enter com.google.firebase.level_up in the Android package name field.
  4. (Optional) Enter your Unity project's platform-specific nickname(s).
  5. Click Register app , and then proceed to the Download config file section.

Add Firebase configuration files

After clicking Register app , you'll be prompted to download two configuration files (one config file for each build target). Your Unity project needs the Firebase metadata in these files to connect with Firebase.

  1. Download both available config files:
    • For Apple (iOS) : Download GoogleService-Info.plist .
    • For Android : Download google-services.json .
  2. Open the Project window of your Unity project, then move both config files into the Assets folder.
  3. Back in the Firebase console, in the setup workflow, click Next and proceed to Add Firebase SDKs for Unity.

Add Firebase SDKs for Unity

  1. Click Download Firebase Unity SDK in the Firebase console.
  2. Unzip the SDK somewhere convenient.
  3. In your open Unity Project, navigate to Assets > Import Package > Custom Package .
  4. In the Import package dialog, navigate to the directory that contains the unzipped SDK, select FirebaseAnalytics.unitypackage , and then click Open .
  5. From the Import Unity Package dialog that appears, click Import .
  6. Repeat the previous steps to import FirebaseCrashlytics.unitypackage .
  7. Return to the Firebase console and, in the setup workflow, click Next .

For more information about adding Firebase SDKs to Unity projects, see Additional Unity installation options .

4. Set up Crashlytics in your Unity project

To use Crashlytics in Unity projects, you'll need to do a few more setup steps. Of course, you'll need to initialize the SDK. But also, you'll need to upload your symbols so that you can see symbolicated stacktraces in the Firebase console, and you'll need to force a test crash to make sure that Firebase is getting your crash events.

Initialize the Crashlytics SDK

  1. In Assets/Hamster/Scripts/MainGame.cs , add the following using statements:
    using Firebase.Crashlytics;
    using Firebase.Extensions;
    
    The first module allows you to use methods from the Crashlytics SDK and the second contains some extensions to the C# Tasks API . Without both using statements the following code will not work.
  2. Still in MainGame.cs , add Firebase initialization to the existing Start() method by calling InitializeFirebaseAndStartGame() :
    void Start()
    {
      Screen.SetResolution(Screen.width / 2, Screen.height / 2, true);
      InitializeFirebaseAndStartGame();
    }
    
  3. And again, in MainGame.cs , find InitializeFirebaseAndStartGame() , declare an app variable, and then overwrite the method's implementation like so:
    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");
          }
        });
    }
    

Placing the initialization logic here prevents player interaction before the Firebase dependencies are initialized.

The benefits and effects of reporting unhandled exceptions as fatal are discussed in the Crashlytics FAQ .

Build your project and upload symbols

The steps for building and uploading symbols are different for iOS and Android apps.

iOS+ (Apple platform)

  1. From the Build Settings dialog, export your project to an Xcode workspace.
  2. Build your app.
    For Apple platforms, the Firebase Unity Editor plugin automatically configures your Xcode project to generate and upload a Crashlytics-compatible symbol file to Firebase servers for each build. This symbols information is required to see symbolicated stack traces in the Crashlytics dashboard.

Androide

  1. (only during initial setup, not for each build) Set up your build:
    1. Create a new folder called Builds at the root of your project directory (ie, as a sibling to your Assets directory), and then create a sub-folder called Android .
    2. In File > Build Settings > Player Settings > Configuration , set Scripting Backend to IL2CPP.
      • IL2CPP generally causes builds to be smaller and have better performance.
      • IL2CPP is also the ONLY available option on iOS and selecting it here allows the two platforms to be in better parity and make debugging differences between the two (if you choose to build both) simpler.
  2. Build your app. In File > Build Settings , complete the following:
    1. Make sure the Create symbols.zip is checked (or if presented with a dropdown, select Debugging ).
    2. Build your APK directly from the Unity Editor into the Builds/Android sub-folder that you just made.
  3. Once your build has finished, you need to generate a Crashlytics-compatible symbol file and upload it to Firebase servers. This symbols information is required to see symbolicated stack traces for native library crashes in the Crashlytics dashboard.

    Generate and upload this symbols file by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
    • FIREBASE_APP_ID : Your Firebase Android App ID (not your package name). Find this value in the google-services.json file that you downloaded earlier. It's the mobilesdk_app_id value.
      Example Firebase Android App ID: 1:567383003300:android:17104a2ced0c9b9b
    • PATH/TO/SYMBOLS : the path of the zipped symbol file generated in the Builds/Android directory when your build finished (for example: Builds/Android/myapp-1.0-v100.symbols.zip ).

Force a test crash to finish setup

To finish setting up Crashlytics and see initial data in the Crashlytics dashboard of the Firebase console, you need to force a test crash.

  1. In the MainGameScene find the EmptyObject GameObject in the editor Hierarchy , add the following script to it and then save the scene. This script will cause a test crash a few seconds after you run your app.
    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. Build your app and upload symbol information after your build finishes.
    • iOS : The Firebase Unity Editor plugin automatically configures your Xcode project to upload your symbol file.
    • Android : Run the Firebase CLI crashlytics:symbols:upload command to upload your symbol file.
  3. Run your app. Once your app is running, watch the device log and wait for the exception to trigger from the CrashlyticsTester .
    • iOS : View logs in the bottom pane of Xcode.
    • Android : View logs by running the following command in the terminal: adb logcat .
  4. Visit the Crashlytics dashboard to view the exception! You'll see it in the Issues table at the bottom of the dashboard. Later in the codelab, you'll learn more about how to explore these reports.
  5. Once you've confirmed the event was uploaded to Crashlytics, select the EmptyObject GameObject you attached it to, remove only the CrashlyticsTester component, and then save the scene to restore it to its original condition.

5. Enable and understand the Debug Menu

So far, you've added Crashlytics to your Unity project, finished setup, and confirmed that the Crashlytics SDK is uploading events to Firebase. You'll now create a menu in your Unity project which will demonstrate how to use more advanced Crashlytics functionality in your game. The Level Up with Firebase Unity project already has a hidden Debug Menu that you'll make visible and write the functionality for.

Enable the Debug Menu

The button to access the Debug Menu exists in your Unity project, but it's not currently enabled. You must enable the button to access it from the MainMenu prefab:

  1. In the Unity Editor, open the prefab named MainMenu . 4148538cbe9f36c5.png
  2. In the prefab hierarchy, find the disabled sub-object named DebugMenuButton , and then select it. 816f8f9366280f6c.png
  3. Enable the DebugMenuButton by checking the box in the upper-left corner to the left of the text field containing DebugMenuButton . 8a8089d2b4886da2.png
  4. Save the prefab.
  5. Run the game in either the editor or on your device. The menu should now be accessible.

Preview and understand the method bodies for the Debug Menu

Later in this codelab, you'll write method bodies for some preconfigured debug Crashlytics methods. In the Level Up with Firebase Unity project, though, the methods are defined in and called from DebugMenu.cs .

While some of these methods will both call Crashlytics methods and throw errors, the ability of Crashlytics to catch these errors does not depend on calling those methods first. Rather, the crash reports generated from automatically catching errors will be enhanced by the information added by these methods.

Open DebugMenu.cs , and then find the following methods:

Methods for generating and annotating Crashlytics issues:

  • CrashNow
  • LogNonfatalError
  • LogStringsAndCrashNow
  • SetAndOverwriteCustomKeyThenCrash
  • SetLogsAndKeysBeforeANR

Methods for logging Analytics events to aid in debugging:

  • LogProgressEventWithStringLiterals
  • LogIntScoreWithBuiltInEventAndParams

In later steps of this codelab, you will implement these methods and learn how they help address specific situations that can occur in game development.

6. Ensure delivery of crash reports in development

Before you start to implement these debug methods and see how they affect crash reports, make sure you understand how events are reported to Crashlytics.

For Unity projects, crash and exception events in your game are immediately written to disk. For uncaught exceptions that don't crash your game (for example, uncaught C# exceptions in game logic), you can have the Crashlytics SDK report them as fatal events by setting the Crashlytics.ReportUncaughtExceptionsAsFatal property to true where you initialize Crashlytics in your Unity project . These events are reported to Crashlytics in real-time without the need for an end-user to restart the game. Note that native crashes are always reported as fatal events and sent along when an end-user restarts the game.

In addition, be aware of the following small—but significant—differences between how the different runtime environments send Crashlytics information to Firebase:

iOS simulator:

  • Crashlytics information is reported if and only if you detach Xcode from the simulator. If Xcode is attached, it catches the errors upstream, preventing information delivery.

Mobile physical devices (Android and iOS):

  • Android-specific: ANRs are only reported on Android 11+. ANRs and non-fatal events are reported on the next run.

Unity Editor:

Test crashing your game at the touch of a button in CrashNow()

After Crashlytics is set up in your game, the Crashlytics SDK automatically records crashes and uncaught exceptions and uploads them to Firebase for analysis. And the reports are displayed in the Crashlytics dashboard in the Firebase console.

  1. To demonstrate that this is indeed automatic: open DebugMenu.cs , and then overwrite the method CrashNow() as follows:
    void CrashNow()
    {
        TestCrash();
    }
    
  2. Build your app.
  3. (Android Only) Upload your symbols by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Tap the Crash Now button, and proceed to the next step of this codelab to find out how to view and interpret the crash report.

7. Understand issue reports in the Firebase console

When it comes to viewing your crash reports, there's a little bit more you need to know about how to get the most out of them. Each of the methods you write will show how to add different types of information to Crashlytics reports.

  1. Tap the Crash Now button, and then restart your app.
  2. Go to the Crashlytics dashboard . Scroll down to the Issues table at the bottom of the dashboard where Crashlytics groups events that all have the same root cause into "issues".
  3. Click on the new issue that's listed in the Issues table. Doing this displays the Event summary about each individual event that was sent to Firebase.

    You should see something like the following screencap. Notice how the Event summary prominently features the stack trace of the call that led to the crash. 40c96abe7f90c3aa.png

Additional metadata

Another helpful tab is the Unity Metadata tab. This section informs you about the attributes of the device on which the event occurred, including physical features, the CPU model/specs, and all sorts of GPU metrics.

Here's an example where the information in this tab might be useful:
Imagine your game makes heavy use of shaders to achieve a certain look, but not all phones have GPUs that are capable of rendering this feature. The information in the Unity Metadata tab can give you a better idea of what hardware your app should test for when deciding what features to automatically make available or disable entirely.

While a bug or crash may never happen on your device, due to the massive diversity of Android devices in the wild, it helps to better understand the particular "hotspots" of your audience's devices.

41d8d7feaa87454d.png

8. Throw, catch, and log an exception

Oftentimes, as a developer, even if your code properly catches and handles a runtime exception, it's good to note that it occurred, and under what circumstances. Crashlytics.LogException can be used for this exact purpose—to send an exception event to Firebase so that you can further debug the issue in the Firebase console.

  1. In Assets/Hamster/Scripts/States/DebugMenu.cs , append the following to the using statements:
    // Import Firebase
    using Firebase.Crashlytics;
    
  2. Still in DebugMenu.cs , overwrite LogNonfatalError() as follows:
    void LogNonfatalError()
    {
        try
        {
            throw new System.Exception($"Test exception thrown in {nameof(LogNonfatalError)}");
        }
        catch(System.Exception exception)
        {
            Crashlytics.LogException(exception);
        }
    }
    
  3. Build your app.
  4. (Android Only) Upload your symbols by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  5. Tap the Log Nonfatal Error button, and then restart your app.
  6. Go to the Crashlytics dashboard , and you should see something similar to what you saw in the last step of this codelab.
  7. This time, though, restrict the Event type filter to Non-fatals so that you're only viewing non-fatal errors, such as the one you just logged.
    a39ea8d9944cbbd9.png

9. Log strings to Crashlytics to better understand the flow of program execution

Have you ever tried to figure out why a line of code that gets called from multiple paths, hundreds if not thousands of times per session, can suddenly generate an exception or crash? While it might be nice to step through the code in an IDE and look at the values more closely, what if this happens only among a vanishingly small percentage of your users? Even worse, what would you do if you can't replicate this crash no matter what you do?

In situations like this, having some context can make a world of difference. With Crashlytics.Log , you have the ability to write out the context you need. Think of these messages as hints to your future self about what might be going on.

While logs can be used in myriad ways, they are typically most helpful for recording situations where the order and/or absence of calls is a vitally important piece of information.

  1. In Assets/Hamster/Scripts/States/DebugMenu.cs , overwrite LogStringsAndCrashNow() as follows:
    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. Build your app.
  3. (Android Only) Upload your symbols by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Tap the Log Strings and Crash Now button, and then restart your app.
  5. Go back to the Crashlytics dashboard , and click into the newest issue listed in the Issues table. Again you should see something similar to the previous issues.
    7aabe103b8589cc7.png
  6. However, if you click the Logs tab within an Event summary , you get a view like this:
    4e27aa407b7571cf.png

10. Write and overwrite a custom key

Let's say you want to better understand a crash corresponding to variables set to a small number of values or configurations. It might be nice to be able to filter, based on the combination of variables and possible values you are looking at, at any given time.

On top of logging arbitrary strings, Crashlytics offers another form of debugging when it is beneficial to know the exact state of your program as it crashed: custom keys.

These are key-value pairs that you can set for a session. Unlike logs which accumulate and are purely additive, keys can be overwritten to only reflect the most recent status of a variable or condition.

In addition to being a ledger of the last recorded state of your program, these keys can then be used as powerful filters for Crashlytics issues.

  1. In Assets/Hamster/Scripts/States/DebugMenu.cs , overwrite SetAndOverwriteCustomKeyThenCrash() as follows:
    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. Build your app.
  3. (Android Only) Upload your symbols by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Tap the Set Custom Key and Crash button, and then restart your app.
  5. Go back to the Crashlytics dashboard , and click into the newest issue listed in the Issues table. Again you should see something similar to the previous issues.
  6. This time, though, click the Keys tab in the Event summary so that you can view the value of keys including Current Time :
    7dbe1eb00566af98.png

Why would you want to use custom keys instead of custom logs?

  • Logs are good at storing sequential data, but custom keys are better if you only want the most recent value.
  • In the Firebase console, you can easily filter issues by the values of keys in the Issues table search box.

Similar to logs though, custom keys do have a limit. Crashlytics supports a maximum of 64 key-value pairs. After you reach this threshold, additional values are not saved. Each key-value pair can be up to 1 KB in size.

11. (Android only) Use custom keys and logs to understand and diagnose an ANR

One of the most difficult classes of issues to debug for Android developers is the Application Not Responding (ANR) error. ANRs occur when an app fails to respond to input for more than 5 seconds. If this happens, it means the app either froze, or is going very slowly. A dialog is shown to users, and they are able to choose whether to "Wait" or "Close App".

ANRs are a bad user experience and (as mentioned in the ANR link above) can affect your app's discoverability in the Google Play Store. Because of their complexity, and because they're often caused by multithreaded code with vastly different behavior on different phone models, reproducing ANRs while debugging is often very difficult, if not near impossible. As such, approaching them analytically and deductively is usually the best approach.

In this method, we will use a combination of Crashlytics.LogException , Crashlytics.Log and Crashlytics.SetCustomKey to supplement automatic issue logging and to give us more information.

  1. In Assets/Hamster/Scripts/States/DebugMenu.cs , overwrite SetLogsAndKeysBeforeANR() as follows:
    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. Build your app.
  3. Upload your symbols by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Tap the button labeled Set Logs And Keys → ANR , and then restart your app.
  5. Go back into the Crashlytics dashboard , and then click into the new issue in the Issues table to view the Event summary . If the call went through properly, you should see something like this:
    876c3cff7037bd07.png

    As you can see, Firebase pinpointed the busy wait on the thread as the main reason your app triggered an ANR.
  6. If you look at the logs in the Logs tab of the Event summary , you will see that the last method recorded as complete is DoSevereWork .
    5a4bec1cf06f6984.png

    By contrast, the last method listed as starting is DoExtremeWork , which indicates that the ANR occurred during this method, and the game closed before it could log DoExtremeWork .

    89d86d5f598ecf3a.png

¿Por qué hacer esto?

  • Reproducing ANRs is incredibly hard, so being able to get rich information about code area and metrics is incredibly important for finding it out deductively.
  • With the information stored in the custom keys, you now know which async thread took the longest to run, and which ones were in danger of triggering ANRs. This sort of related logical and numeric data will show you where in your code is most necessary to optimize.

12. Interspersing Analytics events to further enrich reports

The following methods are also callable from the Debug Menu, but instead of generating issues themselves, they use Google Analytics as another source of information to better understand the workings of your game.

Unlike the other methods you've written in this codelab, you should use these methods in combination with the others. Call these methods (by pressing their corresponding button in the Debug Menu) in whatever arbitrary order you want before running one of the others. Then, when you examine the information in the specific Crashlytics issue, you will see an ordered log of Analytics events. This data can be used in a game to better understand a combination of program flow or user input, depending on how you have instrumented your app.

  1. In Assets/Hamster/Scripts/States/DebugMenu.cs , overwrite the existing implementations of the following methods:
    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. Build and deploy your game, and then enter the Debug Menu .
  3. (Android Only) Upload your symbols by running the following Firebase CLI command:
    firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
    
  4. Press at least one of the following buttons one or more times to call the above functions:
    • Log String Event
    • Log Int Event
  5. Press the Crash Now button.
  6. Restart your game to have it upload the crash event to Firebase.
  7. When you log various arbitrary sequences of Analytics events and then have your game generate an event that Crashlytics creates a report from (as you just have), they get added to the Logs tab of the Crashlytics Event Summary like this:
    d3b16d78f76bfb04.png

13. Going forward

And with that, you should have a better theoretical basis on which to supplement your automatically-generated crash reports. This new information allows you to use the current state, records of past events, and existing Google Analytics events to better break down the sequence of events and logic that led to its outcome.

If your app targets Android 11 (API level 30) or higher, consider incorporating GWP-ASan , a native memory allocator feature useful for debugging crashes caused by native memory errors such as use-after-free and heap-buffer-overflow bugs. To take advantage of this debugging feature, explicitly enable GWP-ASan .

Próximos pasos

Proceed to the Instrument your Unity game with Remote Config codelab, where you'll learn about using Remote Config and A/B Testing in Unity.