自定义您的 Firebase Crashlytics 崩溃报告

iOS
Android
Unity [测试版]

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

为了让您更深入地了解崩溃报告,Firebase 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

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

Java
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 */);

Kotlin
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 */)

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

Java
Android

Crashlytics.setInt("current_level", 3);
Crashlytics.setString("last_UI_action", "logged_in");

Kotlin
Android

Crashlytics.setInt("current_level", 3)
Crashlytics.setString("last_UI_action", "logged_in")
Unity [测试版]

如果调用了多次,现有键的新值将更新该值,并且在记录崩溃时系统仅会捕获最新值。

Crashlytics.SetCustomKey(string key, string value);

添加自定义日志消息

要为导致崩溃的事件提供更多背景信息,您可以向您的应用添加自定义 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(),也可以只将日志写入下一个崩溃报告:

Unity [测试版]

记录的消息与您的崩溃数据相关联,并在您查看特定崩溃时显示在 Firebase Crashlytics 信息中心中。

Crashlytics.Log(string message);

设置用户标识符

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

iOS

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

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

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

Android

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

Java
Android

Crashlytics.setUserIdentifier("user123456789");

Kotlin
Android

Crashlytics.setUserIdentifier("user123456789")

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

Unity [测试版]

您可以使用 ID 编号、令牌或散列值来唯一标识应用的最终用户,而不会披露或传输他们的任何个人信息。您也可以通过将相应值设置为空字符串来清除该值。当您查看特定崩溃时,该值将显示在 Firebase Crashlytics 信息中心中。

Crashlytics.SetUserId(string identifier);

记录非严重异常

除了自动报告您的应用中出现的崩溃,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 块中记录已捕获到的异常:

Java
Android

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

Kotlin
Android

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

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

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

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

您可以通过 C# 使用以下方法记录自定义异常:

Crashlytics.LogException(Exception ex);

自定义异常可包含在您应用的 try/catch 块中:

try {
    myMethodThatThrows();
} catch (Exception e) {
   Crashlytics.LogException(e);
   // handle your exception here!
}

启用自选式报告

默认情况下,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,从而为选定的用户启用收集功能:

    Java
    Android

    Fabric.with(this, new Crashlytics());

    Kotlin
    Android

    Fabric.with(this, Crashlytics())
注意:一旦您在应用会话中对 Crashlytics 进行了初始化,就无法再停止 Crashlytics 报告功能。在您初始化 Crashlytics 之后,用户若要选择停用报告功能,则必须重启您的应用。
Unity [测试版]
Crashlytics.IsCrashlyticsCollectionEnabled = false;

以下示例演示了如何将此功能附加到界面元素:

public class DataCollectionToggleScript : MonoBehaviour {

  public Toggle DataCollectionToggle;

  void Start () {
    Toggle toggle = DataCollectionToggle.GetComponent<Toggle>();
    toggle.isOn = Crashlytics.IsCrashlyticsCollectionEnabled;
    toggle.onValueChanged.AddListener(delegate {
      ToggleValueChanged(toggle);
    });
  }

  void ToggleValueChanged(Toggle toggle){
    Crashlytics.IsCrashlyticsCollectionEnabled = toggle.isOn;
  }
}

没有看到 Fabric Crashlytics 中的等效代码?请查看我们的 Unity API 变更页面,了解 Crashlytics 如何从 Fabric Crashlytics 演变为 Firebase Crashlytics。

管理 Crash Insights 数据

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

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

发送以下问题的反馈:

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