自定义 Firebase Crashlytics 崩溃报告


Crashlytics 信息中心内,您可以点击进入某个问题并获取详细的事件报告。您可以自定义这些报告,以便更好地了解应用中发生的情况以及向 Crashlytics 报告事件的环境。

  • 如果您的应用使用 Firebase SDK for Google Analytics,会自动获取面包屑导航日志。借助这些日志,您可以了解导致应用中发生 Crashlytics 收集的事件的用户操作。

  • 关闭自动崩溃报告并为您的用户启用自选式报告。请注意,默认情况下,Crashlytics 会自动为应用的所有用户收集崩溃报告。

添加自定义键

自定义键可以帮助您获取导致崩溃的应用特定状态。 您可以将任意键值对与您的崩溃报告相关联,然后使用自定义键在 Firebase 控制台中搜索和过滤崩溃报告。

  • Crashlytics 信息中心内,您可以搜索与自定义键匹配的问题。
  • 当您在控制台中查看某个特定问题时,可以查看每个事件关联的自定义键(在“键”子标签中),甚至可以按自定义键过滤事件(使用页面顶部的“过滤条件”菜单)。

使用 setCustomValue 方法可设置键值对。例如:

Swift

// Set int_key to 100.
Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set str_key to "hello".
Crashlytics.crashlytics().setCustomValue("hello", forKey: "str_key")

Objective-C

设置整数、布尔值或浮点数时,请以 @(value) 的形式提供值。

// Set int_key to 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set str_key to "hello".
[[FIRCrashlytics crashlytics] setCustomValue:@"hello" forKey:@"str_key"];

您还可以通过调用现有键并将其设置为不同的值,来修改现有键的值。例如:

Swift

Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set int_key to 50 from 100.
Crashlytics.crashlytics().setCustomValue(50, forKey: "int_key")

Objective-C

[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set int_key to 50 from 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(50) forKey:@"int_key"];

使用 setCustomKeysAndValues 方法(NSDictionary 作为唯一的参数)批量添加键值对:

Swift

let keysAndValues = [
                 "string key" : "string value",
                 "string key 2" : "string value 2",
                 "boolean key" : true,
                 "boolean key 2" : false,
                 "float key" : 1.01,
                 "float key 2" : 2.02
                ] as [String : Any]

Crashlytics.crashlytics().setCustomKeysAndValues(keysAndValues)

Objective-C

NSDictionary *keysAndValues =
    @{@"string key" : @"string value",
      @"string key 2" : @"string value 2",
      @"boolean key" : @(YES),
      @"boolean key 2" : @(NO),
      @"float key" : @(1.01),
      @"float key 2" : @(2.02)};

[[FIRCrashlytics crashlytics] setCustomKeysAndValues: keysAndValues];

添加自定义日志消息

为了了解导致崩溃的事件的更多背景信息,您可以向应用添加自定义 Crashlytics 日志。Crashlytics 会将日志与您的崩溃数据相关联,并将其显示在 Firebase 控制台Crashlytics 页面的日志标签页下。

Swift

使用 log()log(format:, arguments:) 可帮助查明问题。如果您希望获得包含消息的有用日志输出,那么您传递给 log() 的对象必须符合 CustomStringConvertible 属性。log() 会返回您为该对象定义的说明属性。例如:

Crashlytics.crashlytics().log("Higgs-Boson detected! Bailing out…, \(attributesDict)")

.log(format:, arguments:) 为调用 getVaList() 时返回的值设置格式。例如:

Crashlytics.crashlytics().log(format: "%@, %@", arguments: getVaList(["Higgs-Boson detected! Bailing out…", attributesDict]))

如需详细了解如何使用 log()log(format:, arguments:),请参阅 Crashlytics 参考文档

Objective-C

使用 loglogWithFormat 可帮助查明问题。请注意,如果您希望获得包含消息的有用日志输出,那么您传递给这两种方法的对象必须替换 description 实例属性。 例如:

[[FIRCrashlytics crashlytics] log:@"Simple string message"];

[[FIRCrashlytics crashlytics] logWithFormat:@"Higgs-Boson detected! Bailing out... %@", attributesDict];

[[FIRCrashlytics crashlytics] logWithFormat:@"Logging a variable argument list %@" arguments:va_list_arg];

如需详细了解如何使用 loglogWithFormat,请参阅 Crashlytics 参考文档

设置用户标识符

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

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

Swift

Crashlytics.crashlytics().setUserID("123456789")

Objective-C

[[FIRCrashlytics crashlytics] setUserID:@"123456789"];

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

报告非严重异常

除了自动报告您应用中出现的崩溃,Crashlytics 还可帮您记录非严重异常,并在应用下次启动时将这些异常记录发送给您。

如需记录非严重异常,您可以使用 recordError 方法记录 NSError 对象。recordError 通过调用 [NSThread callStackReturnAddresses] 来捕获线程的调用栈。

Swift

Crashlytics.crashlytics().record(error: error)

Objective-C

[[FIRCrashlytics crashlytics] recordError:error];

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

每个 NSError 对象都具有三个参数:

  • domain: String
  • code: Int
  • userInfo: [AnyHashable : Any]? = nil

与根据调用栈轨迹分析结果进行分组的严重崩溃不同,记录的错误是根据 domaincode 来分组的。这是严重崩溃与记录的错误之间的重要区别。例如:

Swift

let userInfo = [
  NSLocalizedDescriptionKey: NSLocalizedString("The request failed.", comment: ""),
  NSLocalizedFailureReasonErrorKey: NSLocalizedString("The response returned a 404.", comment: ""),
  NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Does this page exist?", comment: ""),
  "ProductID": "123456",
  "View": "MainView"
]

let error = NSError.init(domain: NSCocoaErrorDomain,
                         code: -1001,
                         userInfo: userInfo)

Objective-C

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",
  @"View": @"MainView",
};

NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
                                     code:-1001
                                 userInfo:userInfo];

在您记录上述错误时,系统会创建一个根据 NSSomeErrorDomain-1001 分组的新问题。如果记录的其他错误具有与该错误相同的网域和代码值,那么它们都会划分到这个问题下。userInfo 对象中包含的数据会被转换为键值对,并显示在具体问题内的键/日志部分中。

日志和自定义键

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

性能考虑因素

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

尽管在后台线程中调用此 API 是安全的,但请注意,如果将此调用分发到另一个队列中,当前调用栈轨迹的背景信息将会丢失。

那 NSException 呢?

Crashlytics 不提供直接记录 NSException 实例以及为其记录日志的功能。一般来说,Cocoa API 和 Cocoa Touch API 并不具有异常安全性。这意味着使用 @catch 可能会在您的进程中引发非常严重的意外副作用,即使在极其谨慎的情况下使用它也是如此。切勿在代码中使用 @catch 语句。请参阅有关该主题的 Apple 文档

自定义调用栈轨迹

如果您的应用在非原生环境(如 C++ 或 Unity)中运行,您可以使用 Exception Model API 以应用的原生异常格式报告崩溃元数据。报告的异常会被标为非严重异常。

Swift

var  ex = ExceptionModel(name:"FooException", reason:"There was a foo.")
ex.stackTrace = [
  StackFrame(symbol:"makeError", file:"handler.js", line:495),
  StackFrame(symbol:"then", file:"routes.js", line:102),
  StackFrame(symbol:"main", file:"app.js", line:12),
]

crashlytics.record(exceptionModel:ex)

Objective-C

FIRExceptionModel *model =
    [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."];
model.stackTrace = @[
  [FIRStackFrame stackFrameWithSymbol:@"makeError" file:@"handler.js" line:495],
  [FIRStackFrame stackFrameWithSymbol:@"then" file:@"routes.js" line:102],
  [FIRStackFrame stackFrameWithSymbol:@"main" file:@"app.js" line:12],
];

[[FIRCrashlytics crashlytics] recordExceptionModel:model];

自定义栈帧也可以仅使用地址进行初始化:

Swift

var  ex = ExceptionModel.init(name:"FooException", reason:"There was a foo.")
ex.stackTrace = [
  StackFrame(address:0xfa12123),
  StackFrame(address:12412412),
  StackFrame(address:194129124),
]

crashlytics.record(exceptionModel:ex)

Objective-C

FIRExceptionModel *model =
    [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."];
model.stackTrace = @[
  [FIRStackFrame stackFrameWithAddress:0xfa12123],
  [FIRStackFrame stackFrameWithAddress:12412412],
  [FIRStackFrame stackFrameWithAddress:194129124],
];


[[FIRCrashlytics crashlytics] recordExceptionModel:model];

获取面包屑导航日志

面包屑导航日志可让您更好地了解用户与您的应用进行,导致崩溃、非严重或 ANR 事件的互动。在尝试重现和调试问题时,这些日志会很有用。

面包屑导航日志由 Google Analytics 提供支持,因此为了获取面包屑导航日志,您需要为 Firebase 项目启用 Google Analytics ,并添加 Firebase SDK for Google Analytics 到您的应用中。满足这些要求后,当您查看问题的详细信息时,面包屑导航日志便会随事件数据一起自动包含在日志标签页中。

Analytics SDK 会自动记录 screen_view 事件,从而使面包屑导航日志能够显示发生崩溃事件、非严重事件或 ANR 事件之前浏览过的屏幕的列表。screen_view 面包屑导航日志包含 firebase_screen_class 参数。

面包屑导航日志还会填充您在用户会话中手动记录的任何自定义事件,包括事件的参数数据。此数据有助于显示导致崩溃事件、非严重事件或 ANR 事件的一系列用户操作。

请注意,您可以控制 Google Analytics 数据的收集和使用,其中包括填充面包屑导航日志的数据。

启用自选式报告

默认情况下,Crashlytics 会自动为应用的所有用户收集崩溃报告。为了让用户对其发送的数据有更多的控制权,您可以启用自选式报告功能,方法是停用自动报告功能,并且仅当您在代码中进行选择后向 Crashlytics 发送数据:

  1. 如需停用自动收集功能,请在 Info.plist 文件中添加新键:

    • 键:FirebaseCrashlyticsCollectionEnabled
    • 值:false
  2. 在运行时调用 Crashlytics 数据收集替换,从而为选定用户启用收集功能。 在应用的多次启动之间,替换值会保持不变,因此 Crashlytics 可以自动收集相关报告。

    如需停用自动崩溃报告功能,请将 false 作为替换值传递。将此设置为 false 时,新值要等到应用下次运行时才会生效。

    Swift

    Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)

    Objective-C

    [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES];

管理 Crash Insights 数据

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

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