Get crash report in google analytics

2019-06-16 15:18发布

I want to receive notification regarding crash report due to uncaught exception in my google analytics for my android app. I followed the steps given in https://developers.google.com/analytics/devguides/collection/android/v4/exceptions#parsing but still I dont receive any crash report. I had a runtime exception when my app runs. I added the code for ga_reportUncaughtException as true:

true

in my analytics.xml. Is there anything else I need to add in order to get hit in google analytics account. Please help!

2条回答
可以哭但决不认输i
2楼-- · 2019-06-16 15:38

There is an open issue in Analytics. I'm experiencing the same behavior but on real devices from API 10 to 19.

https://code.google.com/p/analytics-issues/issues/detail?id=443

EDIT1: Removed question, just to answer the question described.

EDIT2: I tried to capture and send the exceptions using the Analytics ExceptionBuilder, but it didn't work.

It looks like the report is sent (at least LogCat is showing that the crash is reported), but it is not processed by Analytics.

While Google replies to the issue, I'm using this workaround. I guess it is not the best solution and the code can be improved, but it works for me:

  1. I created a custom dimension in Analytics following this steps https://support.google.com/analytics/answer/2709829?hl=en

  2. In my App, I created a custom exception handler, using the Analytics ExceptionReporter class. When an exception is caught, I get the stack trace and truncate it to 150 Bytes (Actually I'm getting only the first line of the stack and truncate it to 150 chars. I'm assuming that 1Char = 1 Byte). I have to truncate it, because it is the Max Lenght allowed by Analytics when sending custom dimensions values. The stack trace is stored in a Shared Preference instead of being sent. I tried to send it directly, but it does not work once the App has crashed.

    package com.company.package;
    
    import java.lang.Thread.UncaughtExceptionHandler;
    
    import android.content.Context;
    
    import com.google.android.gms.analytics.ExceptionParser;
    import com.google.android.gms.analytics.ExceptionReporter;
    import com.google.android.gms.analytics.GoogleAnalytics;
    import com.google.android.gms.analytics.HitBuilders;
    import com.google.android.gms.analytics.Tracker;
    
    public class GoogleAnalyticsTracker {
    
        private static Tracker mTracker;
        private static GoogleAnalytics mGa;
        private Context mContext;
    
        public GoogleAnalyticsTracker(Context context, int resource) {
            mContext = context;
            mGa = GoogleAnalytics.getInstance(context);
            mTracker = getTracker(resource);
    
            Thread.setDefaultUncaughtExceptionHandler(new AnalyticsExceptionReporter(mTracker, 
                Thread.getDefaultUncaughtExceptionHandler(), context));
        }
    
        synchronized Tracker getTracker(int xmlResource) {
            return mGa.newTracker(xmlResource);
        }
    
        public void sendScreenLabel(String screenLabel) {
            mTracker.setScreenName(screenLabel);
            mTracker.send(new HitBuilders.AppViewBuilder().build());
        }
    
        public void sendCustomDimension(int index, String value) {
            mTracker.send(new HitBuilders.AppViewBuilder().setCustomDimension(index, value).build());
        }
    
        private class AnalyticsExceptionReporter extends ExceptionReporter {
    
            public AnalyticsExceptionReporter(Tracker tracker, UncaughtExceptionHandler originalHandler, Context context) {
                super(tracker, originalHandler, context);
                setExceptionParser(new AnalyticsExceptionParser());
            }
    
            @Override
            public void uncaughtException(Thread t, Throwable e) {
            String exceptionDescription =  getExceptionParser().getDescription(t.getName(), e);
    
                //Add code to store the exception stack trace in shared preferences
    
                super.uncaughtException(t, e);
            }
        }
    
        private class AnalyticsExceptionParser implements ExceptionParser {
    
            @Override
            public String getDescription(String arg0, Throwable arg1) {
                StringBuilder exceptionFirsLine = new StringBuilder();
                for (StackTraceElement element : arg1.getStackTrace()) {
                    exceptionFirsLine.append(element.toString());
                    break;
                }
    
                //150 Bytes is the maximum allowed by Analytics for custom dimensions values. Assumed that 1 Byte = 1 Character (UTF-8)
                String exceptionDescription = exceptionFirsLine.toString(); 
                if(exceptionDescription.length() > 150) 
                    exceptionDescription = exceptionDescription.substring(0, 149);
    
                return exceptionDescription;
            }
        }
    }
    
  3. In the MainActivity when OnStart(), I check if there is any stored stack trace in the shared preferences. If so, I send the custom dimension and clear the shared preference.

    @Override
    protected void onStart() {
        super.onStart();
        String exception = getExceptionFromSharedPreferences(this);
        if(exception != null && !exception.isEmpty()) {
            MainApplication.googleAnalyticsTracker.sendCustomDimension(1, exception);
        } 
        clearExceptionFromSharedPreferences(this);
    }
    
  4. Finally I created a custom report in Analytics

Custom report configuration in Google Analytics

EDIT 3:

I realized that I was sending only the fileName and lineNumber, but not the ExceptionName and the origin of the Exception in my package. I have improved the answer by adding code to also send that info.

private class AnalyticsExceptionParser implements ExceptionParser {

    @Override
    public String getDescription(String arg0, Throwable arg1) {
        String exceptionDescription = getExceptionInfo(arg1, "", true) + getCauseExceptionInfo(arg1.getCause()); 

        //150 Bytes is the maximum allowed by Analytics for custom dimensions values. Assumed that 1 Byte = 1 Character (UTF-8)
        if(exceptionDescription.length() > 150) 
            exceptionDescription = exceptionDescription.substring(0, 150);

        return exceptionDescription;
    }
}
//#endregion

//#region PRIVATE METHODS
private String getCauseExceptionInfo(Throwable t) {
    String causeDescription = "";
    while(t != null && causeDescription.isEmpty()) {
        causeDescription = getExceptionInfo(t, "com.myPackageName", false);
        t = t.getCause();
    }
    return causeDescription;
}

private String getExceptionInfo(Throwable t, String packageName, boolean includeExceptionName) {
    String exceptionName = "";
    String fileName = "";
    String lineNumber = "";

    for (StackTraceElement element : t.getStackTrace()) {
        String className = element.getClassName().toString().toLowerCase();
        if(packageName.isEmpty() || (!packageName.isEmpty() && className.contains(packageName))){
            exceptionName = includeExceptionName ? t.toString() : "";
            fileName = element.getFileName();
            lineNumber = String.valueOf(element.getLineNumber());
            return exceptionName + "@" + fileName + ":" + lineNumber;
        }
    }
    return "";
}
查看更多
放我归山
3楼-- · 2019-06-16 15:52

From my experience you need to understand two things about crashes and exceptions in Google Analytics:

1) Only basic information is stored - Google Analytics will only save the name of the exception and the location (Code file and line number) where the exception was thrown. No information beyond that will be accessible to you on GA. This is definitely not ideal and if you wish to track the actual content of your exceptions (mainly the call stack), use Google Play or implement your own solution.

2) Exceptions are not real-time. Exception information is collected and updated maybe once a day, so if you're experimenting with exceptions and you don't see them immediately, just give it time.

查看更多
登录 后发表回答