FullStory integration with Crashlytics Technical Guide

Summary

Using FullStory and Crashlytics together provides unparalleled insights into app issues so you can debug issues and iterate faster.

When a crash is reported to Crashlytics, an engineer can use the session replay to see visually what the user did leading up to the moment of the crash.

The Goal:

This documentation aims to provide common use cases for integrating FullStory and Crashlytics and technical guidance on how to do so for iOS and Android. Feel free to contact FullStory support for more information, or share your success story with us!

Use Cases

Add a FullStory session replay link to a Crashlytics report

FSsessionreplayNM.png

With FullStory for mobile apps, you can retrieve a link to the beginning of the session replay to be attached to the Crashlytics report. When viewing an issue in Crashlytics, navigate to the session replay link and watch the steps to reproduce without ever having to coordinate with end-users to get their feedback about what happened. This enables engineers to quickly and accurately reproduce the crash and fix the issue.

 

Capture user journeys along with app state

image2.png

In addition to sharing a replay link, you can also share point-in-time data via logging. At any point in your application, you may wish to log certain messages to Crashlytics and FullStory, so you can easily navigate back and forth between Crashlytics and FullStory to find valuable information about specific crashes and errors.

By logging the app state as your users journey through your app, you can easily trace back the cause of an exception as it relates to user interaction and also understand the app state when the crash occurred.

 

Identify users who experienced a crash

When diagnosing and working with an issue, it’s often helpful to zoom-in to a certain set of users to understand how they are affected. Both FullStory and Crashlytics allow you to identify the user by setting a userID. Navigate between FullStory and Crashlytics for a user and have side by side data for the chosen user.

 

Prioritize non-fatal Errors

Non-fatal errors can still have a negative impact on user experience. While trying to understand your users’ overall experience, it may be valuable to investigate how non-fatal errors are getting in their way. Certain non-fatal errors may be caught and handled but may still stop your users from being able to use key functionalities and thus cause frustration and churn. Use custom events in FullStory to surface any errors that may have negative impact. All custom events are indexed, which means you can search for non-fatal errors and prioritize them accordingly.

PurchaseSegmentNM.png

 

Implementation

Add a FullStory session replay link to a crash report

  1. Setup your Crashlytics SDK and get crash reports via Crashlytics
  2. Setup FullStory recording for Android or iOS
  3. Implement your app to respond when FS is ready

Android

  • Extend your Activity to implement FSOnReadyListener and override the onReady method
  • onReady is called when FS is initialized and has FSSessionData ready. So you can retrieve the session URL using FS.getCurrentSessionURL():
@Override
public void onReady(FSSessionData sessionData) {
    Log.d("TAG", "FSSessionURL " + sessionData.getCurrentSessionURL());
    ...
}

iOS

  • Using the FS delegate method called fullstoryDidStartSession(_ sessionURL: String), this will provide you with the session URL (No need to Override the method).
func fullstoryDidStartSession(_ sessionUrl: String) {
    print("FSSessionURL: ",sessionUrl)
    ...
}

 

4.  After retrieving the FS session URL, use the following snippet to attach the sessionURL as a key-value pair to a Crashlytics report:

Android

FirebaseCrashlytics instance = FirebaseCrashlytics.getInstance();
String sessionURL = sessionData.getCurrentSessionURL();
instance.setCustomKey("FSsessionURL", sessionURL);

iOS

Crashlytics.crashlytics()
.setCustomValue(sessionUrl, forKey: "FSURLSession")

 

5.  When a crash occurs, you will see an issue under Crashlytics in your Firebase console. Note that when an app crashes, the session will terminate, meaning that the end of this session is where the crash had occurred. In each session under the issue, you can go to the Keys tab, and see the FullStory session URL:

Keys.png

 

Capture user journeys along with app state

  1. You can choose to send the session URL along with custom log messages to Crashlytics log, and also mark them in FS playback using FS.event or FS.log:

Android

String fsCustomEventTag = "CrashlyticLog";
String sessionURL = sessionData.getCurrentSessionURL();
String logMsg = "Higgs-Boson detected! Bailing out";
Map<String, String> eventVars = new HashMap<>();
eventVars.put("logMessage", logMsg);

// send FS even or log to be shown in the playback
FS.event(fsCustomEventTag,eventVars);
FS.log(FS.LogLevel.INFO, logMsg);

// Crashlytics:
instance.log(logMsg);
instance.log("Current FSSessionURL "+ sessionURL);
instance.log("Look for FS event in playback with tag: " + fsCustomEventTag);

iOS

let fsSessionURL = FS.currentSessionURL
let fsCustomEventTag: String = "CrashlyticLog"
let logMsg = "Higgs-Boson detected! Bailing out"
var eventVars = [String: String]()
eventVars["logMessage"] = logMsg

// send FS even or log to be shown in the playback
FS.event(fsCustomEventTag,properties: eventVars)
FS.log(with: FSLOG_INFO, message: logMsg)

// Crashlytics:
Crashlytics.crashlytics().log(logMsg)
Crashlytics.crashlytics()
.log("Current FSSessionURL \(String(describing: fsSessionURL))");

Crashlytics.crashlytics()
.log("Look for FS event in playback with tag: \(fsCustomEventTag)");

 

2.  View your custom logs for an issue under the Logs tab: 

Logs.png

3. And then you can navigate to the FullStory session URL. You will see custom events on the right hand pane during replay, and custom logging will show up under dev tools to allow you to identify the point of time when the logs were sent to Crashlytics. These app state breadcrumbs help you better understand the exception.

See the logs and event in FullStory playback like below:

Screen_Shot_2020-04-20_at_3.52.41_PM__1_.png

Notice the blocked-out areas of the app visible during replay. These are our default privacy controls in action. To learn more about how we’ve designed our privacy controls for mobile apps, check out this article on our engineering blog: https://bionic.fullstory.com/private-by-default-mobile-analytics/

Set user identifiers across FS and Crashlytics

  1. When you are able to identify the user, you can send the user identity to Crashlytics
  2. At the same time, send the same user id to FullStory using FS.identify, with optional userVars, to help you identify users that experience a crash.

    Android
    // when user is identified(logged in), create a Map for the userVars
    Map<String, String> userVars = new HashMap<>();
    userVars.put("userID", "testuser3");
    userVars.put("displayName","crashlytics user3");
    userVars.put("email","testeamil@gmail.com");

    // send user identity and userVars to FS
    FS.identify(userVar.get("userID"),userVar);

    // send user identity to Crashlytics
    instance.setUserId(userVar.get("userID"));
    iOS
    // when user is identified(logged in), create a Map for the userVars
    var userVars = [String: String]()
    userVars["userID"] = "testuser3"
    userVars["displayName"] = "crashlytics user3"
    userVars["email"] = "testeamil@gmail.com"

    // send user identity and userVars to FS
    FS.identify("testuser3", userVars: userVars);

    // send user identity to Crashlytics
    Crashlytics.crashlytics().setUserID("testuser3")
  3. By doing this you are now able to view and identify issues in Crashlytics by how many users are affected:

    Crashusers.png
  4. User ID is also shown in the Data tab under an issue:

    DataTab.png

  5. Search for a certain user to investigate any user that is affected by crashes (Note that FullStory does not record crash events for iOS as of right now, but you can still see all the sessions from a certain user without adding the crashed event filter):

    Dashtestuser.png

  6. Optionally, build your own deep link into FullStory to be passed into Crashlytics, or vice versa:

    a.    Get the link from your browser when you have a built out search in FullStory, something like this: <FS_USER_LINK>  =
    https://app.fullstory.com/ui/<ORG_ID>/segments/everyone/people:search:(:((UserAppKey:==:%22<USER_ID>%22)):():():():)/0

    (Android only): <FS_USER_CRASH_LINK> = 
    https://app.staging.fullstory.com/ui/5<ORG_ID>6EM/segments/everyone/people:search:(:((UserAppKey:==:%22<USER_ID>%22)):():(((EventType:==:\"crashed\"))):():)/0

    b.    Get a list of crashes for specific users in Crashlytics by searching for a user ID, getting the link from your browser, something like this:
    <CRASHLYTICS_LINK> = 
    https://console.firebase.google.com/u/0/project/<PROJECT_NAME>/crashlytics/app/<PLATFORM:APP_ID>/search?time=last-seven-days&type=crash&q=<USER_ID>

    c.    Pass the deep links into FullStory or Crashlytics. Note that a different keys should be used for each deep link, if the same key is used, the latest value being passed will override the previous ones:

    Android

    // optionally attach the user specific links between platforms 
    // Hardcoding the query link, use with caution

    Map<String, String> userVars = new HashMap<>();
    // call setUserVars or you can set these vars during identify
    userVars.put("crashlyticsURLAndroidDemoapp",<CRASHLYTICS_LINK>);
    FS.setUserVars(userVar);

    // add FS links to the crash report as custom key
    instance.setCustomKey("FSUserSearchURL",<FS_USER_LINK>);
    instance.setCustomKey("FSUserCrashedSearchURL",<FS_USER_CRASH_LINK>);

    iOS

    var userVars = [String: String]()
    // call setUserVars or you can set these vars during identify
    userVars["crashlyticsURLiOSDemoApp"] = <CRASHLYTICS_LINK>
    FS.setUserVars(userVars);

    // add FS links to the crash report as custom key
    Crashlytics.crashlytics()
    .setCustomValue(<FS_USER_LINK>, forKey: "FSUserSearchURL")
  7.  Now you are able to go back and forth when investigating a certain crash like so:
          a.  Identify a user in Crashlytics:

    DataTab.png

          b.   
    Get the link to the FullStory session replay so you can see all the crashes from all users or
                just the current user. Feel free to build on top of these prebuilt links and create your own
                search or segment.

               
                For Android, retrieve all crashes:

    keys_crash.png

        c.   Get the most recent URL to Crashlytic's user search page, so you can go directly to see all the crashes for this user:

    UserCard.png

Send FS events during non-fatal exception handling

  1. Crashlytics automatically reports fatal crashes, but you can optionally send non-fatal crashes when you catch one
  2. You can also send a log or an event to FullStory to help you pinpoint the error when it happens during session replay

Android

try {
// your code that throws an Exception here
}catch (Exception e){
// record the Exception to Crashlytics
FirebaseCrashlytics.getInstance().recordException(e);
// Send the error to FS
Map<String, String> eventVars = new HashMap<>();
eventVars.put("errorMessage", e.getMessage());
FS.event("nonFatalException",eventVars);
FS.log(FS.LogLevel.ERROR,e.getMessage());
}

iOS

do {
    // your code that throws an Exception here such as:
    throw NSError(domain: "TestDomain", code: 404, userInfo: nil)
}catch {
// code to handle your error

// record the Exception to Crashlytics
    Crashlytics.crashlytics().record(error: error)

            

// Send the error to FS via event or log
    var eventVars = [String: String]()
    eventVars["errorMessage"] = error.localizedDescription
    FS.event("nonFatalException",properties: eventVars)
    FS.log(with: FSLOG_ERROR, message: error.localizedDescription)
}


     3.  This will help you pinpoint the non-fatal error that occurred during a user session:
              a.  Pinpoint the error occurrence in FullStory playback

crash_lastshot.png

          b.  In this case we can see that even though the exception is non-fatal and did not crash the       
               app, it prevents your user from clicking on “purchase” button
          c.  You can easily build searches and filters to identify the most crucial exceptions in your     
                workflow:

Screen_Shot_2020-04-22_at_8.43.37_AM.png

       d.  Once you’ve pinpointed the exception that you would like to prioritize, you can then view the
  
             
non-fatal exceptions in Crashlytics

non_fatals_end.png

 

Need to get in touch with us?

The FullStory Team awaits your every question.

Contact us