Logged Errors

In addition to automatically reporting your app’s crashes, Crashlytics lets developers log non-fatal exceptions using NSError objects, which we report and group much like we do with crashes.

Using the API

You can record an NSError using this API:

[CrashlyticsKit recordError:error];
Crashlytics.sharedInstance().recordError(error)

When using the recordError method it is 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.

Warning

Developers should avoid using unique values, such as user ID, product ID, and timestamps, in the domain and code fields. Using unique values in these fields will cause a high cardinality of issues to be created 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.

Note

For any individual app session, only the most recent 8 logged NSErrors are stored.

Logs and Custom Keys

Just like crash reports, you can embed logs and custom keys to add context around why the NSError may have occurred. 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 we retrieve from disk are those that were written right up to the time of the crash. When you log an NSError, though, the app does not immediately terminate. Because we will only send the logged error report on the next app launch, and because we 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 we send the report from the device. Keep this balance in mind when logging NSErrors and using CLSLog and custom keys in your app.

Performance Considerations

You should keep in mind that logging an NSError can be fairly expensive. At the time you make this call, we capture 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.

Also, it is worth thinking about what you are trying to achieve with a logged error. If you are just interested in counting the number of times an event is occurring, consider using Answers custom events. These are a far less-expensive and more feature-rich way of counting occurrences.

What about NSExceptions?

We don’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. We recommend that you never use @catch statements in your code. Please refer to Apple’s documentation on the topic.