invertase / react-native-firebase

🔥 A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.68k stars 2.21k forks source link

App is not crashing if the bug is in Javascript Code. #1052

Closed thevishnupradeep closed 6 years ago

thevishnupradeep commented 6 years ago

Issue

Usually we expect an app to crash in case of an error if the app is in release configuration and the crashlyitcs to trigger and record the bug. But if the error is in Javascript side I am seeing white screen instead of app crashing. This makes the app harder to debug and kind of renders crashlyitcs integration useless to a large extend.

I am developing two different apps using two different setup and I am seeing same issue on both projects.

I am not sure if the issue is with React Native or react-native-firebase or with my configuration. Any help would be great. Thank you.

Environment

  1. Application Target Platform: Both.

  2. Development Operating System: macOS High Sierra

  3. Build Tools: xCode 9.3, Targets: iOS 10, Android SDK 26

  4. React Native version: 0.51.0 & 0.55.3

  5. RNFirebase Version: 3.3.1 & 4.0.6

  6. Firebase Module: Crashlytics

oakis commented 6 years ago

I am using the following code in my app and I get errors recorded on both dev and production.

const defaultHandler = (ErrorUtils.getGlobalHandler && ErrorUtils.getGlobalHandler()) || ErrorUtils._globalHandler;

ErrorUtils.setGlobalHandler(({ stack }) => {
    const { uid, email, displayName } = firebase.auth().currentUser;
    firebase.crashlytics().setStringValue('Name', displayName);
    firebase.crashlytics().setStringValue('Email', email);
    firebase.crashlytics().setUserIdentifier(uid);
    window.log('Sending error to Crashlytics:', stack);
    firebase.crashlytics().log(stack);
    defaultHandler.apply(this, arguments);
});
simonbengtsson commented 6 years ago

We are using something very similar to @oakis. But thought I could share for further reference. Note that you can store the sourcemap of your react native bundle when exporting it and then it will be possible to get the actual line and source file that is responsible for many fatals.

// crashlyticsSetup.js

import firebase from 'react-native-firebase'

/* global global */

const defaultHandler = global.ErrorUtils.getGlobalHandler()
const crashlytics = firebase.crashlytics()

global.ErrorUtils.setGlobalHandler((...args) => {
  const error = args[0] || 'Unknown'
  console.log('Crashlytics error sent', error);

  if (error instanceof Error) {
    crashlytics.setStringValue('stack', `${error.stack}`)
    crashlytics.recordError(0, `RN Fatal: ${error.message}`)
  } else {
    // Have never gotten this log so far. Might not be necessary.
    crashlytics.recordError(0, `RN Fatal: ${error}`)
  }

  defaultHandler.apply(this, args);
});
anhtuank7c commented 6 years ago

@simonbengtsson Awesome ^^!

tannera commented 6 years ago

@simonbengtsson where do you place/import crashlyticsSetup.js so that it runs properly in the App?

simonbengtsson commented 6 years ago

As early as possible. We have it before registering our react native components in our root index file.

thevishnupradeep commented 6 years ago

Sorry for the late response. I actually forgot about this issue I created. By reinstalling from scratch and manually uploading dsym file when there is a release, I was able to make it work.

ButuzGOL commented 5 years ago

Is anybody tried connect error line number with sourcemap ?

dani-mp commented 5 years ago

After reading this issue, it's not clear to me if, in order to get automatic reporting for crashes happening in the JavaScript side of the app, we need to override the global handler or not. I assume we don't need it, as it's not documented in the integration guide, but, in our tests, we don't get the crashes we force in our JavaScript code reported as we do with the native ones.

Could anyone confirm us this? Thanks.

ButuzGOL commented 5 years ago

I have test it if without crashlytics.setStringValue('stack', `${error.stack}`) you will get this error but it wont be clear where it happens

so adding this line gives info where it happened crashlytics.setStringValue('stack', `${error.stack}`) in bundle correct me if i am wrong

also currently i am looking how to connect it with sourcemaps

Ehesp commented 5 years ago

@ButuzGOL > also currently i am looking how to connect it with sourcemaps

Not currently possible I'm afraid. Something we've been looking into but it has some complications with the bundle size becoming huge.

dani-mp commented 5 years ago

This is what we have been able to get in our tests (note that adding the code posted by @simonbengtsson made no difference for us):

Has anyone experienced this? What are we doing wrong?

simonbengtsson commented 5 years ago

@Ehesp About sourcemaps, we generate them and store one locally for each build. Then we can see the exact component and line the error happened at by running it through something like this script:

const fs = require('fs')
const sourceMap = require('source-map');

(async () => {
  // Replace with the logged stacktrace from crashlytics
  const stacktrace = 't@/var/containers/Bundle/Application/A65EDD36-BFEA[...]';

  const pairs = stacktrace
    .match(/\d+:\d+/g)
    .map(str =>
      str
        .split(':')
        .map(str => parseInt(str))
    )

  const rawSourceMap = JSON.parse(fs.readFileSync(`./sourcemap.ios.js`))
  const consumer = await new sourceMap.SourceMapConsumer(rawSourceMap);

  pairs.forEach(([line, column]) => {
    const pos = consumer.originalPositionFor({line, column})
    console.log(JSON.stringify(pos))
  })

  consumer.destroy();
})()
zaporozhetsAnton commented 5 years ago

To detect crashes in JavaScript code happening inside a React component render method you can wrap all your project in ErrorBoundary and in componentDidCatch use firebase.crashlytics().recordError(). If you don't want to use ErrorBoundary, just add componentDidCatch to your main component.

componentDidCatch (error, errorInfo) { this.setState({ error: error, errorInfo: errorInfo }, () => { firebase.crashlytics().recordError(0, 'this.state.error' + 'JSON.stringify(this.state.errorInfo.componentStack)') }) }

isurugajasinghe commented 5 years ago

@simonbengtsson can you pls explain, where do you import I add this crashlyticsSetup.js ?

oakis commented 5 years ago

@isurugajasinghe Read before commenting. :)

"As early as possible. We have it before registering our react native components in our root index file."

isurugajasinghe commented 5 years ago

Hi @oakis please explain me, if your don't mind, I'm native iOS guy. sorry for my knowledge. where i need to place this crashlyticsSetup.js in my index.js? In below I mentioned my index.js

'''console.disableYellowBox = true;

import { Navigation } from "react-native-navigation"; import { registerScreens } from "./src/config/routes"; import { addListeners } from "./src/utilities/listeners"; import { Provider } from "react-redux"; import setup from "./src/store/setup"; import { iconsMap, iconsLoaded } from "./src/utilities/AppIcons";

Navigation.events().registerAppLaunchedListener(() => {

const store = setup();
registerScreens(store, Provider);
addListeners();

});'''

oakis commented 5 years ago

I do not use react-native-navigation (yet at least) but this is how my index.js looks like right now:

import { AppRegistry } from 'react-native';
import './src/setupLogging';
import './src/setupCrashlytics';
import App from './src/App';

AppRegistry.registerComponent('minahallplatser', () => App);

See I'm just injecting my crashlytics code directly into the entry file (index.js), even before the App component

simonbengtsson commented 5 years ago

@oakis So you are from Gothenburg and have made a Västtrafik app? 😄I've done the same but chose to experiment with flutter this time https://github.com/simonbengtsson/arctic-tern

Planning on release on iOS?

oakis commented 5 years ago

@simonbengtsson I've actually been on an interview with you guys when I was working at Raceone. We talked about it then. So to answer your questions, yes. Except for the iOS part because I don't want to add any ads and Apple Dev is expensive. :)

simonbengtsson commented 5 years ago

Haha the Gothenburg dev scene sure is small! Should have checked the profile pic! ^^