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:
Objective-C
[CrashlyticsKit setIntValue:3 forKey:@"current_level"]; [CrashlyticsKit setObjectValue:@"logged_in" forKey:@"last_UI_action"];
Swift
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.
Objective-C
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 toNSLog
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 throughNSLog
.
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
.
Swift
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])) }
Advanced
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:
Objective-C
[CrashlyticsKit setUserIdentifier:@"123456789"];
Swift
Crashlytics.sharedInstance().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:
Objective-C
[CrashlyticsKit recordError:error];
Swift
Crashlytics.sharedInstance().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 code:-1001 userInfo:userInfo];
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.
Data contained within the userInfo
object are converted to key-value pairs and displayed in the keys/logs section within an individual issue.
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.
- Turn off automatic collection with a new key to your
Info.plist
file:- Key:
firebase_crashlytics_collection_enabled
- Value:
false
- Key:
- Enable collection for selected users by initializing Crashlytics at runtime:
Objective-C
[Fabric with:@[[Crashlytics class]]];
Swift
Fabric.with([Crashlytics.self])
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.