前端监控进阶篇 ——Sentry 监控 Next.js 项目实践

上一篇相关文章介绍了 Sentry 的基础篇 —— 本地安装,本篇文章以实际项目出发使用 Sentry 进行前端监控实践。实践案例选择 Next.js 项目,具体项目地址next-sentry-easy。
上一篇:前端监控基础篇 —— Docker + Sentry 搭建前端监控系统

开源社区存在很多搭建 Sentry 的文章,但是关于相关细节配置使用的实践文章其实并不多,对于新手来说不是很友好,Next.js 相关的就更少之又少了。 Next.js 国内社区并不算庞大,关于 Next.js 的监控说实话我也没找到太完美的方案,所以自己也尝试做了几个方案,Sentry 就是其中的一个选择。写这篇文章的原因还有如下的原因:

Next.js 官方示例仓库里有两个关于 sentry 的 Demo,这两个 Demo 都存在一些问题。

第一个with-sentry-simple是一个最基础的简单示例,但是此 Demo 存在 bug —— 只在客户端可以正常上报,而服务端并没有成功上报。

第二个with-sentry是一个比较复杂的案例,问题呢就是太复杂了,不适合新手上手学习。

所以,这里就综合一下,弄一个比较适合新手又比较完整的 Demo。
新建 Sentry 项目
新创建一个 Node.js 的项目,新建好之后 Sentry 就会为项目分配一个 DSN 地址。如下图所示:

初始化项目

package.json

“@sentry/browser”: “^5.11.0”,
“@sentry/node”: “^5.11.0”,
复制代码
next.config.js

const withSourceMaps = require(’@zeit/next-source-maps’)

module.exports = withSourceMaps({
env: {
SENTRY_DSN: ‘http://e69fa8afd38e43e59fc3baf48ec0c681@localhost:9000/11’
},
webpack: (config, options) => {
if (!options.isServer) {
config.resolve.alias[’@sentry/node’] = ‘@sentry/browser’
}
return config
},
})

复制代码上面这么配置是因为 Next.js 的特殊性,服务端渲染框架,既存在服务端也存在客户端场景,项目里引入的只能是一种,通用引入@sentry/node,所以当客户端的时候,要替换成@sentry/browser。

_app.js

import * as Sentry from ‘@sentry/node’

Sentry.init({
// Replace with your project’s Sentry DSN
dsn: process.env.SENTRY_DSN,
});

static async getInitialProps ({ Component, ctx }) {
let pageProps = {};
try {
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps({ ctx })
}
return { pageProps }
} catch (err) {
// This will work on both client and server sides.
console.log('The Error happened in: ', typeof window === ‘undefined’ ? ‘Server’ : ‘Client’);
Sentry.captureException(err)
return { pageProps };
}
}
复制代码在 _app.js 里对 Sentry 进行初始化配置,这里简单只配置了 dsn 参数。另外,在 getInitialProps 函数里进行了操作,如果发生错误,使用 Sentry.captureException()进行上报。

注意,此处主要负责页面渲染,路由跳转时的错误拦截,可以拦截到客户端和服务端两种场景的错误。具体为什么可以去查看我之前写的 Next.js 相关文章。

_error.js

import * as Sentry from ‘@sentry/node’;

const MyError = ({ statusCode, err }) => {
if (err) {
// This will work on both client and server sides in production.
Sentry.captureException(err);
}

return
}
复制代码_error.js 我们都知道,是 Next.js 内置的错误组件,程序在运行时出现错误就会渲染该组件,因此,在这里主要负责程序运行时的错误拦截,比如点击按钮、提交表单等交互操作的错误。
效果
上面初始化工作已经完成,来运行项目简单查看一下效果:

运行时出现错误

如下图所示,分别点击客户端和服务端的错误页面。

Sentry 项目 Issue 列表

如下图所示, Sentry 正常捕获到了代码里所抛出的异常。

邮件提醒

如下图所示,错误还可以通过邮件发送给开发者,更为方便及时的提醒。

扩展项目 —— 捕获数据请求
上面完成了初始化项目,可以正常捕获客户端、服务端、初始化以及运行中的异常错误。不过呢,在使用过程中发现 Sentry 并不能捕获网络请求的错误。比如,项目里使用的都是 Fetch,如下图所示,初始化页面的网络请求报错404,但是 Sentry 的控制台并没有捕获到错误。

正常的商业系统,当用户多且业务逻辑复杂的时候,有一定需求需要对网络请求也进行简单的监控。所以,简单来扩展一下项目,使其能捕获数据请求的异常。

封装 FetchError

class FetchError extends Error {
constructor(url = ‘http://localhost’, props) {

super(props);
const { message, traceId, userId } = props;

if (Error.captureStackTrace) {
  Error.captureStackTrace(this, FetchError)
}

this.name = 'FetchError'
// Custom debugging information
this.url = url
this.message = message
traceId && (this.traceId = traceId)
userId && (this.userId = userId)
this.date = new Date()
console.log(this)

}
}

export default FetchError
复制代码
封装 fetch

import fetch from ‘isomorphic-unfetch’;
import * as Sentry from ‘@sentry/node’;
import FetchError from ‘./FetchError’;

function dealStatus(res) {
if (res.status !== 200) {
const err = new FetchError(res.url, {
traceId: Math.random() * 10000, // create your application traceId
userId: 26,
message: ${typeof window !== 'undefined' ? 'Client' : 'Server'} fetch error - ${res.status}
})
// This will work on both client and server sides in production.
Sentry.captureException(err);
}
return res;
}

// initial fetch
const unfetch = Object.create(null);

HTTP_METHOD.forEach(method => {
// is can send data in opt.body
const bodyData = BODY_METHOD.includes(method);
unfetch[method] = (path, { data } = {}) => {
let url = path;
const opts = {
method,
headers: {
‘Content-Type’: ‘application/x-www-form-urlencoded’,
},
mode: ‘cors’,
cache: ‘no-cache’
};

...

return fetch(url, opts)
  .then(dealStatus)
  .then(res => res.json());

};
});

export default unfetch;
复制代码上面,简单的封装了 Fetch 函数,然后在里面处理了错误异常,如果返回码不是 200,表示本次请求出现错误,使用Sentry.captureException(err);上报错误。
具体效果如下图所示:

可以见到,Sentry 成功上报了数据请求的异常信息。

这里其实存在一个问题,如果恰好服务器崩溃了怎么办?岂不是一天开发人员要收到无数封的邮件轰炸?不是很合理嘛,能不能 Fetch Error 不发邮件呢?这一点在下文会提到~

扩展项目 —— 源码定位
源码定位是什么意思呢,我们先来看看现在正常的错误上报信息,如下图所示:

从上图可以看出来,因为代码是经过压缩的,所以错误的上下文,堆栈信息也是压缩过后的,这样的代码排查问题相当困难了,只是有个报错而已。那么众所周知,我们的构建工具一般来说也就是 webpack,可以配置 source-map 来对代码进行映射。在这里,Sentry 也可以,通过配置 release 版本信息就可以更精确的定位错误信息。
Sentry官方给出来的配置方案有三种,release API、sentry-cli和sentry-webpack-plugin,前两个都需要手动上传项目的 source-map 文件,第三个通过代码配置在项目 build 的时候进行上传,所以这里采用第三种,更为方便。其他的社区都有对应文章,可以去看。

第一步:安装依赖

yarn add @zeit/next-source-maps @sentry/webpack-plugin
复制代码
第二步:配置 .sentryclirc

[defaults]
url = http://localhost:9000/
org = luffyzh
project = next-sentry-example

[auth]
token = your token api
复制代码这个配置文件是配置 release 的关键文件,默认参数是上报路径,组织名称以及项目名称,通过这三个信息就可以准确定位到具体的项目了。然后 auth 下面的 token 需要我们去生成,具体如下图:

生成 token 的时候一定要勾选 project:write 权限才可以!!!

第三步:配置 next.config.js

const webpack = require(‘webpack’)
const withSourceMaps = require(’@zeit/next-source-maps’)()
const SentryCliPlugin = require(’@sentry/webpack-plugin’)

module.exports = withSourceMaps({
webpack: (config, { isServer, buildId }) => {
config.plugins.push(
…[
new webpack.DefinePlugin({
‘process.env.SENTRY_RELEASE’: JSON.stringify(buildId)
}),
new SentryCliPlugin({
include: [’.next’], // 上传的文件夹,next项目传.next文件夹就行
ignore: [‘node_modules’, ‘next.config.js’], // 忽略的文件
configFile: ‘.sentryclirc’, // 上传相关的配置文件
release: buildId, // 版本号
urlPrefix: ‘~/_next’ // 最关键的,相对路径
})
]
)

return config
},
})

复制代码
这里有非常重要的一点,就是urlPrefix这个参数,它默认是~也就是域名加上上传的文件路径,但是 Next.js 项目比特殊,文件路径前面都会加上一个/_next,所以需要加上这个配置项才可以正常访问,要不然 Sentry 获取不到对应的 source-map 文件。

第四步:代码配置

// _app.js
Sentry.init({
// Replace with your project’s Sentry DSN
dsn: process.env.SENTRY_DSN,
release: process.env.SENTRY_RELEASE, // 版本号
});

复制代码
这里同样有个注意点,就是Sentry.init({})的版本号必须与next.config.js的配置项里的版本号一致,这样 Sentry 才可以关联上。在 Next.js 项目中非常简单,使用内置的 buildId 即可~

第五步:build + 查看效果

运行yarn build命令会发现控制台在不断的上传 source-map 文件,如下图所示:

build 过后,在 Sentry 项目控制台可以查看到对应版本的 source-map 文件,如下图所示:

然后重新运行程序,会发现错误堆栈信息已经可以查看源码了,具体那一行代码抛出的异常都可以查看~

扩展项目 —— 用户反馈
关于用户反馈,那就是仁者见仁智者见智了,如果你希望线上项目崩溃的时候显得很优雅,并且你有诉求你的用户能够帮你排查问题,复现步骤,那还是可以的。不过呢,不是所有人都希望这样,因为这就代表着你让用户发现了系统的问题,可能有的人不希望用户发现,或者希望的是私下反馈及时解决避免更多的人发现~总而言之,这也算是一个知识点,就简单配置一下玩耍玩耍:
Sentry.init({
// Replace with your project’s Sentry DSN
dsn: process.env.SENTRY_DSN,
beforeSend(event) {
// Check if it is an exception, if so, show the report dialog
if (process.browser && event.exception) {
Sentry.showReportDialog({
eventId: event.event_id
});
}
return event;
}
});
复制代码其实就一个 API,Sentry.showReportDialog(),我们在出现异常的时候调用它,就可以让用户填写对应的内容进行展示。具体过程如下图所示:

第一步:运行程序出现错误

第二步:填写返回信息

第三步:Sentry 控制用户反馈查看对应信息

从上图可以看出,出现了反馈建议,并且在 Sentry 的反馈列表里,对应反馈内容也正常显示~
扩展项目 —— 关联 Github/Gitlab
继续来扩展项目,那就是 Sentry 监控的错误还可以关联 Github/Gitlab,然后直接新建 Issue,对于测试同学来说,简直是福音啊~
第一次初始化配置我们需要在错误详情页与项目进行关联,如下图所示:

然后安装相应的插件,这里以 Github 为例:

这里是安装不上的,因为这些插件需要在安装 Sentry 的同时一起安装才可以,也就是通过 config.yml 文件配置对应插件。具体的就是上篇文章内容了,因为某些原因(疫情没结束,家里没网络,4G 网络确实有点费劲),这里就不进行安装了,对应地址如下:Sentry 安装 Github Integrations。
安装好之后关联 Issue 效果如下:

下面这些截图从网上获取,对应文章 -> 另一篇 Sentry 文章

其实公司项目没有必要这么新建 JIRA,有测试同学以及邮件提醒,所以够用了,不过如果是个人项目,还是挺有用的。感兴趣的可以自行配置一下~不配置也没事,不耽误使用。
扩展项目 —— 错误级别以及邮件设置
在这里承接上文,上面提到了 Fetch Error 是捕获数据请求的异常的,那么如果服务器崩溃了,重启期间,假设有10000个访问,那么开发人员就会受到邮件轰炸,不是很合理,有没有解决办法呢?有~就是邮件提醒设置。
邮件设置不需要任何代码,使用起来也很简单,当然也很重要,具体设置如下图所示:

Project -> Alerts -> Rules -> Edit进行设置。

上面我设置了,只有 error 以上级别的异常才会发邮件,如果是 error 以下的级别(warning/info)就不发邮件了,但是仍然上报。接下来我们就是需要将我们的 Fetch Error 重新设置级别,因为默认上报的就是 error 级别。
// unfetch.js

/* config the fetch error is warning */
Sentry.withScope(function(scope) {
scope.setLevel(‘warning’);
Sentry.captureException(err);
});
复制代码通过上面的代码,我们将 Fetch Error 设置成了 warning 级别,具体如下图:

warning 级别的颜色是橙色,其他正常异常的是红色

错误详情里,本次异常的 level 对应也是 warning

最后重启项目,就会发现,其他异常依然会有邮件警告,而 Fetch Error 已经没有邮件警告了,但是我们依然会在 Sentry 控制台看到它们~很完美!
总结
至此为止,继上篇基础篇之后,实践篇也写完了,基本包含了所有的可用场景。希望对大家有所帮助,前端监控在生产开发过程中还是比较重要的。感兴趣的可以一起交流~
项目地址如下:
https://www.jianshu.com/p/3e0bed48866d
https://www.jianshu.com/p/e8eb05ecfd91
https://www.jianshu.com/p/db575bdc4742
https://www.jianshu.com/p/41ef2f434e0c
https://www.jianshu.com/p/16643541383c

next-sentry-easy,该项目优化了 Next.js_with-sentry-simple 的 bug,精简了 Next.js_with-sentry 的复杂逻辑,个人感觉算是一个比较平均的项目,如果感兴趣记得 Star,谢谢~

发布了60 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/a59612/article/details/104335935