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.71k stars 2.22k forks source link

Enrich crashlytics stacktrace #3760

Closed thomasblom closed 4 years ago

thomasblom commented 4 years ago

Documentation Feedback

I have successfully integrated Firebase Crashlytics within my app, but one thing disturbs me. If you look at the results in the Firebase Console, you see a non-fatal error that I logged. Schermafbeelding 2020-06-09 om 13 43 08

But what I couldn't find in the documentation is how to modify this. Because every error that I will log will come from io.invertase.firebase.crashlytics.JavaScriptError

In the main overview page with all the errors, you see even less data. All you see is index.android.bundle and a minified function where the error came from (.v). And if it came from an anonymous function, the method is simply .<unknown>. Schermafbeelding 2020-06-09 om 13 53 42

Is it possible to change these values of the error log? Especially in the main view this is needed, because you won't see the difference between the errors if all you see is a minified function with index.android.bundle.

Thanks in advance.


mikehardy commented 4 years ago

Did you keep the sourcemap for your bundle build? Or tag the source so you could regenerate it at that point in time and de-obfuscate? You'll need to map it back in order to get the stack trace you want. None of the crash services that I'm aware of handle react-native / javascript bundle de-obfuscation automatically

angelos3lex commented 4 years ago

I had same problem, trying to de-obfuscate. In this SO post, check the answer that helped us de-obfuscate ios crashlytics logs.

thomasblom commented 4 years ago

Thanks for a quick reply guys! I understand we can de-obfuscate with the sourcemap of our bundle build. The struggle we have is more about the Firebase Crashlytics overview, which will only show index.android/ios.bundle. So we cannot see any difference between two logs, except the line numbers. But there is nothing to do about this, am i correct @mikehardy?

@angelos3lex thanks for the tip! But stack-beautifier doesn't understand the stacktrace which Firebase Crashlytics gives me. Do you know what i'm doing wrong?

Stack trace ``` Non-fatal Exception: io.invertase.firebase.crashlytics.JavaScriptError: TEST at .(index.android.bundle:1221:2263) at .p(index.android.bundle:113:423) at .(index.android.bundle:113:1724) at .p(index.android.bundle:113:423) at .o(index.android.bundle:113:900) at .(index.android.bundle:113:1257) at .c(index.android.bundle:109:205) at .b(index.android.bundle:109:1623) at ._(index.android.bundle:109:488) at .h(index.android.bundle:113:1241) at ._invoke(index.android.bundle:113:1293) at .async(index.android.bundle:113:3688) at .onSelect(index.android.bundle:1221:2181) at .value(index.android.bundle:1223:28310) at .touchableHandlePress(index.android.bundle:217:2263) at .touchableHandlePress([native code]:0:0) at ._performSideEffectsForTransition(index.android.bundle:208:9675) at ._performSideEffectsForTransition([native code]:0:0) at ._receiveSignal(index.android.bundle:208:8357) at ._receiveSignal([native code]:0:0) at .touchableHandleResponderRelease(index.android.bundle:208:5645) at .touchableHandleResponderRelease([native code]:0:0) at .b(index.android.bundle:91:1197) at .k(index.android.bundle:91:1340) at .C(index.android.bundle:91:1394) at .N(index.android.bundle:91:1692) at .A(index.android.bundle:91:2482) at .forEach([native code]:0:0) at .z(index.android.bundle:91:2282) at .(index.android.bundle:91:13914) at ._e(index.android.bundle:91:88659) at .Ne(index.android.bundle:91:13582) at .Ue(index.android.bundle:91:13755) at .receiveTouches(index.android.bundle:91:14547) at .value(index.android.bundle:27:3685) at .(index.android.bundle:27:841) at .value(index.android.bundle:27:2939) at .value(index.android.bundle:27:813) at .value([native code]:0:0) ```

The package refers to the error here, but I think this is a valid format.

Thanks in advance.

angelos3lex commented 4 years ago

@thomasblom add new line before each at ... i.e:

Non-fatal Exception: io.invertase.firebase.crashlytics.JavaScriptError: TEST 
at .(index.android.bundle:1221:2263)
at .p(index.android.bundle:113:423) 
at .(index.android.bundle:113:1724)

(No need to add all these lines, the first 4-5 at ... lines will most probably be adequate for tracking down the crash, or at least to check for the test)

thomasblom commented 4 years ago

@angelos3lex Sorry, my previous comment wasn't formatted the right way. I already had new lines (see my edited comment), still getting errors like this:

/usr/local/lib/node_modules/stack-beautifier/stack-beautifier.js:123
        throw new Error(`Stack trace parse error at line ${i + 1}: ${line}`);
        ^

Error: Stack trace parse error at line 2: at .(index.android.bundle:1221:2263)

Do you have any ideas?

angelos3lex commented 4 years ago

@thomasblom I'm not sure, because on firebase console, my trace was like this:

Unhandled JS Exception: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undef..., stack:
Ol@161:86813
<unknown>@161:39525
Hr@161:48370
Cl@161:82658

and then, the outcome of the beautifier was the lines with at ... but each at ... had the actual function name in a normal readable format. So your stack trace seems a little bit different. :( Maybe this is a difference between ios and android obfuscation, and any further step is needed or smthing?!

thomasblom commented 4 years ago

@angelos3lex It works with a trace like yours, but of course not the right files/lines. It's weird the package says it supports the format of my stacktrace, but it doesn't. I will look into this. Thanks anyway for your help!

@mikehardy Do you know how I can get JSC stack traces instead of the format i'm getting?

mikehardy commented 4 years ago

I do not, sorry. I log synthetic analytics "screen" events via react-navigation on each navigation change (the typical pattern to work around react-native using only a single activity) and typically some other data related to session state. That in combination with even objectively terrible stack traces like the above, has been enough for me to quickly inspect code and see the problem in practice so I haven't dug deeper

russellwheatley commented 4 years ago

Feel free to continue the discussion but this isn't an issue with React Native Firebase, so I'm going to go ahead and close it.

Daha62 commented 4 years ago

Got such error in firebase Console image

mikehardy commented 4 years ago

Yeah, I have my first live build out with the with the v8 crashlytics and I see similar on the java side @Daha62 so I can confirm that Firebase Crashlytics (the thing we just migrated to) can generate stacks like this. Why? I'm unsure in my case. I need to verify if I'm actually uploading the deobfuscation files (I may not be) etc.

Can you verify if you have sent all the de-obfuscation files up to firebase etc?

andersonaddo commented 4 years ago

I think this has to do with your project-specific environments. I'm on the move right now so I can't provide a proper screenshot, but my V8 test crash stack trace is fine:

Fatal Exception: java.lang.RuntimeException: Crash Test
       at io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsModule$1.run(ReactNativeFirebaseCrashlyticsModule.java:45)
       at android.os.Handler.handleCallback(Handler.java:873)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:193)
       at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:225)
       at java.lang.Thread.run(Thread.java:764)
mikehardy commented 4 years ago

Great, thanks for the success report @andersonaddo I was pretty sure it was my local project, now I know to really focus on that

abdullahizzuddiin commented 4 years ago

Hi, will any update for this issue? or this issue has been closed permanently?

I also find my stacktraces is difficult to analyze?

    "@react-native-firebase/analytics": "^6.4.0",
    "@react-native-firebase/app": "^6.4.0",
    "@react-native-firebase/crashlytics": "^6.4.0",
    "@react-native-firebase/dynamic-links": "^6.4.0",
    "@react-native-firebase/messaging": "^6.4.0",
    "@react-native-firebase/perf": "^6.4.0",
    "@react-native-firebase/remote-config": "^6.4.0",
mikehardy commented 4 years ago

@abdullahizzuddiin Firebase Crashlytics knows nothing of javascript stack traces. You will need to post-process javascript stack traces https://stackoverflow.com/questions/62214336/react-native-firebase-crashlytics-deobfuscation/62300702#62300702 was posted above and I think it's useful

mikehardy commented 4 years ago

There is hope perhaps, it appears that firebase crashlytics has added some ability to add information to stack frames manually which would mean it is maybe possible to construct more informative stack traces from the javascript code https://github.com/firebase/firebase-ios-sdk/issues/5975 - not sure exactly how useful it would be but if someone was interested in advancing this area we could certainly merge related PRs...

abdullahizzuddiin commented 4 years ago

Awesome!

stack-beautifier lib worked beautifully on my Android Stacktrace.

abdullahizzuddiin commented 4 years ago

But, is there a way to separate issues based on stacktraces?

Look at this pic, image

So many difference crashes is grouped together on ExceptionsManagerModule.java – line 71: com.facebook.react.modules.core.ExceptionsManagerModule.reportException issue. If we look at the detail, we will find many difference stacktraces (means difference bugs) collected there.

mikehardy commented 4 years ago

I am happy stack-beautifier worked for you! I am not aware of away to differentiate higher layers of the stack though unfortunately, for the same reason you have to post-process javascript stacks, Firebase simply does not know about differences that are higher than the native layer, yet

There is potential to pass this information to firebase with the APIs I linked above but we don't have that feature now, and it is not under development by us or anyone proposing a PR yet that I know about

mikehardy commented 4 years ago

Strangely the custom stack traces thing does not appear available on Android. On iOS it was added in pod 6.21.0

https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=ios#customize-stack-traces

akshbn commented 4 years ago

Hi @thomasblom did you solve the problem you were facing? I'm having the same issue.

akshbn commented 4 years ago

Ok, so i went through the source code of stack-beautifier.

The regex relevant to the format returned by Crashlytics is as follows. /^at (.*) \((.*)\:(\d+)\:(\d+)\)$/

For those of us who are using crashlytics on android will get the stack trace as txt in the below format:

Non-fatal Exception: io.invertase.firebase.crashlytics.UnhandledPromiseRejection: undefined is not an object (evaluating 'c.bodyString')
       at .<unknown>(index.android.bundle:1730:1890)
       at .p(index.android.bundle:79:423)

We need to pre-process the crashlytics trace file for stack-beautifier to parse it properly. I created a shell script to do this. Save the below code in a file called preprocess.sh

sed -i 's/\s*at/at/g' $1
sed -i 's/(/ (/g' $1
sed -i 's/:0:/:1:/g' $1 # Stack beautifier indexes lines from 1 and not 0

Run this script bash preprocess.sh <stack trace filename>

Now, we just have to run stack-beautifier. stack-beautifier <map file name> -t <stack trace filename>

Hope this helps someone in the future.

esutton commented 3 years ago

@thomasblom

Paring down my stack trace text to the first two lines, and adding a space after the stack trace function and before the opening parentheses fixed my stack-beautifier Stack trace parse error.

File: mytrace.txt

# stack trace text copied from crashlytics after deleting all but first two lines
Non-fatal Exception: io.invertase.firebase.crashlytics.UnhandledPromiseRejection: undefined is not an object (evaluating 't.includes')
       at .removeFile(index.android.bundle:474:6500)
# Inserted a space after the 'removeFile' function and before opening '('  
# fixed the stack-beautifier "Stack trace parse error"

Non-fatal Exception: io.invertase.firebase.crashlytics.UnhandledPromiseRejection: undefined is not an object (evaluating 't.includes')
       at .removeFile (index.android.bundle:474:6500)

Success!

Found the origination of my removeFile function error in source file Utility.js:567:17

stack-beautifier sourcemap.android.js -t mytrace.txt
Non-fatal Exception: io.invertase.firebase.crashlytics.UnhandledPromiseRejection: undefined is not an object (evaluating 't.includes')
  at includes (/Users/eddie/Documents/projects/react-native/myapp/v1.6.1146/source/app/components/Utility.js:567:17)

Also, stack-beautifier seems to crash when it encounters [native code] in the trace. I deleted all trace lines starting at first [native code] line.

      # Delete all stack trace lines startign at first found line containing [native code]
       at .callImmediates ([native code]:0:0)
mikehardy commented 3 years ago

If this is something we could alter here without affecting backwards compatibility we could take a PR for it We're in control of this stack trace generation here:

https://github.com/invertase/react-native-firebase/blob/f4dfb1a2f6941ff44cf6e020c78d44b5a056d367/packages/crashlytics/lib/index.js#L114

(and in native android as example, perhaps this is the root of formatting issues as this appears to be the standard for Java error reporting at least based on native experience)

https://github.com/invertase/react-native-firebase/blob/f4dfb1a2f6941ff44cf6e020c78d44b5a056d367/packages/crashlytics/android/src/main/java/io/invertase/firebase/crashlytics/ReactNativeFirebaseCrashlyticsModule.java#L189-L194

We are using stacktrace-js though - odd that it is somehow not compatible out of the box with stack-beautifier :thinking: ?

Perhaps if you tried stacktrace-gps instead https://www.stacktracejs.com/#!/docs/stacktrace-gps ?

esutton commented 3 years ago

@mikehardy thank you for your crashlytics work!

I will happily use any stack trace tool that can associate crashlytics stack trace text with the react native android or ios sourcemap files. Crashlytics is great at notifying me when something is going wrong with my react native apps. However, the stack trace text seldom leads me to the offending source code line.

I am struggling to understand how to use stacktrace-js or stacktrace-gps with a react native sourcemap and crashlytics stack trace text.

This is what I tried. I get "StackFrame is not defined".

File: stacktrace-gps-test.js

// Such meta. Wow var errback = function myErrback(error) { console.log(StackTrace.fromError(error)); };

var gps = new StackTraceGPS();

// Pinpoint actual function name and source-mapped location gps.pinpoint(stackframe).then(callback, errback); //===> Promise(StackFrame({functionName: 'fun', fileName: 'file.js', lineNumber: 203, columnNumber: 9}), Error)

// Better location/name information from source maps gps.getMappedLocation(stackframe).then(callback, errback); //===> Promise(StackFrame({fileName: 'file.js', lineNumber: 203, columnNumber: 9}), Error)

// Get function name from location information gps.findFunctionName(stackframe).then(callback, errback); //===> Promise(StackFrame({functionName: 'fun', fileName: 'http://localhost:3000/file.min.js', lineNumber: 1, columnNumber: 3284}), Error)


Launch from macOS terminal as:

node stacktrace-gps-test.js /Users/edward3/Documents/projects/react-native/field-scout/v1.6.1146/source/stacktrace-gps-test.js:15 var stackframe = new StackFrame({fileName: 'sourcemap.android.js', lineNumber: 474, columnNumber: 6500}); ^

ReferenceError: StackFrame is not defined at Object. (/Users/edward3/Documents/projects/react-native/field-scout/v1.6.1146/source/stacktrace-gps-test.js:15:18)


Is stacktrace-js or stacktrace-gps only usable for traditional node.js apps or browser and not usable for react native apps?
mikehardy commented 3 years ago

Honestly I don't know :sweat_smile: - I have not tried them locally, I was just trying to provide brainstorming suggestions based on the context I have. It's all a bit of a science fair project in this area (decode of JS minified+etc stack traces) and I haven't seen anything yet that is quite ready for systemitization except maybe the script chunks above (those do look promising)

Confounding it is that I think with Hermes this will get even more difficult to resolve back to source lines

esutton commented 3 years ago

@mikehardy thanks for the input. Any kind of brainstorming is good to help cut through the noise.

This seems to work for me if I remove all stack trace lines starting with first line containing "[native code]"

npx metro-symbolicate sourcemap.android.js < bug-android-stack-trace.txt
npx metro-symbolicate sourcemap.ios.js < bug-ios-stack-trace.txt

See: https://reactnative.dev/docs/next/symbolication

wilav-dev commented 2 years ago

preprocess.sh

You save my time!