前言
本文将为大家带来Sentry的企业级实践,内容主要涉及如下:
- Sentry的云部署
- 结合Gitlab-CI的DevOps实践
- 前端项目接入细节
- 微前端架构下的Sentry接入
- Sentry磁盘占用问题解决
介绍
前端应用 [稳定性] 没有保障 —— 测试永远无法做到100%覆盖, 用户也不会总是按照我们所预期的进行操作,因此我们需要在系统异常时主动对其进行收集上报,以制定解决方案。当生产环境中产生了一个 bug 时,如何做到迅速报警,找到问题原因,修复后又如何在线上验证?此时我们需要一个高效的错误监控系统。
搭建
环境
-
Docker 19.03.6+
-
Compose 1.24.1+
# docker-compose # 1.安装 sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 2.对二进制文件执行权限 sudo chmod +x /usr/local/bin/docker-compose # 如果这一步遇到问题,可以创建/usr/bin路径建立链接 sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose # 3.检查版本 docker-compose --version -
最小内存
2GBRAM
准备
-
仓库下载
-
SMTP邮件服务
-
此处以QQ邮箱为例 ( 设置 - 账户 ) , 开启SMTP 服务,
开启之后需要保存好密钥点击 “ 什么是 IMAP,它又是如何设置?” 查看详细设置说明
-
安装
./install.sh
创建用户
根据提示创建管理员账户
启动服务
# 构建
docker-compose build
# 启动服务
docker-compose up -d
搭建完成
-
初始化信息填写
-
配置文件(初始化未填写或后期修改)
项目接入
Sentry & Gitlab 初始配置
Sentry AuthToken 创建
Sentry添加Gitlab拓展
点击 项目 -> 集成 -> GitLab 来查看 Sentry 中是否配置了 Gitlab 项目组。
点击右上角添加按钮
按照 Sentry 的提示在 GitLab 中完成相关操作
点击提交后并成功后,GitLab项目组就添加成功了。
Sentry 新建项目
Sentry 发布仓库关联
⚠注意:添加管理追踪的 GitLab 仓库时,需要对应仓库的管理员权限。
钉钉群添加机器人
开发可自定义勾选,建议勾选issues events 、 release events、tag push events
前端工程操作
依赖安装
默认已科学上网
# SDK 安装
npm i -S @sentry/vue @sentry/tracing @sentry/integrations
# SourceMap 支持依赖安装
npm i -D @sentry/webpack-plugin
⚠️ 若@sentry/webpack-plugin安装失败,可执行以下指令重试
npm config set sentrycli_cdnurl="https://npm.taobao.org/mirrors/sentry-cli"
npm uninstall @sentry/webpack-plugin
npm i -D @sentry/webpack-plugin
commit-msg 提交脚本工具,建议全局安装 (git工作流交互式脚本,其中bugfix同步issue很nice)
npm i -g git-cz
.env.[环境] 工程环境变量配置
# sentry
SENTRY_HOST=http://192.168.5.59:9000/
VUE_APP_SENTRY_ORG=sentry
SENTRY_AUTH_TOKEN=<your sit auth token 粘贴token>
VUE_APP_SENTRY_DSN=http://xxxxxxxxxxxxxxxx@192.168.5.59:9000
VUE_APP_SENTRY_PROJECT_ID=<sit your-project-id>
Sentry SDK 集成
Sentry Vue Plugin
// > src/sentry/index.js
import pkg from '../../package.json'
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
const MateSentry = {
install: (app, opts) => {
const { router, tracingOrigins = ['localhost', process.env.VUE_APP_SENTRY_TRACING_ORIGIN, /^\//] } = opts
app.config.globalProperties.$sentry = Sentry
console.log(`[Sentry init release]${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || pkg.version}`)
Sentry.init({
app,
// 项目监控api address
dsn: `${process.env.VUE_APP_SENTRY_DSN}/${process.env.VUE_APP_SENTRY_PROJECT_ID}`,
// 发布
release: `${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || 'v' + pkg.version}`,
// 环境, 可自定义支持多环境
environment: process.env.NODE_ENV,
// is optional and is true if it is not provided. If you set it to false, Sentry will suppress sending all Vue components' props for logging.
attachProps: true,
// is optional and is false if it is not provided. If you set it to true, Sentry will call Vue's original logError function as well.
logErrors: true,
// 打开或关闭调试模式。如果启用了调试,则在发送事件出错时,SDK将尝试打印出有用的调试信息。默认值始终为false。通常不建议在生产debug中将其打开,尽管打开模式不会引起任何安全隐患。
debug: process.env.NODE_ENV === 'development',
// 初始范围数据设置, 通常可用于设置应用、用户的初始数据
initialScope: {
tags: { tag: process.env.VUE_APP_DEPLOY_VERSION || 'v' + pkg.version }
// userName: { id: 42, email: 'xxx@xxx.com' }
},
// ---性能追踪---
// 错误异常采样率
tracesSampleRate: 1.0,
// 跟踪子组件,并查看有关渲染过程的更多详细信息
trackComponents: true,
// hook 默认为 ['mounted']
hooks: ['created', 'mounted'],
// 集成配置
integrations: [
// Vue Router 路由集成
// 跟踪程序加载期间的性能
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
tracingOrigins
}),
// 控制台日志捕获
new CaptureConsoleIntegration(
{
// array of methods that should be captured
// defaults to ['log', 'info', 'warn', 'error', 'debug', 'assert']
levels: ['warn', 'error']
}
)
]
})
}
}
export default MateSentry
SDK 接入
import MateSentry from '@/sentry'
app.use(MateSentry, { router })
客户端用户数据携带
Sentry.setUser({ email: "john.doe@example.com" });
第三方SDK监控
<!-- script 标签添加 crossorigin="anonymous" 属性 -->
默认情况下(未指定crossOrigin), cors不会使用。 在非同源情况下, 设置
anonymous关键子将不会通过cookies , 客户端SSL证书或HTTP认证交换用户凭据。当需要获取用户凭据的manifest 时,即使是同源的情况下属性值必须设置为use-credentials
- 基于上述的问题,有两种方案可以解决:
- 把页面中所有的跨域资源放在跟页面同样的域下
- 通过 Patch 原生方法来尽可能的捕获到错误, 比如我们可以Patch 原生的setTimeout:
const prevSetTimeout = window.setTimeout; window.setTimeout = function(callback, timeout) { const self = this; return prevSetTimeout(function() { try { callback.call(this); } catch (e) { // 捕获到详细的错误,在这里处理日志上报等了逻辑 // ... throw e; } }, timeout); } // 但 Patch 原生方法有诸多的不确定性,建议视具体场景采用
主动上报
- Sentry.captureException(err)
[Js中可以传入一个Error实例给
captureException()以实现事件形式的主动捕获 —— 这种方式可能会抛出一个无法记录和回溯的错误字符串。](Usage for JavaScript)
// captureException
try {
aFunctionThatMightFail();
} catch (err) {
Sentry.captureException(err);
}
- Sentry.captureMessage('Something went wrong')
[另外一种常见的方式是捕获要发送给
Sentry的纯文本信息——它们对某些团队是很有用的。](Usage for JavaScript)
// captureMessage
Sentry.captureMessage("Something went wrong");
SourceMap
// vue.config.js
const SentryPlugin = require("@sentry/webpack-plugin");
const pkg = require("./package.json");
const PUBLIC_PATH = '/xxx/xxx/'
module.exports = {
productionSourceMap: true, // 生产环境开启sourceMap
configureWebpack: {
devtool: 'source-map', // 开发环境开启
plugins: [
new SentryPlugin({
// sentry-cli configuration
authToken: process.env.SENTRY_AUTH_TOKEN,
url: process.env.SENTRY_HOST,
urlPrefix: `~${PUBLIC_PATH}`,
org: process.env.VUE_APP_SENTRY_ORG,
configFile: 'sentry.properties',
project: pkg.name,
release: `${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || 'v' + pkg.version}`,
// webpack specific configuration
include: "./dist",
ignore: ["node_modules"],
errorHandler (err){
console.error('\n\n@sentry/webpack-plugin upload sourceMap error', err, '\n\n');
},
}),
],
},
// ... ...
};
CI Release 同步
⚠️ gitlab 需创建发布后才可同步sentry release
SPA单页应用集成
Gitlab Yaml准备
variables:
DOCKER_DRIVER: "overlay"
GROUP: "library"
DC_REGISTRY_PROXY: "https://harbor-xx.xxx.cc"
IMAGE_NAME: <YOUR-IMAGE-NAME>
SPACE_NAME: '-xxx-xxx'
TOKEN: ''
DEPLOYMENT_ID: ''
BUNDLE_SERVE: ''
VUE_APP_SENTRY_PROJECT_ID_SIT: 10
VUE_APP_SENTRY_PROJECT_ID_PRO: 2
VUE_APP_SENTRY_DSN_SIT: http://<project-dsn-key>d@192.168.5.59:9000
VUE_APP_SENTRY_DSN_PRO: https://<project-dsn-key>@sentry.xxxx-pro.net
SENTRY_AUTH_TOKEN_SIT: <your-access-token-for-sit>
SENTRY_AUTH_TOKEN_PRO: <your-access-token-for-pro>
SENTRY_HOST_SIT: http://192.168.5.59:9000/
SENTRY_HOST_PRO: https://sentry.xxxx-pro.net/
before_script:
- export PROJECT_IMAGE_NAME=$(cat package.json | grep name | head -1 | awk -F "[\"]" '/name/{print$4}')
- export VUE_APP_DEPLOY_VERSION=$CI_COMMIT_TAG
- export VUE_APP_SENTRY_ORG=sentry;
Gitlab创建Release
⚠️ release.changelog.txt 为更新内容备注文件, 具体规范可同步项目经理版本更新要求
# 创建gitlab release 发布
ReleaseDeploy:
stage: ReleaseDeploy
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG
script:
- echo "running release_job"
release:
name: $CI_COMMIT_TAG
description: ./release.changelog.txt # 此次更新内容
tag_name: '$CI_COMMIT_TAG'
ref: '$CI_COMMIT_TAG'
tags:
- rancher_docker
同步release发布至sentry
# 同步release发布至sentry
SyncSentry:
stage: SyncSentry
only:
- tags
script:
- echo $CI_COMMIT_REF_NAME
- if [[ $(echo $CI_COMMIT_REF_NAME | grep "rc") != "" ]]; then
export VUE_APP_SENTRY_TRACING_ORIGIN=https://activity-pre.9longe.net;
export SENTRY_HOST=$SENTRY_HOST_PRO;
export SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN_PRO;
export VUE_APP_SENTRY_DSN=$VUE_APP_SENTRY_DSN_PRO;
export VUE_APP_SENTRY_PROJECT_ID=$VUE_APP_SENTRY_PROJECT_ID_PRO;
export VUE_APP_ENV="PRE";
elif [[ $(echo $CI_COMMIT_REF_NAME | grep "master") != "" ]]; then
export VUE_APP_SENTRY_TRACING_ORIGIN=https://activity.clktec.com;
export SENTRY_HOST=$SENTRY_HOST_PRO;
export SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN_PRO;
export VUE_APP_SENTRY_DSN=$VUE_APP_SENTRY_DSN_PRO;
export VUE_APP_SENTRY_PROJECT_ID=$VUE_APP_SENTRY_PROJECT_ID_PRO;
export VUE_APP_ENV="PRO";
else
export VUE_APP_SENTRY_TRACING_ORIGIN=<你的服务地址>;
export SENTRY_HOST=$SENTRY_HOST_SIT;
export SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN_SIT;
export VUE_APP_SENTRY_DSN=$VUE_APP_SENTRY_DSN_SIT;
export VUE_APP_SENTRY_PROJECT_ID=$VUE_APP_SENTRY_PROJECT_ID_SIT;
export VUE_APP_ENV=$CI_COMMIT_REF_NAME;
fi;
# SENTRY 发布同步
- echo ''$SENTRY_HOST'api/0/organizations/'$VUE_APP_SENTRY_ORG'/releases/'$PROJECT_IMAGE_NAME'@'$VUE_APP_DEPLOY_VERSION'/deploys/' -H 'Authorization:Bearer '$SENTRY_AUTH_TOKEN'' -H 'Content-Type:application/json' -d '{"environment":"production"}'
- curl ''$SENTRY_HOST'api/0/organizations/'$VUE_APP_SENTRY_ORG'/releases/'$PROJECT_IMAGE_NAME'@'$VUE_APP_DEPLOY_VERSION'/deploys/' -H 'Authorization:Bearer '$SENTRY_AUTH_TOKEN'' -H 'Content-Type:application/json' -d '{"environment":"production"}'
tags:
- rancher_docker
⚠️ 注意
- 环境变量注入策略需要根据项目自行制定
- Sentry 同步的版本
$PROJECT_IMAGE_NAME@$VUE_APP_DEPLOY_VERSION、gitlab 创建的 release、SourceMap 中传递 release参数三者需保持一致
微前端应用集成
场景分析
- sentry sdk在window 全局对象上注册属性
- qiankun 加载子应用在沙箱中做脚本解析
子应用SentryVuePlugin
// Sentry 集成
import pkg from '../../package.json'
import * as OriginSentry from '@sentry/browser'
import {
CaptureConsole as CaptureConsoleIntegration,
Vue as VueIntegration
} from '@sentry/integrations'
// 获取主应用注入的SENTRY
const Sentry = window.$MainSentry || OriginSentry
const MateSentry = {
install: (Vue) => {
console.info(`[Sentry init release]${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || pkg.version}`)
const client = new Sentry.BrowserClient({
beforeSend: (ev) => {
if (window.__POWERED_BY_QIANKUN__ && ev.exception) {
ev.exception.values = ev.exception.values.map(item => {
const { stacktrace: { frames }, ...rest } = item
// FIXME: 主应用加载时,qiankun 加载当前js资源会在首行添加 ;(function(window, self, globalThis){with(window){;
// https://github.com/kuitos/import-html-entry/blob/master/src/index.js#L62
frames[frames.length - 1].colno -= 51
return {
...rest,
stacktrace: {
frames
}
}
})
}
return ev
},
})
const hub = new Sentry.Hub(client)
// FIXME: 注册为主要实例才会生效,
// Sentry会在下一版本改善这个问题
// https://github.com/getsentry/sentry-javascript/issues/3271#issuecomment-792849150
Sentry.makeMain(hub)
Vue.prototype.$SentryHub = hub
}
}
export {
Sentry
}
export default MateSentry
主应用SentryVuePlugin
export function startQiankun () {
registerMicroApps(apps, { // qiankun 生命周期钩子 - 微应用加载前
beforeLoad: (app, globalRef) => {
// FIXME: Sentry 注入
globalRef.$MainSentry = Sentry
},
beforeUnmount: (app, globalRef) => {
// FIXME: Sentry 删除
globalRef.$MainSentry = null
}
})
}
异常监控服务
问题查看
版本追踪
Gitlab Issue关联创建
Issue 同步解决
查看
解决
此处 git-cz 集成到项目仓库中,已全局安装的, 项目中可不集成 git-cz
Sentry磁盘占用问题解决
Sentry服务运行一段时间后
磁盘会占满 86% +
进入数据库
docker exec -it 176e1501e871 /bin/bash
删除数据
vacuumdb -U postgres -d postgres -v -f --analyze
人工转自动
我们使用 crontab 在linux实现定时任务
crontab -e
在里面输入:
0 0 * * * docker exec -it onpremise_web_1 sentry cleanup --days 7 && docker exec -it onpremise_postgres_1 vacuumdb -U postgres -d postgres -v -f --analyze复制代码
最后
以上内容为原创,转载请注明出处
你们的点赞👍是我的动力~
微信公众号 “ 前端FeWeekly ” 每周分享 1篇+ 高质量技术文章,更有技术交流群等着你,扫码关注一下吧~