Debugging production apps is not fun, often cryptic and tedious. It's hard because you can't just put a breakpoint on it and debug in a clean way, you can't always reproduce the issue and there are obscure issues that work on 99% of devices but fail on that one device. And no, you can't reproduce it in the emulator.
When the app is out, I mostly rely on logs at that point. For help in monitoring and logging, I use Sentry and/or Crashlytics. For debugging locally, I also sometimes rely on native logs (logcat, xcode).
Native Logs when debugging locally
Useful for local debugging, like when testing TestFlight published app before it goes to the App store review. Same goes for Android app before it goes to Play review.
On iOS:
- Connect the device to USB, open XCode, Window, Devices and Simulators. Find your device and then view console log.
There will be a bunch of logs once you start it, but you can filter for your own process.
For me it was useful when I was debugging why react native purchases didn't work.
On Android:
- connect the device to USB, and use adb logcat. To get only logging for your app:
adb logcat -e your.package
Sentry vs Crashlytics
They are somewhat similar. If you have to pick one of them, go with Sentry.
Sentry will send sourcemaps and you will see the exact line of code that's causing the error, you will get traces, breadcrumbs, you can separate tracing for different environments. Sentry is a paid product, but for low usage it's free.
Sentry won't catch all native crashes, Crashlytics will. I wish I could set a reproducable for you, it's just coming from my memory that something was reported on Crashlytics, but wasn't on Sentry.
Crashlytics is free and used for catching native crashes. You can use it to log your own errors in the code. You can also log breadcrumbs, like what was the navigation flow before the error?
Crashlytics is a part of the firebase ecosystem.
What I don't like about crashlytics...it doesn't support source maps. You'll see some cryptic errors, and that's not helpful. This old github issue reports there's no source map support yet and who knows when it will be supported. Please let me know if this changes, so I can update this post.
Also to consider, Crashlytics is only for native, Sentry works everywhere.
Integrating Sentry to Expo
To integrate sentry, go to https://yourorganization.sentry.io/projects/new/ and select react native.
Install `@sentry/react-native` and add it to expo app configuration plugins.
plugins: [
[
'@sentry/react-native/expo',
{
url: 'https://sentry.io/',
organization: 'yourorganization',
project: 'yourproject',
},
],
]
By having it in the plugins section, you won't need to worry about the sourcemaps being uploaded to sentry. But, you need a few things:
Create or edit metro.config.js to include sentry configuration:
const { mergeConfig } = require('metro-config')
const { getSentryExpoConfig } = require('@sentry/react-native/metro')
const { getDefaultConfig } = require('expo/metro-config')
const defaultConfig = getDefaultConfig(__dirname)
module.exports = mergeConfig(getSentryExpoConfig(__dirname), defaultConfig)
Initialize it in your app
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: process.env.EXPO_PUBLIC_SENTRY_DSN,
debug: __DEV__,
environment: __DEV__ ? `development-${Platform.OS}` : `production-${Platform.OS}`,
});
export default Sentry.wrap(App);
Having separate environments is useful, you want to separate what you have while developing, or ios and android. You could have issues hapenning only on iOS for example. You can find the DSN in project settings, Client Keys (DSN).
And start logging:
Sentry.captureException(error, {
extra: {
message,
},
});
You need to set these parameters:
- Organization
- Project
- EXPO_PUBLIC_SENTRY_DSN - dsn used for connecting logs to the correct project. It will be something like https://12121.ingest.us.sentry.io/123. Find it in project settings, Client SDK in the sidebar
- SENTRY_AUTH_TOKEN - This is a private environment variable and should not be leaked with expo public variables. It's needed for authorization when uploading sourcemaps. Create it in account settings, organization tokens.
Sentry is far more advanced then what I've shown you, but this is what I use.
Integrating Crashlytics
My assumption is that you have the firebase setup already. Install @react-native-firebase/crashlytics
and add it to plugins section in expo app configuration.
You can test your setup if you put this somewhere in your code:
crashlytics.crash()
And record an error with:
crashlytics().recordError(e);
You can log breadcrumbs like:
crashlytics().log('User opened meal planner');
crashlytics().setAttribute('diet', 'keto');
try {
await saveProfile();
} catch (e) {
crashlytics().recordError(e);
}
Log and attribute will appear in any subsequent Crash or Non-fatal error reports.
If crashlytics isn't sending any crash reports, check if you have it disabled for development. Create a firebase.json file and add in project root:
{
"react-native": {
"crashlytics_debug_enabled": true
}
}
Also take a look at adb logcat logs, for example you might get a:
The Crashlytics build ID is missing
Redownload google service files, delete your app, prebuild clean, delete metro cache and run it again. Expo is sometimes crazy.
Fast Damage Control
When releasing on production, you could use phased releases or even implement over the air updates. OTA updates help when you have bugs in react native layer, not native. You can release the fix without needing to go through the store updates. I have to admit, that for my own apps, I don't do phased releases. I just push "release to production" and hope for the best ðŸ¤
Youtube: https://youtu.be/p_p80Pv5Rq4