On November 15, the Fabric SDK and old versions of the Crashlytics SDK will stop sending crashes. To keep getting crash reports, make sure you upgrade to the latest version of the Firebase Crashlytics SDK (v17.0.0+ for Android, v4.0.0+ for iOS, or v6.15.0+ for Unity).

Customize your Firebase Crashlytics crash reports

Firebase Crashlytics can work with very little setup on your part. As soon as you add the SDK, Crashlytics gets to work sending crash reports to the Firebase console.

To give you more insight into crash reports, Firebase Crashlytics provides four logging mechanisms right 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.

Start with [CrashlyticsKit setObjectValue:forKey:] or one of the related methods:

- (void)setObjectValue:(id)value forKey:(NSString *)key;

// calls -description on value, perfect for NSStrings!
- (void)setIntValue:(int)value forKey:(NSString *)key;

- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;

- (void)setFloatValue:(float)value forKey:(NSString *)key;

Sometimes you need to change the existing key value. Call the same key, but replace the value, for example:

[CrashlyticsKit setIntValue:3 forKey:@"current_level"];
[CrashlyticsKit setObjectValue:@"logged_in" forKey:@"last_UI_action"];
Crashlytics.sharedInstance().setIntValue(42, forKey: "MeaningOfLife")
Crashlytics.sharedInstance().setObjectValue("Test value", forKey: "last_UI_action")

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 makes them visible in the Firebase console.


In Objective-C, use CLS_LOG to help pinpoint issues. It automatically includes information about the Objective-C class, method, and line number associated with the log.

CLS_LOG behavior changes depending on whether it's logging for a debug or a release build:

  • Debug builds: CLS_LOG passes through to NSLog so you can see the output in Xcode and on the device or simulator.
  • Release builds: To improve performance, CLS_LOG silences all other output and does not pass through NSLog.

Use CLS_LOG(format, ...) to log with CLS_LOG. For example:

CLS_LOG(@"Higgs-Boson detected! Bailing out... %@", attributesDict);

Browse the Crashlytics/Crashlytics.h header file for more details on how to use CLS_LOG.


In Swift, use CLSLogv or CLSNSLogv to help pinpoint issues.

There are two things to keep in mind when logging with CLSLogv and CLSNSLogv:

  • Your format argument must be a compile-time constant string. This is enforced by the compiler in Objective-C, but that protection is currently lost in bridging to Swift.
  • Store log values in an array, and call getVaList on that array to retrieve them.

For example:

func write(string: String) {
    CLSLogv("%@", getVaList([string]))
Swift string interpolation will not result in a compile-time constant string. Just like with printf and NSLog, using a non-constant string with CLSLog can result in a crash.


For more control, you can directly leverage CLSLog(format, ...) and CLSNSLog(format, ...). The latter passes through to NSLog so you can see the output in Xcode or on the device or simulator. CLSLog(format, ...) and CLSNSLog(format, ...) are thread safe. CLSLog is meant for logging information important to solving crashes. It should not be used to track in-app events.

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:

[CrashlyticsKit setUserIdentifier:@"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.

Log non-fatal exceptions

In addition to automatically reporting your app’s crashes, Crashlytics lets you log non-fatal exceptions.

On iOS, you do that by recording NSError objects, which Crashlytics reports and groups much like crashes:

[CrashlyticsKit 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 unpredicatable behavior and may require Crashlytics to limit reporting of logged errors for your app.

An NSError object has three arguments: domain: String, code: Int, and userInfo: [AnyHashable : Any]? = nil

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

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

Creates a new issue that is grouped by NSSomeErrorDomain and -1001. Additional logged errors that use the same domain and code values will be grouped under this issue.

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.

Data contained within the userInfo object are converted to key-value pairs and displayed in the keys/logs section within an individual issue.

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

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 because Crashlytics 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 CLSLog 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 will lose the context of the current stack trace.

What about NSExceptions?

Crashlytics doesn’t offer a facility for logging/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, Firebase 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 instead.

To do this, you have to disable automatic collection and initialize Crashlytics only for opt-in users.

  1. Turn off automatic collection with a new key to your Info.plist file:
    • Key: firebase_crashlytics_collection_enabled
    • Value: false
  2. Enable collection for selected users by initializing Crashlytics at runtime:
    [Fabric with:@[[Crashlytics class]]];

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.