将 Firebase Crashlytics 数据导出到 BigQuery

您可以将 Crashlytics 数据导出到 BigQuery 以便进一步分析。借助 BigQuery,您可以使用 BigQuery SQL 来分析数据,将数据导出至其他云提供商,并搭配 Google 数据洞察将数据用于实现可视化和自定义信息中心。

启用 BigQuery Export

  1. 进入 Firebase 控制台的集成页面。
  2. BigQuery 卡片中,点击关联
  3. 按照屏幕上的说明启用 BigQuery。

当您将项目关联到 BigQuery 时:

  • Firebase 会安排每日将您的数据从 Firebase 项目同步到 BigQuery。
  • 默认情况下,项目中的所有应用都会关联到 BigQuery,并且您此后向项目中添加的所有应用都会自动关联到 BigQuery。您能控制哪些应用可发送数据
  • Firebase 会将您现有数据的副本导出到 BigQuery。对于每个关联的应用,这包括一个批处理表,其中含有每日同步的数据。
  • 如果启用了 Crashlytics BigQuery 流式导出,所有关联的应用都将具有一个包含持续更新数据的实时表

如需停用 BigQuery Export,请在 Firebase 控制台中取消关联您的项目

哪些数据会导出至 BigQuery?

Firebase Crashlytics 数据会导出到名为 firebase_crashlytics 的 BigQuery 数据集。默认情况下,系统将在 Crashlytics 数据集内为项目中的每个应用分别创建表。Firebase 会根据应用的软件包标识符为这些表命名:将英文句点转换为下划线,并在末尾处附加平台名称。

例如,ID 是 com.google.test 的应用的数据会位于名为 com_google_test_ANDROID 的表中。此批处理表每天更新一次。如果您启用了 Crashlytics BigQuery 流式导出,Firebase Crashlytics 数据也将实时流式传输到 com_google_test_ANDROID_REALTIME

表中的每一行均表示应用中发生的事件,包括崩溃、非严重错误和 ANR。

启用 Crashlytics BigQuery 流式导出

您可以使用 BigQueryStreaming 实时流式传输您的 Crashlytics 数据。 您可以将其用于任何需要实时数据的用途,例如在实时信息中心内展示信息、实时观察发布情况,或者监控触发提醒和自定义工作流的应用问题。

Crashlytics BigQuery 流式导出不适用于 BigQuery 沙盒。

启用 Crashlytics BigQuery 流式导出后,除了批处理表之外,您还会获得一个实时表。您应该了解,这两种表之间有如下区别:

批处理表 实时表
  • 每天导出一次数据
  • 在将数据批量写入 BigQuery 之前持久存储事件
  • 最多可回填 30 天前的数据
  • 实时导出数据
  • 不提供数据回填

批处理表非常适合用于长期分析以及识别随时间推移而变化的趋势,因为我们在写入事件之前会先将数据持久存储,而且最多可在表中回填 30 天前的事件数据。在将数据写入实时表时,我们会立即将其写入 BigQuery,因此这种表非常适合用于实时信息中心和自定义提醒。您可以使用拼接查询将这两种表结合使用,从而同时获享两者的优势。请参阅下面的查询示例 9

默认情况下,实时表的分区过期时间为 30 天。如需了解如何修改此设置,请参阅更新分区过期时间

启用 Crashlytics BigQuery 流式传输

如需启用流式传输,请前往 BigQuery 集成页面的 Crashlytics 部分,然后选中包含流式传输复选框。

数据洞察模板

如需在数据洞察模板中启用实时数据,请按照使用数据洞察直观呈现导出的 Crashlytics 数据中的说明操作。

视图

您可以使用 BigQuery 界面将如下示例查询转换为视图。如需查看详细说明,请参阅创建视图

您可以对导出的数据执行哪些操作?

BigQuery 导出的数据包含原始崩溃数据,其中包括设备类型、操作系统、异常(Android 应用)或错误(Apple 应用)和 Crashlytics 日志,以及其他数据。

处理 BigQuery 中的 Firebase Crashlytics 数据

以下示例演示了您可以对 Crashlytics 数据运行的查询。 Crashlytics 信息中心不提供这些查询生成的报告。

Crashlytics 查询示例

以下示例说明了如何生成报告,从而将崩溃事件数据汇总为更易理解的摘要。

示例 1:每天的崩溃次数

在努力解决尽可能多的 bug 之后,开发负责人认为其团队终于做好了准备,可以推出他们全新的照片共享应用。在推出之前,他们想检查一下过去一个月内每天的崩溃次数,以确保这次大规模的纠错让应用的运行更加稳定了:

SELECT
  COUNT(DISTINCT event_id) AS number_of_crashes,
  FORMAT_TIMESTAMP("%F", event_timestamp) AS date_of_crashes
FROM
 `projectId.firebase_crashlytics.package_name_ANDROID`
GROUP BY
  date_of_crashes
ORDER BY
  date_of_crashes DESC
LIMIT 30;

示例 2:查找最普遍的崩溃

为了适当排定生产计划的优先级,项目经理想要找出其产品中 10 个最普遍的崩溃。为此,他们生成了一个查询来提供相关的数据点:

SELECT
  DISTINCT issue_id,
  COUNT(DISTINCT event_id) AS number_of_crashes,
  COUNT(DISTINCT installation_uuid) AS number_of_impacted_user,
  blame_frame.file,
  blame_frame.line
FROM
  `projectId.firebase_crashlytics.package_name_ANDROID`
WHERE
  event_timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(),INTERVAL 168 HOUR)
  AND event_timestamp < CURRENT_TIMESTAMP()
GROUP BY
  issue_id,
  blame_frame.file,
  blame_frame.line
ORDER BY
  number_of_crashes DESC
LIMIT 10;

示例 3:10 大易崩溃设备

秋季是新手机的发布季!而开发者们知道,这也意味着又到了解决新设备特有的问题的季节。为了针对很可能出现的兼容性问题而未雨绸缪,他们编写了一个查询,可以识别过去一周里出现最多崩溃的 10 款设备:

SELECT
  device.model,
COUNT(DISTINCT event_id) AS number_of_crashes
FROM
  `projectId.firebase_crashlytics.package_name_ANDROID`
WHERE
  event_timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 168 HOUR)
  AND event_timestamp < CURRENT_TIMESTAMP()
GROUP BY
  device.model
ORDER BY
  number_of_crashes DESC
LIMIT 10;

示例 4:按自定义键过滤

游戏开发者想知道游戏中崩溃最频繁的是哪个关卡。为了跟踪该统计信息,他们设置了自定义 Crashlytics 键 current_level,并在用户每次进入新的关卡时更新该键。

Objective-C

CrashlyticsKit setIntValue:3 forKey:@"current_level";

Swift

Crashlytics.sharedInstance().setIntValue(3, forKey: "current_level");

Java

Crashlytics.setInt("current_level", 3);

现在 BigQuery 导出数据中包含该键,他们编写了一个查询来报告与各个崩溃事件相关联的 current_level 值的分布情况:

SELECT
COUNT(DISTINCT event_id) AS num_of_crashes,
  value
FROM
  `projectId.firebase_crashlytics.package_name_ANDROID`
UNNEST(custom_keys)
WHERE
  key = "current_level"
GROUP BY
  key,
  value
ORDER BY
  num_of_crashes DESC

示例 5:用户 ID 提取

开发者有一个处于抢先体验阶段的应用。该应用的大多数用户都很喜欢它,但有三位用户遇到了异常数量的崩溃。为了确定该问题的根源,他们编写了一个查询,按照用户 ID 拉取这些用户的所有崩溃事件:

SELECT *
FROM
  `projectId.firebase_crashlytics.package_name_ANDROID`
WHERE
  user.id IN ("userid1", "userid2", "userid3")
ORDER BY
  user.id
 

示例 6:查找面临特定崩溃问题的所有用户

开发者向一组 Beta 版测试人员发布的版本中包含一个严重 bug。该团队可以使用上面示例 2 中的查询来找出这一特定崩溃问题的 ID。现在,开发者想要运行查询来提取受此崩溃影响的应用用户列表:

SELECT user.id as user_id
FROM
  `projectId.firebase_crashlytics.package_name_ANDROID`
WHERE
  issue_id = "YOUR_ISSUE_ID"
  AND application.display_version = ""
  AND user.id != ""
ORDER BY
  user.id;

示例 7:受崩溃问题影响的用户数量,按国家/地区细分

开发团队在发布新版本期间发现了一个严重 bug。他们可以使用上面示例 2 中的查询来找出这一特定崩溃问题的 ID。该团队现在想要确认该崩溃是否已蔓延至全球不同国家/地区的用户。

要编写此查询,团队将需要执行以下操作:

  1. 为 Google Analytics 启用 BigQuery Export。请参阅将项目数据导出到 BigQuery

  2. 更新应用以将用户 ID 传递到 Google Analytics SDK 和 Crashlytics SDK。

    Objective-C
    CrashlyticsKit setUserIdentifier:@"123456789";
    FIRAnalytics setUserID:@"12345678 9";
    
    Swift
    Crashlytics.sharedInstance().setUserIdentifier("123456789");
    Analytics.setUserID("123456789");
    
    Java
    Crashlytics.setUserIdentifier("123456789");
    mFirebaseAnalytics.setUserId("123456789");
    
  3. 编写一个查询,根据用户 ID 字段将 Google Analytics BigQuery 数据集中的事件与 Crashlytics BigQuery 数据集中的崩溃联接到一起:

    SELECT DISTINCT c.issue_id, a.geo.country, COUNT(DISTINCT c.user.id) as num_users_impacted
    FROM `projectId.firebase_crashlytics.package_name_ANDROID` c
    INNER JOIN  `projectId.analytics_YOUR_TABLE.events_*` a on c.user.id = a.user_id
    WHERE
     c.issue_id = "YOUR_ISSUE_ID"
     AND a._TABLE_SUFFIX BETWEEN '20190101'
     AND '20200101'
    GROUP BY
     c.issue_id,
     a.geo.country,
     c.user.id
    

示例 8:今天到现在为止的 5 大问题

需要启用 Crashlytics BigQuery 流式导出

SELECT
  issue_id,
  COUNT(DISTINCT event_id) AS events
FROM
  `your_project.firebase_crashlytics.package_name_ANDROID_REALTIME`
WHERE
  DATE(event_timestamp) = CURRENT_DATE()
GROUP BY
  issue_id
ORDER BY
  events DESC
LIMIT
  5;

示例 9:自 DATE 起至今(包括今天)的 5 大问题

需要启用 Crashlytics BigQuery 流式导出。

在此示例中,我们结合使用了批处理表和实时表,以便将实时信息添加到可靠的批量数据中。由于 event_id 是主键,因此我们可以使用 DISTINCT event_id 对两个表中的任何常见事件进行重复数据删除。

SELECT
  issue_id,
  COUNT(DISTINCT event_id) AS events
FROM (
  SELECT
    issue_id,
    event_id,
    event_timestamp
  FROM
    `your_project.firebase_crashlytics.package_name_ANDROID_REALTIME`
  UNION ALL
  SELECT
    issue_id,
    event_id,
    event_timestamp
  FROM
    `your_project.firebase_crashlytics.package_name_ANDROID`)
WHERE
  event_timestamp >= "2020-01-13"
GROUP BY
  issue_id
ORDER BY
  events DESC
LIMIT
  5;

了解 BigQuery 中的 Firebase Crashlytics 架构

将 Crashlytics 与 BigQuery 关联后,Firebase 会导出最近的事件(崩溃、非严重错误和 ANR),包括执行关联操作之前几天(最多两天)的事件,并提供回填选项(最多可回填 30 天前的数据)。

从关联起直至您停用该关联,Firebase 每天都会导出 Crashlytics 事件。每次导出后,可能需要几分钟时间才能在 BigQuery 中找到这些数据。

数据集

Firebase Crashlytics 会为 Crashlytics 数据在 BigQuery 中创建一个新的数据集。该数据集会涵盖您的整个项目(即使它有多个应用)。

除非您选择停止导出应用数据,否则 Firebase Crashlytics 会在数据集中为项目的每个应用创建一个表。Firebase 会根据应用的软件包标识符为这些表命名:将英文句点转换为下划线,并在末尾处附加平台名称。

例如,ID 为 com.google.test 的 Android 应用的数据会存储在名为 com_google_test_ANDROID 的表中,而实时数据(如果已启用)会存储在名为 com_google_test_ANDROID_REALTIME 的表中

除了开发者自定义的任何 Crashlytics 键之外,表还包含一组标准的 Crashlytics 数据。

表中的每一行都表示应用遇到的一个错误。

对于崩溃、非严重错误和 ANR,表中的列完全相同。如果启用了 Crashlytics BigQuery 流式导出,则实时表中的列与批处理表中的列相同。下面列出了导出结果中的各个列。

无堆栈轨迹

表示无堆栈轨迹事件的行中存在的列。

字段名称 数据类型 说明
platform STRING Apple 或 Android 应用
bundle_identifier STRING 软件包 ID,例如 com.google.gmail
event_id STRING 事件的唯一 ID
is_fatal BOOLEAN 应用是否崩溃
error_type STRING 事件的错误类型(FATAL、NON_FATAL、ANR)
issue_id STRING 与事件相关的问题
variant_id STRING 与此事件相关的问题变体
请注意,并非所有事件都有关联的问题变体。
event_timestamp TIMESTAMP 事件发生的时间
device RECORD 发生事件的设备
device.manufacturer STRING 设备制造商
device.model STRING 设备型号
device.architecture STRING X86_32、X86_64、ARMV7、ARM64、ARMV7S 或 ARMV7K
memory RECORD 设备的内存状态
memory.used INT64 使用的内存字节数
memory.free INT64 剩余的内存字节数
storage RECORD 设备的永久性存储空间
storage.used INT64 使用的存储空间字节数
storage.free INT64 剩余的存储空间字节数
operating_system RECORD 设备上的操作系统的详细信息
operating_system.display_version STRING 设备上的操作系统的版本
operating_system.name STRING 设备上的操作系统的名称
operating_system.modification_state STRING 设备是否已经过修改,例如已越狱/已启用 root 权限 (MODIFIED 或 UNMODIFIED)
operating_system.type STRING 在设备上运行的操作系统的类型(例如 IOS、MACOS);仅适用于 Apple 平台应用
operating_system.device_type STRING 设备的类型(例如 MOBILE、TABLET、TV 等);也称为“设备类别”
应用 RECORD 引发事件的应用
application.build_version STRING 应用的版本号
application.display_version STRING
user RECORD 可选:收集的应用用户信息
user.name STRING 可选:用户的姓名
user.email STRING 可选:用户的电子邮件地址
user.id STRING 可选:与用户关联的特定应用的 ID
custom_keys REPEATED RECORD 开发者定义的键值对
custom_keys.key STRING 开发者定义的密钥
custom_keys.value STRING 开发者定义的值
installation_uuid STRING 标识唯一应用和设备安装的 ID
crashlytics_sdk_versions STRING 生成事件的 Crashlytics SDK 版本
app_orientation STRING PORTRAIT、LANDSCAPE、FACE_UP 或 FACE_DOWN
device_orientation STRING PORTRAIT、LANDSCAPE、FACE_UP 或 FACE_DOWN
process_state STRING BACKGROUND 或 FOREGROUND
logs REPEATED RECORD 由 Crashlytics 日志记录器生成的带时间戳的日志消息(如果已启用)
logs.timestamp TIMESTAMP 日志创建时间
logs.message STRING 记录的消息
breadcrumbs REPEATED RECORD 带有时间戳的 Google Analytics 路径(如果已启用)
breadcrumbs.timestamp TIMESTAMP 与路径关联的时间戳
breadcrumbs.name STRING 与路径关联的名称
breadcrumbs.params REPEATED RECORD 与路径关联的参数
breadcrumbs.params.key STRING 与路径关联的参数键
breadcrumbs.params.value STRING 与路径关联的参数值
blame_frame RECORD 被确定为崩溃或错误根本原因的帧
blame_frame.line INT64 帧文件的行号
blame_frame.file STRING 帧文件的名称
blame_frame.symbol STRING 水化合 (hydration) 符号,或原始符号(如果无法水化合)
blame_frame.offset INT64 包含代码的二进制图片中的字节偏移量,Java 异常未设置
blame_frame.address INT64 包含代码的二进制图片中的地址,Java 帧未设置
blame_frame.library STRING 包含帧的库的显示名
blame_frame.owner STRING DEVELOPER、VENDOR、RUNTIME、PLATFORM 或 SYSTEM
blame_frame.blamed BOOLEAN Crashlytics 的分析是否已确定此帧是导致崩溃或错误的原因
exceptions REPEATED RECORD 仅适用于 Android:此事件期间发生的异常。嵌套的异常以时间倒序呈现(读取:最后一条记录是抛出的第一个异常)
exceptions.type STRING 异常类型,例如 java.lang.IllegalStateException
exceptions.exception_message STRING 与异常关联的消息
exceptions.nested BOOLEAN 对于除最后抛出的异常(即第一条记录)之外的所有异常都为 true
exceptions.title STRING 线程的标题
exceptions.subtitle STRING 线程的副标题
exceptions.blamed BOOLEAN 如果 Crashlytics 确定此异常是导致错误或崩溃的原因,则为 true
exceptions.frames REPEATED RECORD 与异常关联的帧
exceptions.frames.line INT64 帧文件的行号
exceptions.frames.file STRING 帧文件的名称
exceptions.frames.symbol STRING 水化合符号,或原始符号(如果无法水化合)
exceptions.frames.offset INT64 包含代码的二进制图片中的字节偏移量,Java 异常未设置
exceptions.frames.address INT64 包含代码的二进制图片中的地址,Java 帧未设置
exceptions.frames.library STRING 包含帧的库的显示名
exceptions.frames.owner STRING DEVELOPER、VENDOR、RUNTIME、PLATFORM 或 SYSTEM
exceptions.frames.blamed BOOLEAN Crashlytics 的分析是否已确定此帧是导致崩溃或错误的原因
error REPEATED RECORD 仅限 Apple 应用:非严重错误
error.queue_name STRING 线程正在运行的队列
error.code INT64 与应用自定义记录的 NSError 关联的错误代码
error.title STRING 线程的标题
error.subtitle STRING 线程的副标题
error.blamed BOOLEAN Crashlytics 的分析是否已确定此帧是导致错误的原因
error.frames REPEATED RECORD 堆栈轨迹的帧
error.frames.line INT64 帧文件的行号
error.frames.file STRING 帧文件的名称
error.frames.symbol STRING 水化合符号,或原始符号(如果无法水化合)
error.frames.offset INT64 包含代码的二进制图片中的字节偏移量
error.frames.address INT64 包含代码的二进制图片中的地址
error.frames.library STRING 包含帧的库的显示名
error.frames.owner STRING DEVELOPER、VENDOR、RUNTIME、PLATFORM 或 SYSTEM
error.frames.blamed BOOLEAN Crashlytics 的分析是否已确定此帧是导致错误的原因
threads REPEATED RECORD 事件发生时显示的线程
threads.crashed BOOLEAN 线程是否崩溃
threads.thread_name STRING 线程的名称
threads.queue_name STRING 仅限 Apple 应用:运行线程的队列
threads.signal_name STRING 导致应用崩溃的信号的名称,仅出现在崩溃的原生线程上
threads.signal_code STRING 导致应用崩溃的信号的代码;仅出现在崩溃的原生线程上
threads.crash_address INT64 导致应用崩溃的信号的地址;仅出现在崩溃的原生线程上
threads.code INT64 仅限 Apple 应用:应用的自定义记录 NSError 的错误代码
threads.title STRING 线程的标题
threads.subtitle STRING 线程的副标题
threads.blamed BOOLEAN Crashlytics 的分析是否已确定此帧是导致崩溃或错误的原因
threads.frames REPEATED RECORD 线程帧
threads.frames.line INT64 帧文件的行号
threads.frames.file STRING 帧文件的名称
threads.frames.symbol STRING 水化合符号,或原始符号(如果无法水化合)
threads.frames.offset INT64 包含代码的二进制图片中的字节偏移量
threads.frames.address INT64 包含代码的二进制图片中的地址
threads.frames.library STRING 包含帧的库的显示名
threads.frames.owner STRING DEVELOPER、VENDOR、RUNTIME、PLATFORM 或 SYSTEM
threads.frames.blamed BOOLEAN Crashlytics 的分析是否已确定此帧是导致错误的原因
unity_metadata.unity_version STRING 此设备上运行的 Unity 版本
unity_metadata.debug_build BOOLEAN 指示该 build 是否为调试 build
unity_metadata.processor_type STRING 处理器类型
unity_metadata.processor_count INT64 处理器(内核)数量
unity_metadata.processor_frequency_mhz INT64 处理器的频率(以 MHz 为单位)
unity_metadata.system_memory_size_mb INT64 系统内存的大小(以 Mb 为单位)
unity_metadata.graphics_memory_size_mb INT64 图形内存(以 MB 为单位)
unity_metadata.graphics_device_id INT64 图形设备的标识符
unity_metadata.graphics_device_vendor_id INT64 图形处理器供应商的标识符
unity_metadata.graphics_device_name STRING 图形设备的名称
unity_metadata.graphics_device_vendor STRING 图形设备的供应商
unity_metadata.graphics_device_version STRING 图形设备的版本
unity_metadata.graphics_device_type STRING 图形设备的类型
unity_metadata.graphics_shader_level INT64 图形着色器级别
unity_metadata.graphics_render_target_count INT64 图形渲染目标的数量
unity_metadata.graphics_copy_texture_support STRING 支持以 Unity API 中定义的方式复制图形纹理
unity_metadata.graphics_max_texture_size INT64 纹理渲染专用大小上限
unity_metadata.screen_size_px STRING 屏幕大小(以像素为单位),格式为“宽 x 高”
unity_metadata.screen_resolution_dpi STRING 屏幕的 DPI(以浮点数表示)
unity_metadata.screen_refresh_rate_hz INT64 屏幕的刷新率(以 Hz 为单位)

使用数据洞察直观呈现导出的 Crashlytics 数据

Google 数据洞察可以将您在 BigQuery 中的 Crashlytics 数据集转换成方便阅读和共享、可完全自定义的报告。

如需详细了解如何使用数据洞察,请阅读数据洞察快速入门指南:欢迎使用数据洞察

使用 Crashlytics 报告模板

数据洞察提供了一个 Crashlytics 的示例报告,其中包含一组来自已导出的 Crashlytics BigQuery 架构的全面维度和指标。如果您已启用 Crashlytics BigQuery 流式导出,则可以在数据洞察模板的实时趋势页面上查看这些数据。您可以将该示例作为模板,根据自己应用的原始崩溃数据快速创建新报告并直观呈现数据:

  1. 打开 Crashlytics 数据洞察信息中心模板
  2. 点击右上角的使用模板
  3. 新数据源下拉列表中,选择创建新数据源
  4. 点击 BigQuery 卡片上的选择
  5. 选择一个包含导出的 Crashlytics 数据的表,方法是依次选择我的项目 > [your-project-name] > firebase_crashlytics > [your-table-name]。 您的批处理表格始终可供选择;如果已启用 Crashlytics BigQuery 流式导出,那么还可以改为选择实时表。
  6. 配置下,将 Crashlytics 模板级设置为默认
  7. 点击连接,创建新的数据源。
  8. 点击添加到报告,以返回 Crashlytics 模板。
  9. 最后,点击创建报告,创建 Crashlytics 数据洞察信息中心模板的副本。