So, you’ve built a React Native app on Android that is going to change everything. Soon the fateful day arrives when you must release it into the wild, like a chick leaving the nest.
A few hours in, you have a user email stating that the app keeps crashing every time they load the screen dedicated to raccoon gifs.
Well, it’s a good thing you have crash logs set up, so you know exactly what caused the crash, right? Well, there's just one problem...
There’s not much documentation out there related to forwarding React Native JS error messages to reporting services like fabric.io on Android devices. Right now, much of the development focus in React Native seems to be geared towards an iOS target. While it doesn’t seem like there are any tutorials around for Android yet, we can still learn a lot from some of the solutions in the iOS sphere.
Case in point, I will be shamelessly stealing from Bruno Barbieri’s wonderful React Native iOS tutorial to provide a sort of companion piece for the Android side of things. Give that a read first to get a good overview on the topic.
We’re going to be using the same general idea to get things going on Android as Bruno does for iOS.
- Install the Fabric and Crashlytics SDKs
- Generate and add Fabric API keys
- Copy Default Logger
- Forward production logging messages to Crashlytics
- Replace the default logger with our modified copy
If you want an example of how this would be set up in a real project, I’ve set up a repository on GitHub that goes through and implements each of these steps here.
STEP 1: Install Fabric and Crashlytics SDK
To get started, you will need to sign up if you do not already have an account. After that, all we have to do is follow the steps Fabric provides us on their website.
In our
buildscript {repositories {maven { url 'https://maven.fabric.io/public' }}dependencies {// The Fabric Gradle plugin uses an open ended version to react// quickly to Android tooling updatesclasspath 'io.fabric.tools:gradle:1.+'}}apply plugin: 'io.fabric'repositories {maven { url 'https://maven.fabric.io/public' }}dependencies {// Crashlytics Kitcompile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {transitive = true}…}
STEP 2: Generate and add Fabric API keys
Next we need to add our API and Build Secret keys to a new
// android/app/fabric.propertiesapiSecret=MyWhatASuperSecretiveKeyapiKey=WowSoSecret
To find these keys, go to your Organization page at Settings > Organizations > <Organization> and click on the tabs for each one.
NOTE
If you have not previously configured your application through your account on fabric.io, you will not be able to get your API Key from the Organization settings page.
In this case, your best option is to follow the the general Android installation instructions that Fabric provides as this will fill in the API Key value in your
STEP 3: Copy Default Logger
This is where the real changes come into play. There are some tutorials around that show how to get Fabric set up in Android (here), but neither of them will forward JS exceptions to our Fabric dashboard, just the Java errors.
Our ultimate goal is to insert our logger — which will send logs to Fabric when on users' devices, but still allow us to tinker around when in development — in place of the default logger, FLog.
The first step in this is to implement Facebook's LoggingDelegate interface. The easiest, and most foolproof way to do this is to just copy their own DefaultLoggingDelegate into our own file at
We will then associate that copy with our package, and rename it from DefaultLoggingDelegate to ReactNativeFabricLogger.
package com.facebook.common.logging;package com.awesomeproject;import com.facebook.common.logging.LoggingDelegate;…// public class FLogDefaultLoggingDelegate implements LoggingDelegate {public class ReactNativeFabricLogger implements LoggingDelegate {…// public static final FLogDefaultLoggingDelegate sInstance = new FLogDefaultLoggingDelegate();public static final ReactNativeFabricLogger sInstance = new ReactNativeFabricLogger();...// public static FLogDefaultLoggingDelegate getInstance() {public static ReactNativeFabricLogger getInstance() {...}// private FLogDefaultLoggingDelegate() {private ReactNativeFabricLogger() {…}}
Step 4: Forward production logging messages to Crashlytics
Now we just need to place Crashlytics logger in place of the Android logger when the app is in production.
import com.crashlytics.android.Crashlytics;public class ReactNativeFabricLogger implements LoggingDelegate {private void println(int priority, String tag, String msg) {Log.println(priority, prefixTag(tag), msg);if (BuildConfig.DEBUG) {Log.println(priority, prefixTag(tag), msg);} else {Crashlytics.log(priority, prefixTag(tag), msg);}}private void println(int priority, String tag, String msg, Throwable tr) {Log.println(priority, prefixTag(tag), getMsg(msg, tr));if (BuildConfig.DEBUG) {Log.println(priority, prefixTag(tag), getMsg(msg, tr));} else {Crashlytics.log(priority, prefixTag(tag), msg);}}}
Step 5: Replace the default logger with our modified copy
Finally, we need to tell Fabric that we want to use Crashlytics and FLog to use our modified logger in place of the default logger.
import com.facebook.common.logging.FLog;import com.crashlytics.android.Crashlytics;import io.fabric.sdk.android.Fabric;public class MainActivity extends ReactActivity {public void onCreate() {Fabric.with(this, new Crashlytics());FLog.setLoggingDelegate(ReactNativeFabricLogger.getInstance());}}
That’s it!
Our JS exceptions will now be shown in the Crashlytics dashboard with stack traces and everything. The error grouping isn’t quite as nice as you might find in a native application, but it certainly does the trick.
Now, go get those raccoon gifs back!
EDIT The first version of this post said that console.logs would also be forwarded to Fabric logging. Unfortunately, only exceptions will be output with this version of our logger. I am currently trying to hook up the console to Crashlytics, and will update this post when it's all worked out.
Follow along here to get updated on the current status of the process. If you have any suggestions on how to move forward, let me know!
PS: Do you have a React project in the pipeline? Check out our React development services to learn more about our development engagements.