{"id":159,"date":"2019-11-08T15:49:05","date_gmt":"2019-11-08T15:49:05","guid":{"rendered":"https:\/\/blog.mozilla.org\/data\/?p=159"},"modified":"2020-03-30T15:53:41","modified_gmt":"2020-03-30T15:53:41","slug":"instrumenting-android-crashes-with-glean","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/data\/2019\/11\/08\/instrumenting-android-crashes-with-glean\/","title":{"rendered":"This Week in Glean: Instrumenting Android Crashes with Glean"},"content":{"rendered":"<p><em>(\u201cThis Week in Glean\u201d is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release notes, documentation, hopes, dreams, or whatever: so long as it is inspired by Glean. You can find an<a href=\"https:\/\/mozilla.github.io\/glean\/book\/appendix\/twig.html\"> index of all TWiG posts online<\/a>.)<\/em><\/p>\n<p>For the last year I\u2019ve been working on this shiny new library by Mozilla known as <a href=\"https:\/\/github.com\/mozilla\/glean\">Glean SDK<\/a>. The Glean SDK is a cross-platform telemetry library that makes collecting data easier for developers to understand and instrument in their applications. Just take a look to the tagline, \u201cTelemetry for humans\u201d to understand the objective of Glean. Glean is being used right now in things like <a href=\"https:\/\/github.com\/mozilla-mobile\/fenix\/\">Firefox Preview<\/a> and <a href=\"https:\/\/github.com\/mozilla-mobile\/firefox-tv\/\">Firefox for Fire TV<\/a> and I\u2019m sure we will see it in even more Mozilla products very soon.<\/p>\n<div class=\"wp-block-jetpack-markdown\">\n<p>One of the things that we might want to collect some data on in the applications we create is crashes. Every application will have them from time to time. Sometimes they are the application\u2019s fault, sometimes it\u2019s the fault of the OS, but they happen and we usually want to know more about them so we can fix them (if possible). So let\u2019s take a look at how we can use Glean to instrument an Android application and record some data when it crashes.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h2>Before We Start<\/h2>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>There are a few things that we need to have installed in order to get ready. The biggest one of those is <a href=\"https:\/\/developer.android.com\/studio\/\">Android Studio<\/a>, which (if you include the Android SDK) can take a little while to download and get installed. Second, I\u2019m going to recommend having the <a href=\"https:\/\/mozilla.github.io\/glean\/book\/\">Glean documentation<\/a> open and available as we will be referring to it throughout this walk-through as we instrument our app. This walkthrough also assumes that you already have some knowledge of Android application development. We won\u2019t be doing anything too involved, but knowing where to go to create a new project and how to add dependencies to a Gradle file will be helpful to know beforehand.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h2>Create A Project<\/h2>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>With our tools and documentation installed and open, let\u2019s get started by creating a new Android Studio project. We could choose any type of project or template to start with, but let go with the \u201cBasic Activity\u201d template, simply because it comes with a button already on the screen that we can rig to crash the app in order to collect our metrics. We need to give the project a name: something along the lines of &#8220;GleanCrashExample&#8221; sounds reasonable enough. Then we make sure to choose Kotlin for the language and API level 21 for the minimum SDK level. Tada! A new project ready for integration with Glean!<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h2>Setup Build Configuration<\/h2>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Add Glean As A Dependency<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Now that we have a project started, we can add Glean to it as a dependency in the Gradle configuration. Find the <code>build.gradle<\/code> file for your <em>app module<\/em> and add the following line to the dependencies section:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Groovy\">implementation 'org.mozilla.components:service-glean:20.0.0'\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>As of this writing, version <code>20.0.0<\/code> is the latest version of <a href=\"https:\/\/github.com\/mozilla-mobile\/android-components\/\">Android Components<\/a>. Android Components is a collection of tools for building browsers on Android and it is the easiest way to consume Glean on Android at the moment. You should probably take a look at the Android-Components releases and check that you are using the latest version of Android-Components for your project. So just replace the version above with the latest.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Add Python Environment<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>We need to add a Python environment for the <a href=\"https:\/\/github.com\/mozilla\/glean_parser\/\">glean_parser<\/a> to run in by adding the following lines to the very top of the <code>build.gradle<\/code> file:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Groovy\">plugins {\r\n    id \"com.jetbrains.python.envs\" version \"0.0.26\"\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Import SDK Generator Script<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>The last part of the changes to the app\u2019s <code>build.gradle<\/code> file is to include the <code>sdk_generator.gradle<\/code> script. Right before the end of the <code>build.gradle<\/code> file, add the following (making sure the version here matches the version of the <code>implementation<\/code> line above):<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Groovy\">apply from: 'https:\/\/github.com\/mozilla-mobile\/android-components\/raw\/v20.0.0\/components\/service\/glean\/scripts\/sdk_generator.gradle'\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Add Mozilla Maven Repository<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Finally, in order to be able to download Glean, we need to add Mozilla\u2019s Maven repository URL to the Project <code>build.gradle<\/code> file (as opposed to the app module\u2019s <code>build.gradle<\/code> file). For our project named \u201cGleanCrashExample\u201d, there will be a <code>build.gradle<\/code> that has <code>(Project: GleanCrashExample)<\/code> next to it in the project explorer of Android Studio. In this Gradle file, you will find a section called <code>allProjects<\/code> which has in it a section called <code>repositories<\/code>. You may already find <code>google()<\/code> and <code>jcenter()<\/code> there, but we need to add the following in order to download Glean:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Groovy\">maven {\r\n    url \"https:\/\/maven.mozilla.org\/maven2\"\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>That should be it for additions to the build configuration files. You should try to do a Gradle sync now to make sure everything works. If you have any problems, try going back through the configuration steps to make sure you got everything.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h2>Instrument The Project With Glean<\/h2>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Initial Integration<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Now that we have added Glean to our app, we need to do a little initialization in order to get it ready to record and send data. It is important to override the <code>Application<\/code> class so that we can initialize the Glean SDK in the <code>Application.onCreate()<\/code> method. Add a new file with a class that extends <code>Application<\/code> called <code>GleanCrashExampleApplication<\/code> and update the application manifest to add <code>android:name=\".GleanCrashExampleApplication\"<\/code> as a property in the <code>application<\/code> section so that the app knows to use the custom Application class. Your <code>GleanCrashExampleApplication.kt<\/code> file should look like this:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">package org.mozilla.gleancrashexample\r\n\r\nimport android.app.Application\r\nimport mozilla.components.service.glean.Glean\r\nimport org.mozilla.gleancrashexample.GleanMetrics.Pings\r\n\r\nclass GleanCrashExampleApplication: Application() {\r\n    override fun onCreate() {\r\n        super.onCreate()\r\n\r\n        \/\/ Register the sample application's custom pings.\r\n        Glean.registerPings(Pings)\r\n\r\n        \/\/ Set upload enabled\r\n        Glean.setUploadEnabled(true)\r\n\r\n        \/\/ Initialize the Glean library. Ideally, this is the first thing that\r\n        \/\/ must be done right after enabling logging.\r\n        Glean.initialize(applicationContext)\r\n    }\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>This adds the necessary import statements, and then registers pings, sets the upload enabled state, and initializes the Glean SDK within the <code>Application.onCreate()<\/code> method.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>That\u2019s it! The app now has Glean installed and enabled and will already be able to send the baseline ping without any additional work!<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Add Custom Metric<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Since we are instrumenting crashes with some custom metrics, we still have some work to do. We will need to add a <code>metrics.yaml<\/code> file to define the metrics we will use to record our crash information and a <code>pings.yaml<\/code> file to define a custom ping which will give us some control over the scheduling of the uploading of the crash telemetry.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>In order to do this we need to make a decision: What metric type will we use to represent our crash data? This could probably be argued several ways, but I have chosen to instrument it as an <a href=\"https:\/\/mozilla.github.io\/glean\/book\/user\/metrics\/event.html\">event<\/a>, because events capture information in a nice concise way and have a built-in way of passing additional information using the <code>extras<\/code> field. So if we want to pass along the cause of the crash, or a few lines to describe the crash, or even potentially a few lines of stack trace, events let us do that easily (with <a href=\"https:\/\/mozilla.github.io\/glean\/book\/user\/metrics\/event.html#limits\">some limitations<\/a>).<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Now that we have decided what metric type we will use, we can create our <code>metrics.yaml<\/code>. Inside of the <code>app<\/code> folder in the Android Studio project we are going to create a new file and call it <code>metrics.yaml<\/code>. Then we can add the schema definition and the metric definition to the file, so that it looks like this:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-YAML\"># Required to indicate this is a `metrics.yaml` file\r\n$schema: moz:\/\/mozilla.org\/schemas\/glean\/metrics\/1-0-0\r\n\r\ncrash:\r\n  exception:\r\n    type: event\r\n    description: |\r\n      Event to record crashes caused by unhandled exceptions\r\n    notification_emails:\r\n      - crashes@example.com\r\n    bugs:\r\n      - https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1582479\r\n    data_reviews:\r\n      - https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1582479\r\n    expires:\r\n      2099-01-01\r\n    send_in_pings:\r\n      - crash\r\n    extra_keys:\r\n      cause:\r\n        description: The cause of the crash\r\n      message:\r\n        description: The exception message\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>By way of explanation, this creates a metric called <code>exception<\/code> within a metric category called <code>crash<\/code>. There is a brief description and the required notification, bug, data review, and expiry fields. Then we have the <code>send_in_pings<\/code> field with a value of <code>- crash<\/code>. This means that we will send the crash data via a custom ping named <code>crash<\/code> (which we haven\u2019t created, yet). Finally we have the <code>extra_keys<\/code> field with two keys defined, <code>cause<\/code> and <code>message<\/code>. This will allow us to send a couple of pieces of additional information along with the event.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Add Custom Ping<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Now we need to define the custom ping by creating a <code>pings.yaml<\/code> file in the same directory as we just created the <code>metrics.yaml<\/code> file. We already know what the name of the ping is, <code>crash<\/code>, so the <code>pings.yaml<\/code> file should look like this:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-YAML\"># Required to indicate this is a `pings.yaml` file\r\n$schema: moz:\/\/mozilla.org\/schemas\/glean\/pings\/1-0-0\r\n\r\ncrash:\r\n  description: &gt;\r\n    A ping to transport crash data\r\n  include_client_id: true\r\n  notification_emails:\r\n    - crash@example.com\r\n  bugs:\r\n    - https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1582479\r\n  data_reviews:\r\n    - https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1582479\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Before we can use our newly defined metric or ping, we need to build the application. This will cause the <a href=\"https:\/\/github.com\/mozilla\/glean_parser\/\">glean_parser<\/a> to work its magic and generate the API files that represent the metrics we defined<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p><strong>NOTE:<\/strong> Just a little advice from my personal experience here, but I would recommend running the clean task on your project before building <em>any<\/em> time you have modified one of the Glean YAML files. Sometimes it doesn\u2019t regenerate the files without cleaning, so something to keep in mind if you aren\u2019t seeing your changes to the YAML files show up in the project.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>We now have a couple of tasks to perform back in the <code>MainActivity.kt<\/code> file in order to make use of the metric and ping we defined. First, we need to add an import line:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">import org.mozilla.gleancrashexample.GleanMetrics.Pings\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Then we need to register our custom ping by calling <code>Glean.registerPings(Pings)<\/code>. In the <code>MainActivity.onCreate()<\/code> function, add the following line:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">Glean.registerPings(Pings)\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>This registers the custom ping with Glean so that it knows about it and can manage the storage and other important details of it like sending it when we tell it to.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Instrument The App To Record The Event<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Next, we need to make the <code>MainActivity<\/code> handle uncaught exceptions, so we extend the class definition by adding <code>Thread.UncaughtExceptionHandler<\/code> as an inherited class like this:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">class MainActivity : AppCompatActivity(), Thread.UncaughtExceptionHandler {\r\n    ...\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>In order to be a responsible <code>Thread.UncaughtExceptionHandler<\/code>, we need to implement an override for the <code>uncaughtException()<\/code> function. Somewhere in your <code>MainActivity<\/code> class, add the following override:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">override fun uncaughtException(p0: Thread, p1: Throwable) {\r\n    Crash.exception.record(\r\n        mapOf(\r\n            Crash.exceptionKeys.cause to p1.cause!!.toString(),\r\n            Crash.exceptionKeys.message to p1.message!!)\r\n    )\r\n    Pings.crash.send()\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>To explain what\u2019s happening here, first we are recording to the <code>Crash.exception<\/code> metric we created. If you recall, the category of the metric was <code>crash<\/code> and the name was <code>exception<\/code> so we access it by calling <code>record()<\/code> on the <code>Crash.exception<\/code> object that was created by the magic of Glean. You can also see where we are passing in the extra information for the cause and the message which will get packaged up and sent along with our ping when the second action of <code>Pings.crash.send()<\/code> is called. Basically this forces our ping to be sent immediately after the recording of the event.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Finally, we need to register our <code>MainActivity<\/code> as the default uncaught exception handler by adding the following line to the <code>MainActivity.onCreate()<\/code> function:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">Thread.setDefaultUncaughtExceptionHandler(this)\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h3>Make A Way To Crash The App<\/h3>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>That\u2019s pretty much it! Now we just need to make a way to crash our app with an uncaught exception. The &#8220;Basic Activity&#8221; template has a floating action button called <code>fab<\/code> that can be used, and it\u2019s set up in the <code>onCreate()<\/code> of the <code>MainActivity<\/code>. We can change the click listener to make it look like this in order to make it throw an exception:<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<pre><code class=\"language-Kotlin\">fab.setOnClickListener {\r\n    \/\/ Go boom!\r\n    throw NullPointerException()\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>Now run the application and click the floating action button and you should have just crashed the app while recording and sending crash telemetry! I should mention, this information didn\u2019t really get recorded by anything, as there is additional work that is required on our ingestion pipeline in order to accept telemetry from new applications, and that step is for another blog post, but there are <a href=\"https:\/\/github.com\/mozilla\/probe-scraper#adding-a-new-glean-repository\">docs available if you are interested<\/a>.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<h2>Conclusion<\/h2>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p>If you are interested in seeing the fully operational project used to help write this tutorial, you can find it on GitHub <a href=\"https:\/\/github.com\/travis79\/GleanCrashExample\">here<\/a>.<\/p>\n<\/div>\n<div class=\"wp-block-jetpack-markdown\">\n<p><strong>Important Note:<\/strong> This is a <em>very<\/em> simple example of a strategy to instrument crashes by using Glean. There probably will be challenges to using this approach in a production application that should be considered. For instance, when an app crashes it can be in an unknown state and may not be able to do things like upload data to a server. There is a less trivial implementation of instrumenting crashes in Android Components called <a href=\"https:\/\/github.com\/mozilla-mobile\/android-components\/tree\/master\/components\/lib\/crash\">lib-crash<\/a> that takes this a step further by taking into consideration such things as multiprocessing and persistence.<\/p>\n<p>(( This is a syndicated copy of <a href=\"https:\/\/blogoftravis.wordpress.com\/2019\/11\/08\/this-week-in-glean-2019-11-08\/\">the original post<\/a>. ))<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>(\u201cThis Week in Glean\u201d is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/data\/2019\/11\/08\/instrumenting-android-crashes-with-glean\/\">Read more<\/a><\/p>\n","protected":false},"author":1757,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[315988,448297],"tags":[30,448297],"coauthors":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts\/159"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/users\/1757"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/comments?post=159"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts\/159\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/media?parent=159"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/categories?post=159"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/tags?post=159"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/coauthors?post=159"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}