原文地址:blog.sentry.io/2019/09/26/…
原文作者:
发布时间:2019年6月26日
本文由 简悦SimpRead 转码,原文地址 blog.sentry.io
下面我们来看看如何使用Sentry来检测本地应用程序的崩溃,并在短时间内修复它们.......
无论是电脑游戏、物联网设备,还是高性能后端,你都有可能使用原生语言来开发这个应用。迄今为止,本地应用开发最受欢迎的选择是C和C++。好吧,也许不是最流行的,但绝对是最普遍的。有些人甚至可以说是 "不可避免的"。
不幸的是,同样不可避免的是偶尔出现的应用程序崩溃;"分段故障 "和 "总线错误 "对你来说可能并不陌生。下面我们来看看如何使用Sentry来检测这些崩溃,并在短时间内修复它们。
选择正确的客户端
首先,挑选一个能够捕捉崩溃报告并将其发送给Sentry的客户端。长期以来,Sentry一直支持流行的Google Crashpad和Google Breakpad 库。如果你的应用程序已经使用了这些库中的一个,或者你熟悉如何设置它们,你就可以了。只需将上传URL设置为我们的minidumpendpoint,就可以看到崩溃的情况了。
有了我们全新的本地SDK,你有了更好的选择。它有三个版本,可以完美地适应你的需求。
- Standalone: 捕获自定义信息和错误,并添加面包屑。虽然这是最基本的分布,但你可以定制你想处理的信号,并提供你自己的堆栈行走库。我们的独立SDK提供了最大的灵活性,但也需要你自己处理崩溃。
- 使用Crashpad: 基于独立的SDK,这个版本运送并包装了Google Crashpad来捕获崩溃。你仍然可以添加面包屑和捕捉自定义信息,但你还可以免费获得崩溃的应用程序的迷你转储。这在Windows和macOS上效果最好。
- 使用Breakpad: 作为Crashpad的替代品,这个版本运送和包装Google Breakpad来捕获崩溃。否则,它是相同的。我们建议在Linux上使用这个版本。
如果你熟悉我们其他的SDK,你会立即认出初始化SDK所需的代码。在下面的例子中,我们要为一个Windows应用程序初始化Crashpad版本,同时还要传递处理程序的可执行路径。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sentry.h"
int main(void) {
sentry_options_t *options = sentry_options_new();
// The handler is a Crashpad-specific background process
sentry_options_set_handler_path(options, "bin/Release/crashpad_handler.exe");
// This is where Minidumps and attachments live before upload
sentry_options_set_database_path(options, "sentry-db");
sentry_options_add_attachment(options, "application.log", "../ce32ff7a.log");
// Initialize the SDK and start the Crashpad handler
sentry_init(options);
// Capture a custom message
sentry_value_t event = sentry_value_new_event();
sentry_value_set_by_key(event, "message",
sentry_value_new_string("Hello World!"));
sentry_capture_event(event);
// Make sure everything flushes before exiting
sentry_shutdown();
}
在我们的发布页面,你可以下载捆绑版的SDK的源文件和构建文件。对于Windows,我们为你提供了一个Microsoft Visual Studio 2017解决方案。你可以直接从该解决方案开始,添加你自己的文件,或者将项目拖到现有的解决方案中。
对于macOS和Linux,我们分发构建库、实用程序和示例程序的Makefiles。你可以将它们整合到你现有的构建过程中,或者单独使用它们来构建二进制文件并将它们复制过来。
如果我们的发布包不包含你需要的项目文件,premake
文件允许你生成一个自定义项目或对构建设置进行修改。official readme包含更多关于集成和定制SDK的信息。
添加用于调试的上下文信息
现在你已经添加了SDK,你可以使用它的API来添加有助于你调试问题的信息。SDK的_value API_允许你从几个原语中创建任意复杂的值。
// create an object and add a string value
sentry_value_t object = sentry_value_new_object();
sentry_value_set_by_key(object, "key", sentry_value_new_string("value"));
// add that object into the list
sentry_value_t list = sentry_value_new_list();
sentry_value_append(list, object);
// ...
在内部,SDK负责处理字符串、对象和列表的内存管理问题。只要你总是分配这些函数的返回值,你就不需要担心内存泄漏。而且所有这些函数都是线程安全的。
让我们来看看最常见的属性添加。
sentry_value_t user = sentry_value_new_object();
sentry_value_set_by_key(user, "id", sentry_value_new_int32(42));
sentry_value_set_by_key(user, "username",
sentry_value_new_string("some_name"));
sentry_set_user(user);
这将把一个用户(具有给定的标识符和名字)添加到所有未来的事件或minidumps中。用户信息被存储起来,直到被另一个对sentry_set_user
的调用所覆盖,并立即应用于所有未来的事件。
sentry_value_t default_crumb = sentry_value_new_breadcrumb(0, "hello, world");
sentry_add_breadcrumb(default_crumb);
这样就增加了一个面包屑,并包含在以后的每个事件中。SDK保留了一个_100_最近的面包屑的窗口。
sentry_set_transaction("startup");
sentry_set_level(SENTRY_LEVEL_WARNING);
sentry_set_tag("mode", "demo");
这将设置标签和其他基本的事件属性。和用户信息一样,这些属性在被覆盖之前都会被应用。当然,你并不局限于这些属性。参见event payload docs,以获得所有支持的属性和创建自定义事件的接口的更完整列表。
向Sentry提供调试信息
现在,你的应用程序已经准备好向Sentry发送数据了,是时候进行构建了。为了处理崩溃报告,Sentry需要调试信息,它允许我们提取堆栈痕迹,并将堆栈帧符号化为函数名和行号。
向Sentry提供调试信息的最简单方法是使用sentry-cli
上传。这个小助手会扫描兼容的文件并将其上传到你的项目中。你可以在你的构建或发布过程中运行它。
$ sentry-cli upload-dif /path/to/files
> Found 2 debug information files
> Prepared debug information files for upload
> Uploaded 2 missing debug information files
> File processing complete:
OK 3003763b-afcb-4a97-aae3-28de8f188d7c-1
(crash.exe; x86_64 executable)
OK 3003763b-afcb-4a97-aae3-28de8f188d7c-1
(crash.pdb; x86_64 debug companion)
有两个要点需要注意。
- 始终上传调试信息和你部署或分发给用户的相同构建的可执行文件。Sentry使用独特的标识符,这些标识符随每次构建而变化,以便与崩溃报告相匹配。
- 检查所有可用的调试信息是否已经上传。Sentry 会分析上传的文件,并在 Debug Files 设置屏幕上显示其中包含的信息。为了达到最佳效果,请检查你上传的文件中是否有_"debug"和"unwind"_。
如果你不确定哪些文件包含可用的信息,你也可以使用sentry-cli
来检查它们的内容。这在Linux上特别有用,因为在Linux上生成和剥离调试信息要比其他平台略微复杂一些。
$ sentry-cli difutil check path/to/file
Debug Info File Check
Type: elf debug companion
Contained debug identifiers:
> 924e148f-3bb7-06a0-74c1-36f42f08b40e (x86_64)
Contained debug information:
> symtab, debug
Usable: yes
配置符号服务器
如果你不能把调试文件上传到我们这里,或者已经把它们储存在不同的地方,我们已经帮你解决了。我们很高兴地宣布,符号服务器现在已经不再是预览版。你可以在_调试文件_设置屏幕上方便地配置它们。
开箱后,Sentry为你提供了一个内置符号服务器的列表,其中包含了系统库或流行的第三方框架的调试文件。默认情况下,_iOS_和_Microsoft_是激活的,这给你提供了这两个平台的函数名称。其他例子包括_Nvidia_和_AMD_的图形驱动,_Unity_项目,以及Github的_Electron_框架。
一旦你从列表中选择了存储库,Sentry会立即将这些服务器的调试文件应用到所有新收到的崩溃报告和事件中。同样,你也可以在任何时候从列表中删除服务器,Sentry将不再使用它们的调试文件。
如果你的组织是在_Business_或_Enterprise_计划中,你甚至可以在这个列表中添加自定义服务器。如果你正在运行自己的符号服务器,这可能是你最感兴趣的。我们目前支持讲符号服务器协议的HTTP服务器、亚马逊S3桶和谷歌云存储桶。通过这种方式,我们与微软调试器使用的服务器完全兼容,并支持与LLDB和GDB使用的相同的查询路径。
Sentry兼容所有主要的调试文件库的布局,并支持各种压缩格式。更多信息请参见我们的所有支持的功能的文档。当然,如果你遇到困难,我们也会提供帮助,所以如果你有关于自定义符号服务器的问题,请联系我们的了不起的支持工程师。
用自定义分组减少噪音
现在,崩溃报告正在不断涌来,是时候把注意力集中在令人兴奋的部分了:压制这些错误。不要误会我们的意思,我们并不是说bug是令人兴奋的。但是,你必须同意,当最终解决一个问题时,会有一种愉快的、几乎是治疗性的感觉。
Sentry通过将事件归类为独特的问题来帮助你,这就提供了一个关于你的应用程序状态的概览。
- 哪种崩溃发生得最多?
- 在最新的版本中,尽管我进行了修复,但崩溃仍然发生?
- 哪些问题影响了我最重要的用户?
问题分组是由Sentry在后台自动完成的。为了对问题进行分组,Sentry会查看错误和堆栈跟踪的相关部分,以找到具有类似事件的问题。虽然我们采用合理的默认值来进行这种匹配,但有时也会出错。出于这个原因,你可以用自定义规则来微调分组。
首先,确保你使用的是最新版本的分组算法。在_项目设置>分组配置_,你可以升级到最新的算法和基本配置。我们在不断改进分组逻辑,但你可以选择应用变化的时间,以防止新问题对工作流程的突然干扰。
有时,Sentry可能仍然会考虑对你不重要的堆栈框架进行分组。像 "std "或 "boost "这样的常见命名空间在默认情况下会被隐藏和忽略,但来自其他库的框架仍可能被考虑。你可以通过添加自定义分组增强功能来进行微调。这些允许你覆盖哪些框架被认为是_in-app_,哪些被包括在_grouping_。默认情况下,只有应用程序内的框架被考虑分组。
那么,这究竟是如何工作的呢?让我们假设你的应用程序总是通过一个函数run_loop
来调用,并最终调用一个函数call_external
来分配给第三方代码。为了排除run_loop
下面和call_external
上面的所有框架,添加以下分组增强。
platform:native function:call_external ^-group ^-app
platform:native function:run_loop v-group v-app
分组规则可以在框架上匹配,然后执行一些动作。^
指定将该动作应用于上述所有框架,-group
和-app
是分别将框架排除在分组和应用程序框架显示之外的动作。
让我们看一个不同的例子。如果我们不想看到任何来自google::breakpad
命名空间的框架怎么办?很简单。
platform:native function:google::breakpad::* -app
你可以把堆栈痕迹减少到调试问题所需的基本部分。当然,还有更多的匹配器和动作需要配置,所以请到我们的分组文档中了解完整的内容。
还有一件事:Source context
作为顶部的一个小樱桃,让我们添加源代码上下文。有什么比在你的堆栈跟踪中直接看到违规代码更好的呢,对吗?你所要做的就是把源代码上传到Sentry。
不用担心,在这里你并不孤单。Sentry CLI可以方便地处理你的调试文件,搜索相关的源代码,并创建一个包含所有必要元数据的捆绑包。如果你直接从构建系统上传调试文件,就像在上传命令中传递--include-sources
一样简单。
$ sentry-cli upload-dif --include-sources path
> ...
OK 3003763b-afcb-4a97-aae3-28de8f188d7c-1
(crash.exe; x86_64可执行文件)
OK 3003763b-afcb-4a97-aae3-28de8f188d7c-1
(crash.pdb; x86_64调试伴侣)
OK 3003763b-afcb-4a97-aae3-28de8f188d7c-1
(crash.pdb; x86_64源代码包)
之后,你会注意到源码包和你的调试文件一起出现在设置中,并且共享相同的标识符。这就是Sentry如何将你的源代码的正确版本与进来的事件和崩溃相匹配。无论你的构建过程有多复杂--使用子模块、符号链接或生成的文件--源码上下文总是存在的。
重要的是,要在发生构建的同一台机器上运行这个命令。Sentry CLI 使用存储在调试文件中的绝对路径来确保正确的源代码被捆绑。如果你的构建过程没有立即运行上传,你可以选择在构建时生成一个捆绑文件而不上传。
$ sentry-cli difutil bundle-sources /path/to/debug_file
这将在调试文件旁边创建一个源代码捆绑ZIP,可以用upload-dif
上传。这样,你就可以在以后的时间里把源代码和你的调试文件一起上传。
下一步是什么?
从这里开始,整个Sentry平台就为你服务了。链接你的版本控制系统来接收可疑的提交,与项目管理工具整合如JIRA来扩展工作流程,或者使用Discover来对你的事件进行高级分析。
我们很高兴为本地应用程序提供一流的错误监控。由于崩溃会给用户和开发者带来最令人沮丧的体验,因此尽可能多地捕捉背景信息以迅速解决这些错误是至关重要的。
这篇文章中介绍的许多功能已经酝酿了很久。我们很高兴听到你正在构建的独特的应用程序,我们的团队已经迫不及待地想从你的工程师那里得到反馈。
一如既往,我们非常欢迎你在我们的论坛发帖,或与我们的支持工程师联系,提出问题。
来自Sentry,与⌨️和❤️