Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle grouping of errors in Fabric/Crashlytics

I have a project that uses Fabric/crashlytics. In order to be able to detect all runtime errors in production, I want to send (non-fatal) exceptions to Crashlytics using:

Crashlytics.logException(new RuntimeException("some identifiable error message");

I want to add this logic to our existing Log-class in the project that is loaded via gradle as a separate "utilities-module". Since this module doesn't hold a reference to Crashlytics (and I can't seem to find a good way of forwarding this reference from the main module), I have solved it by introducing an interface, implemented from the main application:

if (!BuildConfig.APP_BUILD_VERSION.equals("local")) {
        UtilitiesConfig.setExternalLogger(new Logger.ExternalLogger() {
            @Override
            public void reportError(String tag, String message, Throwable throwable) {
                Crashlytics.logException(new RuntimeException(TextUtils.isEmpty(tag) ? message : tag + ": " + message));
            }
        });
    }

This works fine, only problem is that all reports in Fabric are being grouped as one similar exception, since the top rows of the stack trace are similar (i.e. the interface and the application). For example:

Non-fatal Exception: java.lang.RuntimeException: c: onNetworkError, type: CANT_ACCESS_SERVER
   at my.package.Application$3.reportError(Application.java:234)
   at my.package.utilities.Logger.e(Logger.java:34)
   at my.package.utilities.net.CallbackCallable.onNetworkError(CallbackCallable.java:76)
   at my.package.utilities.net.MsgCallable.onNetworkError(MsgCallable.java:250)
   at my.package.utilities.net.CancellableCallable.call(CancellableCallable.java:80)
   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
   at java.lang.Thread.run(Thread.java:776)

...and this:

Non-fatal Exception: java.lang.RuntimeException: u: Cannot update
   at my.package.Application$3.reportError(Application.java:234)
   at my.package.utilities.Logger.e(Logger.java:43)
   at my.package.NTPManager$SyncTimeAsyncTask.doInBackground(NTPManager.java:150)
   at my.package.NTPManager$SyncTimeAsyncTask.doInBackground(NTPManager.java:133)
   at android.os.AsyncTask$2.call(AsyncTask.java:304)
   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
   at java.lang.Thread.run(Thread.java:762)

are both being grouped together, despite them being two different errors originating from different classes/rows and with different error messages.

Is there an efficient way of changing either some configuration in Crashlytics or by reporting it through said callback in another fashion, making Fabric not considering them the same error?

like image 506
Mattias Zippert Avatar asked Feb 22 '18 11:02

Mattias Zippert


People also ask

What is fabric Crashlytics?

Crashlytics is a part of Fabric.io and gives mobile app developers insight into their apps' performance, so you can pinpoint and fix issues quickly and easily.

What is non-fatal exception?

A non-fatal error might be an exception that is caught within a try-catch block, or a performance issue such as an ANR, which indicates the main thread is blocked and the user might be about to kill your app.

What is non-fatal error IOS?

A non-fatal error is a failure in your application that didn't result in a crash for the user. In other words: the loss is recoverable, and the application can continue.


1 Answers

Since issues are grouped by the root cause of the exception, if you used the throwable passed to reportError and set it as the cause of the exception you create, you should get the desired result.

if (!BuildConfig.APP_BUILD_VERSION.equals("local")) {
    UtilitiesConfig.setExternalLogger(new Logger.ExternalLogger() {
        @Override
        public void reportError(String tag, String message, Throwable throwable) {
            String logMessage = TextUtils.isEmpty(tag) ? message : tag + ": " + message;
            Crashlytics.logException(new RuntimeException(logMessage, throwable));
        }
    });
}
like image 75
arekolek Avatar answered Sep 21 '22 01:09

arekolek