获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

自定义您的 Firebase Crashlytics 崩溃报告

本指南介绍如何使用 Firebase Crashlytics SDK 自定义崩溃报告。默认情况下,Crashlytics 会自动为您应用的所有用户收集崩溃报告(您可以关闭自动崩溃报告并为您的用户启用选择加入报告)。 Crashlytics 提供了四种开箱即用的日志记录机制:自定义键自定义日志用户标识符捕获的异常

添加自定义键

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

  • Crashlytics 仪表板中,您可以搜索与自定义键匹配的问题。
  • 当您在控制台中查看特定问题时,您可以查看每个事件的关联自定义键(子选项卡),甚至可以通过自定义键过滤事件(页面顶部的过滤器菜单)。

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

迅速

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

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

迅速

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

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

迅速

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 页面中的日志选项卡下。

迅速

使用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 号、令牌或散列值的形式为每个用户分配一个唯一标识符:

迅速

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

Objective-C

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

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

报告非致命异常

除了自动报告您的应用程序崩溃之外,Crashlytics 还允许您记录非致命异常并在您的应用程序下次启动时将它们发送给您。

您可以通过使用recordError方法记录NSError对象来记录非致命异常。 recordError通过调用[NSThread callStackReturnAddresses]捕获线程的调用堆栈。

迅速

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分组。这是致命崩溃和记录错误之间的重要区别。例如:

迅速

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 使用称为堆栈展开的过程捕获当前线程的调用堆栈。这个过程可能是 CPU 和 I/O 密集型的,特别是在支持 DWARF 展开的架构(arm64 和 x86)上。 unwind完成后,信息同步写入磁盘。如果下一行崩溃,这可以防止数据丢失。

虽然在后台线程上调用此 API 是安全的,但请记住,将此调用分派到另一个队列会丢失当前堆栈跟踪的上下文。

NSExceptions 呢?

Crashlytics 不提供直接记录和记录NSException实例的工具。一般来说,Cocoa 和 Cocoa Touch API 不是异常安全的。这意味着使用@catch可能会在您的过程中产生非常严重的意外副作用,即使在非常小心使用时也是如此。永远不要在代码中使用@catch语句。请参阅Apple 关于该主题的文档

自定义堆栈跟踪

如果您的应用在非原生环境(例如 C++ 或 Unity)中运行,您可以使用异常模型 API 以应用的原生异常格式报告崩溃元数据。报告的异常被标记为非致命的。

迅速

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

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

迅速

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

启用选择加入报告

默认情况下,Crashlytics 会自动为您应用的所有用户收集崩溃报告。为了让用户更好地控制他们发送的数据,您可以通过禁用自动报告并仅在您选择在代码中将数据发送到 Crashlytics 来启用选择报告:

  1. 通过向Info.plist文件添加新密钥来关闭自动收集:

    • 键: FirebaseCrashlyticsCollectionEnabled
    • 值: false
  2. 通过在运行时调用 Crashlytics 数据收集覆盖来为选定用户启用收集。覆盖值在您的应用程序启动期间保持不变,因此 Crashlytics 可以自动收集报告。

    要选择退出自动崩溃报告,请将false作为覆盖值传递。当设置为false时,新值将在应用程序的下一次运行之前应用。

    迅速

    Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)

    Objective-C

    [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES];

管理崩溃洞察数据

Crash Insights 通过将匿名堆栈跟踪与来自其他 Firebase 应用的跟踪进行比较,并让您知道您的问题是否属于更大趋势的一部分,从而帮助您解决问题。对于许多问题,Crash Insights 甚至提供资源来帮助您调试崩溃。

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