Customize your Firebase Crashlytics crash reports

This guide describes how to customize your crash reports by using the Firebase Crashlytics SDK, which is currently in beta. Make sure your app is upgraded to the Firebase Crashlytics SDK before following this guide.

By default, Firebase Crashlytics automatically collects crash reports for all your app's users (you can turn off automatic crash reporting and enable opt-in reporting for your users instead). Crashlytics provides four logging mechanisms out of the box: custom keys, custom logs, user identifiers, and caught exceptions.

Add custom keys

Custom keys help you get the specific state of your app leading up to a crash. You can associate arbitrary key/value pairs with your crash reports and see them in the Firebase console.

Use the setCustomValue method to set key/value pairs:

Swift

For example:

// Set int_key to 100.
Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set str_key to "hello".
Crashlytics.crashlytics().setCustomValue("hello", forKey: "str_key")
Objective-C

When setting integers, booleans, or floats, box the value as @(value). For example:

// Set int_key to 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set str_key to "hello".
[[FIRCrashlytics crashlytics] setCustomValue:@"hello" forKey:@"str_key"];

You can also modify the value of an existing key by calling the key and setting it to a different value. For example:

Swift
Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set int_key to 50 from 100.
Crashlytics.crashlytics().setCustomValue(50, forKey: "int_key")
Objective-C
[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set int_key to 50 from 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(50) forKey:@"int_key"];

Add custom log messages

To give yourself more context for the events leading up to a crash, you can add custom Crashlytics logs to your app. Crashlytics associates the logs with your crash data and displays them in the Crashlytics page of the Firebase console, under the Logs tab.

Swift

In Swift, use log() or log(format:, arguments:) to help pinpoint issues. When logging with these methods, keep the following in mind:

  • If you want to get a useful log output with messages, the object that you pass to log() must conform to the CustomStringConvertible property. The log() returns the description property you define for the object. For example:
    Crashlytics.crashlytics().log("Higgs-Boson detected! Bailing out…, \(<var>attributesDict</var>)")
    
  • .log(format:, arguments:) formats values returned from calling getVaList(). For example:
    Crashlytics.crashlytics().log(format: "%@, %@", arguments: getVaList(["Higgs-Boson detected! Bailing out…", <var>attributesDict</var>]))
    

For more details on how to use log() or log(format:, arguments:), refer to the Crashlytics functions reference documentation.

Objective-C

In Objective-C, use log or logWithFormat to help pinpoint issues. Note that if you want to get a useful log output with messages, the object that you pass to either method must override the description instance property. For example:

[[FIRCrashlytics crashlytics] log:@"Simple string message"];

[[FIRCrashlytics crashlytics] logWithFormat:@"Higgs-Boson detected! Bailing out... %@", <var>attributesDict</var>];

[[FIRCrashlytics crashlytics] logWithFormat:@"Logging a variable argument list %@" arguments:<var>va_list_arg</var>];

For more detail on how to use log and logWithFormat, refer to the Crashlytics functions reference documentation.

Set user identifiers

To diagnose an issue, it’s often helpful to know which of your users experienced a given crash. Crashlytics includes a way to anonymously identify users in your crash reports.

To add user IDs to your reports, assign each user a unique identifier in the form of an ID number, token, or hashed value:

Swift
Crashlytics.crashlytics().setUserID("123456789")
Objective-C
[[FIRCrashlytics crashlytics] setUserID:@"123456789"];

If you ever need to clear a user identifier after you set it, reset the value to a blank string. Clearing a user identifier does not remove existing Crashlytics records. If you need to delete records associated with a user ID, contact Firebase support.

Report non-fatal exceptions

In addition to automatically reporting your app’s crashes, Crashlytics lets you record non-fatal exceptions and sends them to you the next time your app launches.

Note: Crashlytics only stores the most recent eight exceptions in a given app session. If your app throws more than eight exceptions in a session, older exceptions are lost.

You can record non-fatal exceptions by recording NSError objects with the recordError method. recordError captures the thread’s call stack by calling [NSThread callStackReturnAddresses].

Swift
Crashlytics.crashlytics().record(error: error)
Objective-C
[FIRCrashlytics recordError:error];

When using the recordError method, it's important to understand the NSError structure and how Crashlytics uses the data to group crashes. Incorrect usage of the recordError method can cause unpredictable behavior and may cause Crashlytics to limit reporting of logged errors for your app.

An NSError object has three arguments:

  • domain: String
  • code: Int
  • userInfo: [AnyHashable : Any]? = nil

Unlike fatal crashes, which are grouped via stack trace analysis, logged errors are grouped by domain and code. This is an important distinction between fatal crashes and logged errors. For example:

NSDictionary *userInfo = @{
    NSLocalizedDescriptionKey: NSLocalizedString(@"The request failed.", nil),
    NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The response returned a 404.", nil),
    NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Does this page exist?", nil),
    ProductID: @"123456";
    UserID: @"Jane Smith"
};

NSError *error = [NSError domain:NSSomeErrorDomain
                          code:-1001
                          userInfo:userInfo];

When you log the error above, it creates a new issue that is grouped by NSSomeErrorDomain and -1001. Additional logged errors that use the same domain and code values are grouped under the same issue. Data contained within the userInfo object are converted to key-value pairs and displayed in the keys/logs section within an individual issue.

Warning: Avoid using unique values, such as user ID, product ID, and timestamps in the domain and code fields. Using unique values in these fields causes a high cardinality of issues and may result in Crashlytics needing to limit the reporting of logged errors in your app. Unique values should instead be added to the userInfo Dictionary object.

Logs and custom keys

Just like crash reports, you can embed logs and custom keys to add context to the NSError. However, there is a difference in what logs are attached to crashes versus logged errors. When a crash occurs and the app is relaunched, the logs Crashlytics retrieves from disk are those that were written right up to the time of the crash. When you log an NSError, the app does not immediately terminate. Because Crashlytics only sends the logged error report on the next app launch and must limit the amount of space allocated for logs on disk, it is possible to log enough after an NSError is recorded so that all relevant logs are rotated out by the time Crashlytics sends the report from the device. Keep this balance in mind when logging NSErrors and using logs and custom keys in your app.

Performance considerations

Keep in mind that logging an NSError can be fairly expensive. At the time you make the call, Crashlytics captures the current thread’s call stack using a process called stack unwinding. This process can be CPU and I/O intensive, particularly on architectures that support DWARF unwinding (arm64 and x86). After the unwind is complete, the information is written to disk synchronously. This prevents data loss if the next line were to crash.

While it is safe to call this API on a background thread, remember that dispatching this call to another queue loses the context of the current stack trace.

What about NSExceptions?

Crashlytics doesn’t offer a facility for logging and recording NSException instances directly. Generally speaking, the Cocoa and Cocoa Touch APIs are not exception-safe. That means the use of @catch can have very serious unintended side-effects in your process, even when used with extreme care. You should never use @catch statements in your code. Please refer to Apple’s documentation on the topic.

Enable opt-in reporting

By default, Crashlytics automatically collects crash reports for all your app's users. To give users more control over the data they send, you can enable opt-in reporting for your users by disabling automatic collection and initializing Crashlytics only for selected users:

  1. Turn off automatic collection by adding a new key to your Info.plist file:

    • Key: FirebaseCrashlyticsCollectionEnabled
    • Value: false
  2. Enable collection for select users by calling the Crashlytics data collection override at runtime. The override value persists across launches of your app so Crashlytics can automatically collect reports for future launches of that app instance. To opt out of automatic crash reporting, pass false as the override value.

    Swift
    Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
    Objective-C
    [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES];

Manage Crash Insights data

Crash Insights helps you resolve issues by comparing your anonymized stack traces to traces from other Firebase apps and letting you know if your issue is part of a larger trend. For many issues, Crash Insights even provides resources to help you debug the crash.

Crash Insights uses aggregated crash data to identify common stability trends. If you’d prefer not to share your app's data, you can opt-out of Crash Insights from the Crash Insights menu at the top of your Crashlytics issue list in the Firebase console.