Sentry

Sentry 监控服务接入主要分为以下几个步骤,请务必阅读仔细。

创建项目

登录Sentry控制台,在对应组织下点击创建项目

选择对应语言,我们一般是 Vue

Sentry

然后输入项目名称,注意项目名称是英文,约定:项目git名一致

然后点击创建项目,之后会进入SDK接入提示界面,类似如下界面

SDK

SDK 接入

按照文档描述,使用 npm 安装 SDK,然后引入,注意区分项目版本(Vue2Vue3),然后在项目入口文件下写入以下代码(大部分和文档描述一致,有小部分改动)


import Vue from "vue";
import Router from "vue-router";
import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";

Vue.use(Router);

const router = new Router({
// ...
});

Sentry.init({
Vue,
dsn: '<文档描述中的 dsn>',
environment: process.env.VUE_APP_SENTRY_ENVIRONMENT,
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins: ['localhost', /^\//]
})
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0
})

注意上述代码中的 environment 以及 tracingOrigins

设置环境变量

为了区分环境,我们需要在项目根目录下新建几个 vue 的环境变量文件(注意必须是Vue项目才有效)

  • .env
  • .env.development
  • .env.production
  • .env.preview

上述文件中,.env.preview 是非必选的,其他必选,文件内容如下

NODE_ENV=development
VUE_APP_SENTRY_ENVIRONMENT=development

注意,文件中必须要有 NODE_ENV 以及 VUE_APP_SENTRY_ENVIRONMENT 这两项,并且 NODE_ENV 要在文件第一行,其中 VUE_APP_SENTRY_ENVIRONMENT 代表 SDK 运行环境,一般开发环境为 develop,部署环境为 production,可以继续细化测试,QA等环境,需要部署时注意。

上述操作完成后,SDK 即接入成功,无需额外操作即可正常使用项目。

注意

为了节省服务器资源,建议大家开发环境不要上传日志信息,可以调试测试是否可以上传,测试通过后通过判断,取消开发环境日志上传

if (env.NODE_ENV !== 'development') {
Sentry.init({
dsn: '项目的dsn',
environment: '环境变量',
release: '版本信息',
integrations: [
new VueIntegration({
Vue,
tracing: true
}),
new Integrations.BrowserTracing({
// routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins: ['localhost', /^\//]
})
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0
})
}

查看监控

进入控制台页面,点击项目,选择刚才创建的项目,可以查看概览,可以选择环境查看

project

点击左侧问题即可查看问题

点击问题即可查看详细问题,其他额外操作请自行探索。

版本管理

Sentry 默认支持版本管理,对于版本管理,我们做以下约定,目前仅对 monorepo 项目做版本管理,为了对应版本管理,SDK 代码需要做以下改动


import Vue from "vue";
import Router from "vue-router";
import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";
import Pkg from '../package.json' // 根据具体项目来,项目的 package.json

Vue.use(Router);

const router = new Router({
// ...
});

Sentry.init({
Vue,
dsn: '<文档描述中的 dsn>',
environment: '<环境信息>',
release: `${Pkg.name}@${Pkg.version}`,
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins: ['localhost', /^\//]
})
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0
})

注意上述的 release 项,添加上述代码后,无其他改动,如需修改版本号,则由对应项目负责人使用 lerna publish 命令进行操作,需要在 release 分支进行操作。

上传 sourcemap

sourcemap 是打包生成的 .map 文件,是代码压缩后和源代码的一个映射文件,正常部署之后的代码都是压缩过的。 Sentry 只会收集浏览器端的错误,因此搜集过来的代码是压缩过的,如下图

code

但是如果我们上传了 sourcemap 文件,那么错误收集之后的样子就会如下

error

因此我们可以看出,sourcemap 文件可以极大的方便线上问题定位。上传 sourcemap 主要分为以下几个步骤

  • 打包
  • 上传 sourcemap 文件到 Sentry 服务器
  • 删除代码包中的 .map 文件(Why?)
  • 部署代码到应用服务器

为什么要上传 sourcemap 文件之后删除?是因为在正式环境暴露 sourcemap 文件就意味着把代码逻辑完全暴露给用户,极易遭受不法分子的安全攻击,或者代码机密泄漏,因此我们需要上传完成后删除 sourcemap 文件。

1、打包

打包之前需要安装部分插件

npm i @sentry/webpack-plugin -D

然后在打包入口文件中写入如下配置(以 VueCLI3 举例)

const SentryWebpackPlugin = require('@sentry/webpack-plugin')
const Pkg = require('./package.json')

module.exports = {
configureWebpack: config => {
config.plugins = [
...config.plugins,
new SentryWebpackPlugin({
release: `${Pkg.name}@${Pkg.version}`, // 注意这个版本号务必要和 Sentry 收集服务中的 版本号一致!!!!
include: ['dist'],
ignore: ['node_modules']
// urlPrefix: '~/static/js' // URL前缀,在CDN部署模式下有用
})
]
}
}

2、上传

在项目根目录下新建一个 .sentryclirc 文件,此文件为 ini 格式,写入以下配置

[defaults]
url = <sentry服务器>
org = <组织>
project = <项目名>

[auth]
token = <上传Token>

具体代表含义可以百度,以上内容均可以在 Sentry 后台中找到,举例如下

[defaults]
url = https://xxxxx.com/
org = xxxxx
project = xxxxx

[auth]
token = xxxxxx

进行上述操作之后,即可以执行打包命令,可以看到 sourcemap 上传过程

3、删除代码包中的 .map 文件

首先需要安装几个插件(插件有很多,这里只是举例)

npm i fs-extra glob -D

在项目打包配置文件中写入如下配置

const glob = require('glob')
const { removeSync } = require('fs-extra')

module.exports = {
configureWebpack: config => {
config.plugins = [
...config.plugins,
{
apply: (compiler) => {
compiler.hooks.done.tap('CleanJsMapPlugin', () => {
glob.sync('dist/**/*.js.map').forEach((f) => removeSync(f))
})
}
}
]
}
}

完整的配置文件如下

const SentryWebpackPlugin = require('@sentry/webpack-plugin')
const Pkg = require('./package.json')
const glob = require('glob')
const { removeSync } = require('fs-extra')

module.exports = {
configureWebpack: config => {
config.plugins = [
...config.plugins,
new SentryWebpackPlugin({
release: `${Pkg.name}@${Pkg.version}`, // 注意这个版本号务必要和 Sentry 收集服务中的 版本号一致!!!!
include: ['dist'],
ignore: ['node_modules']
// urlPrefix: '~/static/js' // URL前缀,在CDN部署模式下有用
}),
{
apply: (compiler) => {
compiler.hooks.done.tap('CleanJsMapPlugin', () => {
glob.sync('dist/**/*.js.map').forEach((f) => removeSync(f))
})
}
}
]
}
}

4、部署过程略

进行上述操作后,即可进行打包,部署。

注意,不同项目的上传 sourcemap 配置并不相同,请勿直接 copy。这里只是提供大概手段以及思路。

多环境配置

上述我们大致介绍了一下环境配置,实际上,上文的环境配置只能覆盖 developmentproduction 这两个环境,更多的环境例如 QAtestpre-release等并不能覆盖到,因此会导致真正的线上环境和测试环境等混淆在一起无法区分(虽然可以通过触发监控的域名区分,但是并不好,无法做警告通知,数据统计等)。

接下来我们借助一种骚操作,做到 Sentry 真正的多环境配置,即一次配置,一劳永逸。

1、静态部署多环境配置(针对非服务端渲染项目

首先我们需要借助 nginx,为什么要借助 nginx 是因为前端打包后并不能读取环境变量,但是 nginx 可以,我们可以借助 nginx 作为中转,读取环境变量然后返回给我们。先在 nginx 的配置文件中加入 header 头。

# 注意此文件实际上并不是 nginx 配置文件,是一个 nginx template 文件

location ~ .*\.(?:htm|html)$ {
add_header Cache-Control

"private, no-store, no-cache, must-revalidate, proxy-revalidate";

add_header vue_env ${VUE_ENV}; # ${VUE_ENV} 为 nginx 变量

}

从上述声明中我们不难看出,关键的是指定 ${VUE_ENV} 这个变量,这个变量的值会在入口文件的 response header 中体现出来,我们读取即可,而 ${VUE_ENV} 则可以不同环境分别指定,或者借助 docker-nginx 动态指定均可。

然后我们可以读取 response header,获取环境信息

const req = new XMLHttpRequest()
req.open('GET', window.location.href, false)
req.send(null)
const responseHeader = req
.getAllResponseHeaders()
.trim()
.split(/[\r\n]+/)
.reduce((acc, cur) => {
const parts = cur.split(': ')
return { ...acc, [parts.shift()]: parts.join(': ') }
}, {})

console.log(responseHeader) // responseHeader 即为响应头

获取到环境信息后,我们可以注入到 Sentry 中

Sentry.init({
Vue,
dsn: '此处填写 dsn',
environment: responseHeader.vue_env ?? process.env.VUE_APP_SENTRY_ENVIRONMENT,
release: `${Pkg.name}@${Pkg.version}`,
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins: ['localhost', /^\//]
})
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0
})

然后部署,即可以看到效果

2、服务端渲染的多环境配置

服务端渲染由于有 node 环境,本身支持环境变量注入,即 process.env,因此我们不需要借助 nginx。但是要注意,由于大多数前端框架处理环境变量时候使用的插件是 webpack.DefinePlugin,此插件最大的问题是使用变量替换,这也会导致在应用代码中指定的环境不会再运行时改写。

// 开发环境
console.log(process.env.NODE_ENV) // development

// 生产环境
console.log(process.env.NODE_ENV) // production

// 测试环境
console.log(process.env.NODE_ENV) // production

因此我们需要改变思路,将环境变量存起来,此处用 Nuxt 项目举例

import path from 'path'
import { removeSync, writeFileSync, createFileSync } from 'fs-extra'

// 虽然代码很丑 但是可以解决问题 后续再优化代码
if (process.env.NODE_ENV === 'production') {
const filePath = path.resolve(__dirname, './static/env')
removeSync(filePath)
createFileSync(filePath)
writeFileSync(filePath, process.env.VUE_ENV)
}

即我们写入一个 env 文件,为当前运行环境下的环境变量即可,读取时只需要做如下操作

import Vue from 'vue'
import * as Sentry from '@sentry/browser'
import { Vue as VueIntegration } from '@sentry/integrations'
import { Integrations } from '@sentry/tracing'
import Pkg from '../package.json'
import ACTIONS from '../util/vuexEvents'

export default function ({ isDev, $axios, store }) {
if (!isDev) {
$axios.get('env', { baseURL: '' }).then(({ data }) => {
store.commit(ACTIONS.ENV, data ?? 'test') // 将环境变量存起来,以防它用
Sentry.init({
dsn: '写入你的 dsn',
environment: data ?? process.env.NODE_ENV,
release: `${Pkg.name}@${Pkg.version}`,
integrations: [
new VueIntegration({
Vue,
tracing: true
}),
new Integrations.BrowserTracing({
// routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins: ['localhost', /^\//]
})
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0
})
})
}
}

然后就可以看到效果

initialScope

上述介绍了 Sentry 大体的接入流程,但是针对于某些特定场景,我们仅仅监控到错误还不够,特别是对于一些硬件设备,我们需要知道业务的标注信息,以方便及时警告。幸运的是 Sentry 为我们提供了此功能

Sentry.init({
dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
debug: true,
initialScope: {
tags: {"my-tag": "my value"},
user: {id: 42, email: "john.doe@example.com"},
}
});
Sentry.init({
dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
debug: true,
initialScope: scope => {
scope.setTags({ a: 'b' });
return scope;
},
});

两种方式均可,添加了自定义 tag 之后,在性能监控,错误监控下,都会有自定义的 tag 出现,如下图

tag

这样我们可以自定义相应的 tag 信息,以方便我们进行问题定位

注意,tag 不是越多越好,自定义标注内容也是,尽量简洁明了,亢余的信息我们不应该传递,应该根据实际业务需求传递。

其他

未完待续

文章作者: Jdeseva
文章链接: https://jdeseva.github.io/2022/01/05/Sentry/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 沧海鲸歌