原创陈嘉伟 / 叫叫技术团队
前言
在当今快节奏的前端开发世界中,错误和异常是不可避免的挑战。为了确保我们的应用程序始终保持稳定和高质量,我们需要一种可靠的监控方案。而 Sentry 监控正是为前端团队提供了这样的守护者角色。 本文将带你深入了解 Sentry 监控的强大功能,我们将探讨如何集成和配置 Sentry 监控,以及如何利用其捕获、追踪和处理前端错误和异常的能力。 通过使用 Sentry 监控,你将能够快速定位和解决前端问题,提高应用程序的稳定性和用户体验。无论你是开发者还是团队负责人,本文都将为你提供实用的知识和建议,帮助你优化前端开发流程并提高团队的效率。 让我们开始探索 Sentry 监控的世界,成为前端团队的守护者吧。
Sentry 监控简介
Sentry 是一个 C/S 架构,我们需要在自己应用中集成 Sentry 的 SDK 才能在应用发生错误是将错误信息发送给 Sentry 服务端。根据语言和框架的不同,我们可以选择自动或自定义设置特殊的错误类型报告给 Sentry 服务端。
Sentry 的服务端分为 web、replay、snuba、worker 这几个部分,应用(客户端)发生错误后将错误信息上报给web,web 处理后放入消息队列或 Redis 内存队列,worker 从队列中消费数据进行处理。
Sentry 支持自动收集和手动收集两种错误收集方法;我们能成功监控到 Vue 中的错误、异常,但是还不能捕捉到异步操作、接口请求中的错误,比如接口返回404、500等信息,此时我们可以通过 Sentry.caputureException()进行主动上报。
安装 Sentry 监控
自托管部署 Sentry 服务
Docker 部署
- 在本地安装 Docker 环境参考: docs.docker.com/get-docker/
- 下载 Sentry 最新的安装包:github.com/getsentry/s…
- 在安装包目录下执行命令
./install.sh该脚本会处理所有需要安装的配置。 - 安装完成后使用 Docker 命令启动
docker-compose up -dSentry 默认启动端口9000本地可以访问http://127.0.0.1:9000。
添加Sentry监控
创建项目
创建完成后,能够看到相应的 SDK 接入提示:
SDK 安装
Sentry 的 SDK 支持多种不同的框架和语言,这里选择其中一种详细讲解,如需了解其他的可以查看官方文档。
这里选用 Vue2.0 项目作为深入的了解。
初始化 SDK
安装 Vue 框架的 Sentry SDK:
npm install --save @sentry/vue
import Vue from "vue";
import Router from "vue-router";
import * as Sentry from "@sentry/vue";
Vue.use(Router);
const router = new Router({
// ...
});
Sentry.init({
Vue,
dsn: "http://619a0e82d62a4a249b1c76f5e4a91c59@127.0.0.1:9000/4",
integrations: [
new Sentry.BrowserTracing({
// Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/],
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
}),
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
});
// ...
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
SDK 常用参数
讲解部分配置,其他不在这里赘述,详细参考:官方文档
- dsn:项目地址,用于收集错误信息的 Sentry 分配的地址;
- debug:是否开启 debug 模式,开启 debug,就会把信息打印到控制台上面;
- release:代码的版本号,可以确定当前的错误/异常属于哪一个发布的版本,可以应用到 sourcemaps 来映射源码;
- environment:环境名称;
- sampleRate:是否开启随机发送事件给 Sentry ,1为100%,0.1为10%几率发送事件;
- attachStacktrace:是否开启堆栈跟踪,开启后跟着消息一起收集;
- beforeSend:发送前操作;
SDK API
- captureException(exception):捕获一个 js 异常,传入一个 exception 对象或者类对象;
- captureMessage(message,level):捕获一条信息,传入信息内容和信息级别;
- captureEvent(sentryEvent):捕获一个事件,sentryEvent 是手动创建的,自定义的;
- addBreadcrumb(Breadcrumb):添加一个面包屑,以供接下里的捕获;
- configureScope((scope)=>{}):全局配置:设置 context 信息到 scope 上面;
- withScope((scope)=>{}):设置一个临时的 scope 信息到 context 上面,只适用于当前范围内捕获的事件;
Sentry.withScope(function (scope) {
scope.addEventProcessor(function (event, hint) {
// Add anything to the event here
// returning `null` will drop the event
return event;
});
// The event processor will apply to this event
Sentry.captureMessage("Test");
});
context 上下文信息
讲解部分配置,详细参考:官方文档
上下文信息包括:user、tags、level、fingerprint、extra data。 这些信息我们可以通过在 scope 上面设置来定义。 其中可以通过两种方法得到 scope:
// 将 scope 配置到 context 上面
Sentry.configureScope((scope) => { });
// 创建一个临时到 scope ,配置到 context 上面
Sentry.withScope((scope) => { });
User
讲解部分配置,详细参考:官方文档
scope.setUser({
id: "1" ,
username: "xiao" ,
ip_address: "{{auto}}" ,
email: "test.doe@example.com" ,
});
通过 setUser 来设置 User 信息。 其中 User 可以设置的信息包括 id、username、ip_address、email。 :::info 如果用户的 ip_address 设置为 "{{auto}}" ,Sentry 将从您的应用程序和 Sentry 服务器之间的连接推断 IP 地址。 :::
Tags
讲解部分配置,详细参考:官方文档
Tags 是给事件自定义不同的键/值对,可以在查找的时候更容易。 后台查找的时候,查找选项会多出来一个选项,就是通过 tags 来设置的。
scope.setTag( "page_local" , "de-at" );
通过 setTag 来设置了一个 page_local 的标签,后台会多一个 page_local 选项。
Event Level
讲解部分配置,详细参考:官方文档
通过这个来设置事件等级。 包括:fatal、error、 warning、 info、 debug
scope.setLevel( "warning" );
通过 setLevel 来设置。
Event Fingerprint
讲解部分配置,详细参考:官方文档
所有事件都有指纹。具有相同指纹的事件被分组到一个问题中。 如果想要更好的对错误进行分组,也可以自定义指纹的分组信息。
Sentry.init({
// ...
beforeSend: function (event, hint) {
const exception = hint.originalException;
if (exception instanceof DatabaseConnectionError) {
event.fingerprint = ['database-connection-error'];
}
return event;
},
});
Extra Data 传入额外的信息,并不会创建索引(也就是不可以提供来检索)。 在最新版 Sentry 中,不推荐使用额外信息。
scope.setExtra( "character_name" , "Mighty Fighter" );
通过 setExtra 来设置。
- 使用 Extra Data 传入的额外信息无法搜索
- 使用 Tags 可以搜索到结果
添加错误堆栈追踪依赖
npx @sentry/wizard@latest -i sourcemaps
webpack.config.js 配置中添加:
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");
module.exports = {
// ... other config options
devtool: "source-map", // Source map generation must be turned on
plugins: [
sentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: "sentry",
project: "demo-vue",
url: "http://127.0.0.1:9000/",
}),
],
};
配置完成后,构建项目即可在 Sentry Web 端项目管理中看到对应项目的 sourceMap 文件。
错误捕获与追踪
Sentry 前端异常监控基本原理
错误事件捕获
1.重写 window.onerror 方法、重写 window.onunhandledrejection 方法
window.onerror = function (message, source, lineno, colno, error) {
console.log('message, source, lineno, colno, error', message, source, lineno, colno, error);
}
参数:
- message:错误信息(字符串)。可用于 HTML onerror="" 处理程序中的 event;
- source:发生错误的脚本 URL(字符串);
- lineno:发生错误的行号(数字);
- colno:发生错误的列号(数字);
- error:Error对象(对象);
当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件;这可能发生在 window 下,但也可能发生在 Worker 中。 这对于调试回退错误处理非常有用。
Sentry 源码可以搜索 global.onerror 定位到具体位置:
GlobalHandlers.prototype._installGlobalOnErrorHandler = function () {
// 代码有删减
// 这里的 this._global 在浏览器中就是 window
this._oldOnErrorHandler = this._global.onerror;
this._global.onerror = function (msg, url, line, column, error) {}
// code ...
}
同样,可以搜索 global.onunhandledrejection 定位到具体位置:
GlobalHandlers.prototype._installGlobalOnUnhandledRejectionHandler = function () {
// 代码有删减
this._oldOnUnhandledRejectionHandler = this._global.onunhandledrejection;
this._global.onunhandledrejection = function (e) {}
}
上报错误
采用
Ajax上传
支持 fetch 使用 fetch,否则使用 XMLHttpRequest。
BrowserBackend.prototype._setupTransport = function () {
// 代码有删减
if (supportsFetch()) {
return new FetchTransport(transportOptions);
}
return new XHRTransport(transportOptions);
};
fetch
FetchTransport.prototype.sendEvent = function (event) {
var defaultOptions = {
body: JSON.stringify(event),
method: 'POST',
referrerPolicy: (supportsReferrerPolicy() ? 'origin' : ''),
};
return this._buffer.add(global$2.fetch(this.url, defaultOptions).then(function (response) { return ({
status: exports.Status.fromHttpCode(response.status),
}); }));
};
XMLHttpRequest
XHRTransport.prototype.sendEvent = function (event) {
var _this = this;
return this._buffer.add(new SyncPromise(function (resolve, reject) {
// 熟悉的 XMLHttpRequest
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState !== 4) {
return;
}
if (request.status === 200) {
resolve({
status: exports.Status.fromHttpCode(request.status),
});
}
reject(request);
};
request.open('POST', _this.url);
request.send(JSON.stringify(event));
}));
}
使用 Sentry 面板进行错误追踪和分析
了解了 SDK 的错误捕获机制以及错误上报的原理,我们接下来看看如何使用 Sentry 进行错误的追踪和分析。
基本术语
- Event:应用端每次触发异常,就是一个 Event,会上报到 Sentry 中;
- Issue:Sentry 会对上报的异常按照一定的指纹算法进行 Group,(具体算法可以查看不同 SDK 的 Issue Group 文档,比如 Django Issue Grouping)相同的异常 Event 会被归类到一个 Issue 下;
Issues 面板
解释问题列表默认会展示当前项目所未解决问题列表,可用来搜索查找某个线上问题在 Sentry 是否有过告警的情况。
Tab(选项栏)
- All Unresolved:所有未解决问题;
- For Review:需要 review 的问题,此选项会列出最7天出现的新 issues 和被重新打开的 issues ;
- ignore:已忽略的搜索;
Search(搜索框)
搜索框一般用来根据某些已知条件搜索 Sentry 中是否有上报相关信息的错误,比如已知某个线上问题工单里面的关键信息,在 Sentry 中搜索相关错误。
搜索语法
搜索查询语法是使用一种 key:value 模式构建的。每一对 key:value 都是一个 token。每个 token 可以为:
- 一个普通的字符串,作为普通字符,搜索匹配的结果是与问题标题和正文匹配的结果:
- 一个问题属性或者事件属性,这些都是 Sentry 内置的关键字,常见的内置关键字 tag 有:
- href:问题发生的链接
- os:系统信息 (iOS、Android)
- device:设备信息(iPhone)
- brower:浏览器信息
- 一个自定义标签 Tag(每个项目都可以使用 Sentry.setTag 设置自定义标签)
其他高级搜索
| 描述 | 事例 | 备注 |
|---|---|---|
| 多条件联合搜索 | user.id: 1111 | |
| os.name: "Android 12" | 空格分割条件 | |
| 匹配多个 value | os.name: [iOS, Android] | 需要注意这里的含义为:iOS 或者 Android |
| 逻辑非 ! | !os.name: [Android] | |
| 通配符 * | browser: "Safari 11*" | |
更多搜索可参考 Sentry Search
Issue 详情面板
主要由顶部面板和侧边栏组成的概览信息,以及主区域显示的详情信息组成。
在详情面板中可以看到
172.18.0.1的IP地址,这个是 Sentry 标示当前采集用户的 IP 信息,在我们自己的业务当中,我们也可以自定义这个用户信息,便于我们排查问题的时候查到对应的用户。
// 自定义采集用户信息
Sentry.setUser({
id: 业务内部的用户ID值,
username: "用户名称",
email: "john.doe@example.com",
segment: "用户划分",
ip_address: "如果用户的ip_address设置为“{{auto}}”,Sentry将从你的应用和Sentry服务器之间的连接中推断出IP地址。"
});
SideBar(侧边栏)
侧边栏记录了最近24小时、最近30天事件发生的次数;首次出现事件与最后出现时间;需要重点关注 All tags,它展示了与本次错误相关联的所有标签分布,以及占比。比如最近30天次错误发生了150次,tag:Mobile Safari 13.0.3 占比为100%,则应考虑是否为此 iOS 系统版本兼容性问题。
Tags(标签)
除了 Sentry 默认采集的一些通用标签之外,我们还可以自定义一些标签,能够有助于我们更快的识别问题。
// 自定义标签 标签名称、标签值
Sentry.setTag("page_name", "test-page");
Exception(Stack Trace)(堆栈跟踪)
显示事件出错的代码行(需要先接入 sourceMap)。
Breadcrumbs(面包屑)
面包屑列表分别展示了:类型、类别、描述、等级、时间等信息,时间排序为倒序。点击时间可以切换为相对时间。接入用户回放功能后还会显示用户回放的入口信息。提供导致错误事件的历史记录和时间线,查看问题发生之前的一些其他轨迹。
通过对 Issue 面板的使用,我们能够了解到一个错误发生的前后状态的变化,也能够通过面包屑知道用户的那一些交互行为触发了错误事件,便于我们能够快速的定位和复现问题。
异常告警
Sentry 也提供了异常告警通知,正确的配置告警能够让我们及时发现系统的错误并解决。
规则设置:
- An event is seen: 一个事件发生的时候;
- An issue is first seen: 第一个发生错误的时候;
- An issue changes state from resolved to unresolved:问题从解决到未解决的时候;
- An event’s tags match {key} {match} {value}: 匹配到 tags 的键值对的时候发送;
- An issue is seen more than {value} times in {interval}:在固定时间内出现次数匹配的时候;
- An issue is seen by more than {value} users in {interval}:在固定时间内出现用户的次数匹配的时候;
- An event’s {attribute} value {match} {value}: 匹配到某一个事件的时候;
- An event’s level is {match} {level}: 事件级别匹配的时候;
Sentry 默认是以邮件的形式发送通知给被通知人员,如果觉得邮件没办法及时看到通知消息,也可以使用 Sentry 的 Webhook 集成类似于钉钉这种办公工具,能够通过钉钉发送指定的消息给对应的人员。
总结
Sentry 监控在前端开发中扮演着重要的角色。它提供实时错误监测、错误定位调试和数据分析等功能,帮助团队快速发现和解决应用程序中的问题,提升应用稳定性和用户体验。通过自动化的告警和通知机制,Sentry 监控确保团队能够及时响应关键问题,并促进团队协作和知识共享。总的来说,Sentry 监控是一种强大的工具,为前端团队提供了洞察力和能力,提高应用程序开发质量。
每一次错误排查都加深了我们对 Sentry 功能的理解,通过 Sentry,我们能够捕获并解决隐藏在应用程序中的潜在问题,保障应用的质量和稳定性。
问题不可避免,但在 Sentry 的护航下,我们能够更好地成长。