自定义您的 Firebase Crashlytics 崩溃报告

iOS
Android

您只需要对 Firebase Crashlytics 进行极少量设置就可以让其工作。在您添加 SDK 后,Crashlytics 就会立即开始将崩溃报告发送到 Firebase 控制台。

要对崩溃报告进行更精细的控制,请自定义您的 Crashlytics SDK 配置。例如,您可以为注重隐私保护的用户启用自选式报告,添加日志以跟踪恼人的错误等等。

启用自选式报告

默认情况下,Firebase Crashlytics 会自动为您的应用的所有用户收集崩溃报告。为了让用户对其发送的数据有更多的掌控权,您可以启用自选式报告功能。

为此,您必须停用数据自动收集,并只为选择发送报告的用户初始化 Crashlytics。

iOS
  1. 在您的 Info.plist 文件中,使用一个新的键来关闭自动收集功能:
    • 键:firebase_crashlytics_collection_enabled
    • 值:false
  2. 通过在运行时初始化 Crashlytics 来为选定的用户启用收集功能:
    Objective-C
    [Fabric with:@[[Crashlytics class]]];
    Swift
    Fabric.with([Crashlytics.self])
Android
  1. 使用 AndroidManifest.xml 文件中的 meta-data 标记来关闭自动收集功能:
    <meta-data
        android:name="firebase_crashlytics_collection_enabled"
        android:value="false" />
    
  2. 在应用的某个 Activity 中初始化 Crashlytics,从而为选定的用户启用收集功能:
    Fabric.with(this, new Crashlytics());
    
注意:一旦您在应用会话中对 Crashlytics 进行了初始化,就无法再停止 Crashlytics 报告功能。在您初始化 Crashlytics 之后,用户若要选择停用报告功能,则必须重启您的应用。

添加自定义日志

要为导致崩溃的事件提供更多背景信息,您可以将自定义 Crashlytics 日志添加到您的应用。Crashlytics 会将日志与您的崩溃数据相关联,并在 Firebase 控制台中显示这些日志。

iOS
Objective-C

在 Objective-C 中,使用 CLS_LOG 来帮助查明问题。它会自动包含与日志相关联的 Objective-C 类、方法和行号信息。

在不同的用途下,CLS_LOG 的行为也会不一样,具体取决于它是为调试版本还是发布版本记录日志:

  • 调试版本CLS_LOG 会传递至 NSLog,以便您可以在 Xcode 中以及设备或模拟器上查看输出结果。
  • 发布版本:为了提高性能,CLS_LOG 会关闭所有其他输出,并且不会传递至 NSLog

使用 CLS_LOG(format, ...) 即可通过 CLS_LOG 记录日志。例如:

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

浏览 Crashlytics/Crashlytics.h 头文件,以便详细了解如何使用 CLS_LOG

Swift

在 Swift 中,使用 CLSLogvCLSNSLogv 来帮助查明问题。

在使用 CLSLogvCLSNSLogv 记录日志时,需要注意以下两点:

  • 您的格式参数必须是一个编译时常量字符串。在 Objective-C 中,这是由编译器来保证的,但在过渡到 Swift 后暂不具备这项保护功能。
  • 将日志值存储在一个数组中,然后通过对该数组调用 getVaList 来检索这些值。

例如:

func write(string: String) {
    CLSLogv("%@", getVaList([string]))
}
Swift 字符串插值类型不会导致出现编译时常量字符串。就像使用 printf 和 NSLog 时一样,在 CLSLog 中使用非常量字符串可能会导致崩溃。

高级

如需更多控制手段,您可以直接利用 CLSLog(format, ...)CLSNSLog(format, ...)。后者会传递至 NSLog,以便您可以在 Xcode 中以及设备或模拟器上查看输出结果。CLSLog(format, ...)CLSNSLog(format, ...) 是线程安全的。CLSLog 用于记录对解决崩溃问题十分重要的信息,不应将其用来跟踪应用内事件。

Android

在 Android 上,使用 Crashlytics.log 来帮助查明问题。

Crashlytics.log 既可以将日志写入崩溃报告并执行 Log.println(),也可以只将日志写入下一份崩溃报告:

为了避免拖慢您的应用,Crashlytics 将日志大小限制为不超过 64kB。如果某个会话的日志量超过该限制,Crashlytics 会删除较早的日志条目。

添加自定义键

自定义键可以帮助您获取导致崩溃的应用的特定状态。您可以将任意键/值对与您的崩溃报告相关联,然后在 Firebase 控制台中查看这些键/值对。

iOS

[CrashlyticsKit setObjectValue:forKey:] 或以下某个相关方法开始:

- (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;

有时您需要更改现有的键值。为此,请调用相同的键,但将值替换掉,例如:

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")
Android

有五个用来设置键的方法。每一个方法处理的数据类型都不相同:

Crashlytics.setString(key, "foo" /* string value */);

Crashlytics.setBool(key, true /* boolean value */);

Crashlytics.setDouble(key, 1.0 /* double value */);

Crashlytics.setFloat(key, 1.0f /* float value */);

Crashlytics.setInt(key, 1 /* int value */);

重新设置某个键可以更新其对应的值,例如:

Crashlytics.setInt("current_level", 3);
Crashlytics.setString("last_UI_action", "logged_in");
Crashlytics 最多可支持 64 个键/值对。一旦达到此阈值后,系统就不会再保存更多的值。每个键/值对的大小上限为 1 kB。

设置用户 ID

要诊断某个问题,了解哪些用户遇到了特定的崩溃通常很有帮助。Crashlytics 提供了一种在崩溃报告中以匿名方式标识用户的方法。

iOS

要将用户 ID 添加到报告中,请以 ID 编号、令牌或哈希值的形式为每个用户分配一个唯一标识符:

Objective-C
[CrashlyticsKit setUserIdentifier:@"123456789"];
Swift
Crashlytics.sharedInstance().setUserIdentifier("123456789")

如果您在设置某个用户标识符后需要将其清除,请将该值重置为空白字符串。清除用户标识符不会移除现有的 Crashlytics 记录。如果您需要删除与用户 ID 关联的记录,请与 Firebase 支持团队联系

Android

要将用户 ID 添加到报告中,请以 ID 编号、令牌或哈希值的形式为每个用户分配一个唯一标识符:

Crashlytics.setUserIdentifier("user123456789");

如果您在设置某个用户标识符后需要将其清除,请将该值重置为空白字符串。清除用户标识符不会移除现有的 Crashlytics 记录。如果您需要删除与用户 ID 关联的记录,请与 Firebase 支持团队联系

记录非严重异常

除了自动报告您的应用中出现的崩溃,Crashlytics 还可以为您记录非严重异常。

iOS

在 iOS 上,您可以通过记录 NSError 对象来记录非严重异常,Crashlytics 对于这种对象的报告和分组方式与崩溃非常相似:

Objective-C
[CrashlyticsKit recordError:error];
Swift
Crashlytics.sharedInstance().recordError(error)

当使用 recordError 方法时,了解 NSError 结构以及 Crashlytics 如何使用相关数据对崩溃进行分组是非常重要的。如果对 recordError 方法的使用不正确,可能会导致不可预知的行为,并且可能需要限制 Crashlytics 报告为您的应用所记录的错误的功能。

一个 NSError 对象有三个参数:domain: Stringcode: IntuserInfo: [AnyHashable : Any]? = nil

与根据堆栈跟踪分析结果进行分组的严重崩溃不同,记录的错误是根据 NSError domaincode 来分组的。这是严重崩溃与记录的错误之间的重要区别。例如,日志中记录如下错误:

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];

这会创建一个根据 NSSomeErrorDomain-1001 分组的新问题。其他使用相同的域和代码值的已记录错误将划分到这个问题下。

避免在域和代码字段中使用唯一值,例如用户 ID、产品 ID 和时间戳。在这些字段中使用唯一值会导致问题的基数增大,并且可能导致 Crashlytics 需要在您的应用中对报告记录的错误的功能进行限制。应该将唯一值添加到 userInfo Dictionary 对象中。

userInfo 对象中包含的数据被转换为键值对,并显示在单个问题内的键/日志部分中。

Crashlytics 只存储某个给定的应用会话中最近的 8 次异常。如果您的应用在一个会话中抛出的异常数超过了 8 个,则较早的异常会被舍弃。

日志和自定义键

与崩溃报告类似,您可以嵌入日志和自定义键以便将背景信息添加到 NSError 中。但是,在需要附加什么日志方面,崩溃和记录的错误是有区别的。当崩溃发生并且应用重新启动时,Crashlytics 从磁盘中检索到的日志是截至崩溃那一刻所写入的日志。而当您记录一个 NSError 时,应用并不会立即终止。由于 Crashlytics 只在下一次应用启动时才发送记录的错误报告,并且 Crashlytics 必须限制为日志分配的磁盘空间量,因此有可能出现以下情况:在记录某个 NSError 之后日志空间就已经满了,以至于到了 Crashlytics 从设备发送报告的时候,所有相关的日志都已经被轮替掉了。在记录 NSError 以及在您的应用中使用 CLSLog 和自定义键时,请注意对此进行权衡。

性能考虑因素

请注意,记录 NSError 的开销可能会相当大。当您进行调用时,Crashlytics 会通过一个名为 Stack Unwinding(堆栈展开)的过程来捕获当前线程的调用堆栈。这个过程可能需要占用大量的 CPU 和 I/O 资源,尤其是在支持 DWARF 展开的架构(arm64 和 x86)上。展开过程完成后,信息将同步写入到磁盘中。这可以防止在下一行代码发生崩溃时出现数据丢失现象。

尽管在后台线程中调用此 API 是安全的,但请注意,如果将此调用分发到另一个队列中,则将会丢失当前堆栈跟踪的上下文信息。

那 NSException 呢?

Crashlytics 不提供直接记录/记载 NSException 实例的功能。一般来说,Cocoa API 和 Cocoa Touch API 并不具有异常安全性。这意味着使用 @catch 可能会在您的进程中产生非常严重的意想不到的副作用,即使在极其谨慎的情况下使用它也是如此。您任何时候都不应在自己的代码中使用 @catch 语句。请参阅有关该主题的 Apple 文档

Android

在 Android 上,这意味着您可以在应用的 catch 块中记录已捕获到的异常:

try {
    methodThatThrows();
} catch (Exception e) {
    Crashlytics.logException(e);
    // handle your exception here
}

所有记录的异常在 Firebase 控制台中均显示为非严重问题。问题摘要中会包含您通常从崩溃中可以获得的所有状态信息,以及按 Android 版本和硬件设备细分的数据。

Crashlytics 在一个专用的后台线程中处理异常,所以对您的应用性能的影响极小。为了减少用户的网络流量,Crashlytics 会将已记录的异常汇集到一起,并在下次应用启动时批量发送这些异常。

Crashlytics 只存储某个给定的应用会话中最近的 8 次异常。如果您的应用在一个会话中抛出的异常数超过了 8 个,则较早的异常会被舍弃。

管理 Crash Insights 数据

Crash Insights 会比较您的匿名堆栈跟踪和来自其他 Firebase 应用的跟踪,并让您知道您的问题是否属于个例,从而帮助您解决问题。对于许多问题,Crash Insights 甚至会提供资源来帮助您调试崩溃。

Crash Insights 使用汇总的崩溃数据来识别常见的稳定性趋势。如果您不想分享应用的数据,则可以在 Crash Insights 菜单中选择停用 Crash Insights。此菜单位于 Firebase 控制台的 Crashlytics 问题列表顶部。

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面