这篇文章是我在公司做 Sentry 相关分享的演讲稿。
大家好,现在由我来讲解 Sentry 的 Issues (问题)模块。我会分为三个部分来讲,首先我会介绍 Sentry 一些重要的概念,然后讲一下 Issues 的基本使用方式,最后是 Issues 的基本实践。
基本介绍
在开始讲解前,我们先来眼熟一下 Issues 模块的界面。
当我们在菜单上选中 Issues(问题) 以后,就可以看到 Issues 界面。整个界面可以分为三个部分,分别是:
- 不同状态 Issues 的 Tab切换
- 筛选栏,用来筛选 Issues
- Issues 列表
Issue & Event
在 Sentry 的产品逻辑里,一条日志叫 Event,内容相似的 Event 会被聚合成一个 Issue ,也就是我们在界面上看到的Issues 列表。另外 Sentry 提出了“指纹”的概念,Sentry 会根据日志的堆栈信息、异常类型和日志信息等去计算两条日志是否拥有同样的指纹,拥有同样指纹的 Event 会被聚合成一个 Issue 。
如果你想自行对 Event 进行合并,那么可以尝试以下方法:
- 在 Issues 界面对 Issue 进行手动合并
- 自定义指纹规则
[Project] > Settings > Issue Grouping > Fingerprint Rules
例如截图所示,所有错误类型为 ConnectTimeout 的 Event 都会得到一个叫 connect-timeout-type 的指纹,并且这些 Event 会被聚合到一个 Issue 下。
- 日志上报时给日志加指纹
Sentry.withScope(function (scope) {
scope.setFingerprint(['aaatest'])
})
Sentry.init({
// ...
beforeSend(event, hint) {
const error = hint.originalException;
if (
error &&
error.message &&
error.message.match(/database unavailable/i)
) {
event.fingerprint = ["database-unavailable"];
}
return event;
},
});
Sentry 对日志做 Issue 和 Event 维度的划分,我觉得好处在于:一般我们线上的 bug 会同时影响到很多用户,那么这些用户报上来日志量很大并且内容相似,Sentry 帮我们对相似日志做了一个聚合,这样我们能更快的筛选到我们想看的日志。
SDK的接入与使用
这里以 Web 端的 Vue 项目为例子,我们需要安装的依赖包有:
"dependencies": {
"@sentry/integrations": "^7.47.0", // 一些集成功能
"@sentry/vue": "^7.47.0", // 获取sentry实例
}
然后我们要在项目里定义一份 Sentry 的配置文件:
import Vue from 'vue'
import * as Sentry from '@sentry/vue'
import { Plugin } from '@nuxt/types'
import { HttpClient as HttpClientIntegration } from '@sentry/integrations'
const plugin: Plugin = (ctx) => {
const { app, store } = ctx
Sentry.init({
// 项目设置里取
dsn: '', // 如果传空或不传则不会上报任何 Sentry 错误
Vue,
environment: process.env.APP_ENV,
// 上报日志前的钩子
beforeSend(event: any) {
return event
},
// 用户行为放入面包屑前的钩子
beforeBreadcrumb(breadcrumb, hint) {
return breadcrumb
},
integrations: [], // 一些集成功能
})
}
export default plugin
问题分类
- 所有未解决的 ( is:unresolved):所有未解决的问题
- 新的(is:new):7天之内被创建的问题
- 进行中的(is:ongoing):7天之前被创建的问题或者手动标记为已经看过的的问题
- 待审查 ( is:unresolved is:for_review):还没有查看过的问题,待审查问题是所有未解决问题的子集。
- 回归 ( is:regressed):解决了再次出现的问题。
- 不断升级 ( is:escalating):当事件数量明显高于前一周时,sentry将问题定义为不断升级。
- 已存档 ( is:archived):所有已存档的问题。
问题筛选
Issues 查询有4个筛选项,分别是项目、环境、日期和自定义。自定义通过 key:value 的的形式进行搜索。支持的key有:
自定义搜索如果存在多对 key:value
,那么只有满足所有 key:value
条件的 Issue 会被搜索出来。
另外,自定义搜索不能使用 OR 或者 AND 语句,但是可以使用 !
语句:
除了上述的 key,Sentry 还内置了一些 tags 用来搜索:
当然我们也可以自定义 tag ,然后用这些 tag 来搜索:
Sentry.init({
beforeSend(event: any) {
event.tags = {
...event.tags,
language: 'en-US'
}
return event
}
})
SDK 在上报日志前会暴露一个叫 beforeSend
的钩子,我们可以在这个钩子里添加自定义的 tag 。
问题详情
我们在 Issues 界面点开一条 Issue 就可以看到当前 Issue 最新上报的那条日志。
纵观整个 Issue 详情页,可以分为以下几个部分:
- 日志摘要
- 代码报错堆栈
- 面包屑
- 回放(Replays)
面包屑
启用面包屑的方式:
Sentry.init({
integrations: [
// 启用面包屑
new Sentry.Integrations.Breadcrumbs({
console: true
})
],
})
面包屑会自动记录的行为有:
- console
- dom 操作(比如点击元素)
- Fetch 请求
- 浏览器路由跳转
- Sentry 自身的行为(比如上报日志)
- XHR 请求
可以看到,网络请求相关的用户行为,Sentry 只会主动记录 request_body_size
和 response_body_size
两个字段。但是一般我们更希望可以看到请求的响应头和响应体,那么这时候我们可以自行修改面包屑记录的数据:
Sentry.init({
// ...
beforeBreadcrumb(breadcrumb, hint) {
if (breadcrumb.category === 'xhr' && breadcrumb.data) {
breadcrumb.data.response = hint?.xhr.response
breadcrumb.data.responseHeaders = hint?.xhr.getAllResponseHeaders()
}
return breadcrumb;
},
})
Sentry SDK会暴露一个叫 beforeBreadcrumb
的钩子,我们可以在这个钩子里对 Sentry 即将记录的用户行为做一些更改。beforeBreadcrumb
包含两个参数,第一个参数 breadcrumb
是 Sentry 已经整理好的一条面包屑数据,第二个参数 hint
则是 Sentry 额外采集到的一些数据。比如对于 XHR 请求,我们可以在 hint
参数里拿到 xhr 对象,这样我们就可以获取到请求的响应头和响应体。
回放(Replays)
回放是 Web 端独有的功能。回放并不是视频录制,而是把面包屑里的用户行为串起来,是对用户行为类似于视频的再现。
回放界面如下图所示,整个界面左半部分为回放,右半部分为用户行为列表:
可以看到 Sentry 自动对回放内容做了敏感信息处理,我们并不能通过回放看到用户在页面上填写的内容。
回放开始录制的方式:
// 第一种:在配置文件设置采样比例,Sentry 自动录制
Sentry.init({
integrations: [
new Sentry.Replay()
],
// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
// 采样率
replaysSessionSampleRate: 0.1,
// If the entire session is not sampled, use the below sample rate to sample
// sessions when an error occurs.
// 错误采样率
replaysOnErrorSampleRate: 1.0,
})
// 第二种:手动开始录制
replay.stary()
回放结束录制的方式:
- 用户超过15分钟在页面上没有操作,则自动结束录制。
- 录制时间超过60分钟,则自动结束录制。
- 手动调用
replay.stop()
最佳实践
前面我们已经介绍了 Issues 模块的基本概念和使用方式,现在我们来看看在实际开发中,我们如何通过 Issues 模块去定位问题和解决问题。
不管是使用哪个监控系统,我们对项目异常的处理流程都是:找到异常对应的日志 --> 根据日志定位问题 --> 分配问题修复人 --> 解决问题。
比如现在我们有一个bug,在首页点击搜索的时候没有跳转到搜索结果页,那么我们需要用 Sentry 做以下事情:
- 根据问题发生的大概时间、报错信息以及自定义 tag 等筛选出对应的 Issue:
- 根据代码报错堆栈定位报错的代码文件
根据报错堆栈我们可以知道是 jrpass-search.vue
文件的 search
方法出现了错误。
-
根据面包屑找到出错数据的获取源头
-
指派修复人
- 修复完成后扭转问题状态