欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

哨兵(vue3 vite)前端监控构建高级指南(纯干货)

最编程 2024-04-08 08:01:29
...

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天, image.png

前言

Hello,最近在公司内刚刚完成了前端监控的搭建和优化,写这篇文章简单记录一下途中遇到的一些问题,以及自己的一些想法。在这期间有遇到的一些问题我发现掘金上以及一些社区里很少有有价值的信息,所以也当作是一个简单的分享。干货较多,耐心看完相信你一定会有些收获~

ps:这篇文章不会对部署sentry的过程和一些基础概念做详细的介绍(因为这部分内容网上的资料不少,官方文档里介绍的也非常详细)所以可能不太适合对sentry无任何了解的同学~

image.png

目录

  1. 简介
  2. 私有化部署&服务器注意点
  3. 前端项目接入
  4. sourcemap上传
  5. release版本控制

简介

先简单一句话介绍一下Sentry:监控项目中出现的错误,并上报到服务器中。
关于什么搭建前端监控的意义、Sentry里各种名词的定义我这里就不赘述了,有不少文章对这些内容有详细的介绍,大家可以看看了解一下。
我会在下边的内容着重介绍 sourcemap的上传、以及如何整合到开发流程(release脚本控制)这两个方面。

  • 因为对于前端项目来说,如果不能正确的上传sourcemap文件到sentry的服务器中。最终上报的错误内容只会显示压缩后的代码,基本上无法帮我们定位问题
  • 其次,如果无法和你目前团队的开发流程整合到一起,这个事情也很难推动下去

私有化部署&服务器注意点

Sentry官方提供免费的io(sentry.io/welcome/) 如果手头没有合适的服务器或者想简单先体验一下Sentry功能的可以在这里注册个账号,先搞起来。当然应用在公司内还是需要做私有化部署的。

Sentry提供了docker-compose的包,整个部署过程不会太复杂,大概几行命令就搞定详细的过程就不放在这里了,可以去文档里查也可以找运维同学协助 github.com/getsentry/s…

官方推荐配置4C8G,我这边亲测2C8G可以流畅运行,内存要保证8G否则可能部署之后打不开
sentry默认没有nginx 所以无法使用https 如果需要用域名访问的话需要自己安装一个nginx的docker镜像,并做好相应的配置
Sentry收集的错误日志还是比较耗费磁盘资源的,需要定期关注服务器的磁盘使用情况并做好清理工作,我就曾经遇到过日志打满磁盘导致的Sentry无法打开的问题

创建sentry项目

创建项目

简单介绍一下创建sentry项目的流程,在sentry页面内点击 创建项目 此处我以vue项目为例,选择后创建该项目

image.png 此时,界面会跳转到 在这里说明一下。sentry官方提供了@sentry/vue这一sdk 所以此处不使用❗️@sentry/brower

image.png 在项目内执行

yarn add @sentry/tracing @sentry/vue vite-plugin-sentry

vite-plugin-sentry用来自动上传sourcemap文件,稍后做详细介绍

创建token

image.png 在此处创建新的令牌,权限勾选如下

image.png

前端项目接入

目前我们公司的技术栈为v3+vite所以这部分的项目内接入主要以此来进行介绍,当然如果你还在使用webpack构建项目,sentry也有相应支持的插件进行sourcemap的自动化上传

main.ts

if (process.env.NODE_ENV === EnvName.PRODUCTION) { // 只在生产环境中开启sentry,调试时可以先去掉
 Sentry.init({
  app,
  dsn: '填写你自己项目的dsn',
  release: packageJson.version, // 版本控制 稍后介绍
  integrations: [new BrowserTracing()],
  tracesSampleRate: 1.0,
 })
}

其实至此,sentry就已经接入了你的项目当中,你可以在main.ts中写一个错误

throw new Error('test Sentry')

如果在sentry的页面中可以看到这个错误,就证明你的sentry服务器,以及接入的sdk已经正常工作了

sourcemap上传

sentry官方提供了sentry-cli工具来进行上传,这里介绍的插件其实就是对cli功能的一些包装和实现。用sentry-cli脚手架工具一样可以完成下面这些操作~(但不推荐)

.sentryclirc

在项目的根目录下创建此文件,并写入如下内容(此步骤可以使用 sentry-cli init来进行创建)

[auth]
token=刚刚创建的token,复制到这里
 [defaults]
 url=你私有化部署sentry的地址
 org=组织名称
 project=项目名称

vite.config.ts

import viteSentry from 'vite-plugin-sentry'

const sentryConfig: ViteSentryPluginOptions = {
 configFile: './.sentryclirc',
 release: packageJson.version, // package.json version 保持同步
 deploy: {
  env: 'production',
 },
 skipEnvironmentCheck: true, // 可以跳过环境检查
 sourceMaps: {
  include: ['./dist/assets'],
  ignore: ['node_modules'],
  urlPrefix: '~/assets',
 },
}

export default defineConfig({
    ...
    plugins: [
        ...
        process.env.NODE_ENV === 'production' ? viteSentry(sentryConfig) : null,
    ],
    build: {
     sourcemap: process.env.NODE_ENV === 'production',
    },
})

此时当执行vite build时,viteSentry这个插件会根据你rc文件中配置,以及release的设置,将构建的sourcemap文件上传到sentry对应的项目-release之下。当次版本新捕获到错误时就可以还原出错误行,以及详细的错误信息

image.png

image.png

script

因为我们打开了sourcemap的构建功能,但是此文件只应该发送到sentry服务器上,绝不能部署到线上!所以在vite bulid之后应该删除sourcemap文件。这里其实取决于不同的发布流程了,如果公司的CI/CD做的较好或者有运维同学,这部分就做脚本编排即可,这里给出一个简单的例子。

const fs = require('fs')
const path = require('path')
const filePath = './dist/assets/'

const list = fs.readdirSync(filePath)
list.forEach(file => {
 file = path.join(filePath, file)
 if (path.extname(file) === '.map') {
  fs.unlinkSync(file)
 }
})

var gFilePath = './dist/assets'
var gExtension = '.js'
var gSrcStr = '//# sourceMappingURL=[0-9A-Za-z]*.[0-9A-Za-z]*.js.map'
var gReplaceStr = ''

dirContentReplace(gFilePath, gExtension, gSrcStr, gReplaceStr) // 去掉js文件的sourcemap关联

function dirContentReplace(filePath, extension, srcStr, replaceStr) {
 var fs = require('fs')
 var path = require('path')
 //readdir方法读取文件名
 fs.readdir(filePath, 'utf8', function (err, files) {
  if (err) return console.log(err)
  //根据后缀名筛选要操作的文件
  var targetFiles = files.filter(function (file) {
   return path.extname(file).toLowerCase() === extension
  })
  targetFiles.forEach(function (item, index) {
   var itemPath = path.join(filePath, item)
   //readFile方法读取文件内容
   fs.readFile(itemPath, 'utf8', function (err, data) {
    var result = data.replace(RegExp(srcStr, 'g'), replaceStr)
    //writeFile改写文件内容
    fs.writeFile(itemPath, result, 'utf8', function (err) {
     if (err) return console.log(err)
    })
   })
  })
 })
}

function recDirContentReplace(filePath, extension, srcStr, replaceStr) {
 var path = require('path')
}

到这里呢,其实sentry的接入已经算可以正常使用的阶段了,大部分文章介绍的内容也局限于此。虽然web应用理论上只存在一个版本,我们可以直接把release写死,或者需要修改的时候手动替换一下即可。但是这部分还可以更优雅一些,就是我这边在使用package.json中的version字段来控制sentry的版本以及每次上线时release分支的tag,保持一致。

release版本控制

整体思路

main.ts 和 vite.config.ts中 sentry的release均使用 package.json中的version来统一控制
每次上线,或者fix线上问题后rebase到master之前,执行一个小脚本工具(分享在最后)更新package.json中的version -> git tag version-> 之后再进行deploy的相关流程。这样就实现了release的自动化控制,以及和开发流程的整合

/**
 * @type {{prompts?: {}|*, Separator?: Separator|{}, ui?: {BottomBar: BottomBar, Prompt: PromptUI}|*, createPromptModule?: function(*): (function(*, *): (Promise<never>))|*, prompt?: *, registerPrompt?: function(*, *): void, restoreDefaultPrompts?: function(): void}}
 */
// 更新package.json文件的版本号
const inquirer = require('inquirer')
const exec = require('child_process').exec
function index() {
 inquirer
  .prompt([
   {
    type: 'confirm',
    name: 'confirm',
    message: '???? 请确保当前位于rebase分支,且已按流程正确完成rebase操作',
   },
  ])
  .then(answers => {
   if (JSON.stringify(answers.confirm) === 'true') {
    inquirer
     .prompt([
      {
       type: 'list',
       name: 'version',
       message: '???????? 请选择更新类型',
       choices: [
        { name: '补丁(patch)', value: 'patch' },
        { name: '次要(minor)', value: 'minor' },
        { name: '主要(major)', value: 'major' },
       ],
      },
     ])
     .then(answers => {
      updatePackageJson(JSON.stringify(answers.version))
     })
   }
  })
}

// 更新package.json文件的版本号
function updatePackageJson(version) {
 var cmd = `yarn version --${version} --no-git-tag-version`
 exec(cmd, function (err, stdout, stderr) {
  if (err) {
   console.log(err)
   return
  }
  console.log(stdout)
  prePublish()
 })
}

// 获取上一次commit提交的版本号
function getLastCommitVersion() {
 var cmd = 'git log -1 --pretty=%B'
 exec(cmd, function (err, stdout, stderr) {
  if (err) {
   console.log(err)
   return
  }
  var msg = stdout.trim()
  var version = msg.split('\n')[0].split(' ')[1]
  const reg = /\d+.\d+.\d+/
  if (reg.test(version)) {
   var tagCmd = 'git tag "' + 'v' + version + '"'
   exec(tagCmd, function (err, stdout, stderr) {
    if (err) {
     console.log(err)
     return
    }
    if (stdout) {
     console.log('版本号已存在,不能重复提交')
     return
    }
    console.log('tag已创建,可以提交')
   })
  } else {
   console.log('❌ 版本号不符合规范,不能提交')
   process.exit(1)
  }
 })
}

// 将本次commit插入上一条commit消息中
function prePublish() {
 var cmd = 'git log -1 --pretty=%B'
 exec(cmd, function (err, stdout, stderr) {
  if (err) {
   console.log(err)
   return
  }
  var msg = stdout.trim()
  var cmd = 'git add . && git commit --amend -m "' + msg + '"'
  exec(cmd, function (err, stdout, stderr) {
   if (err) {
    console.log(err)
    return
   }
   console.log(stdout)
   getLastCommitVersion()
  })
 })
}

index()

问题记录&踩坑总结

authToken (sentry)

  • 登录 sentry
  • 左上角 -> api keys -> 创建新的令牌
  • ( Settings -> Account -> API -> Auth Tokens )

DSN (sentry)

  • sentry -> 项目 -> 设置 -> Instrumentation -> "获取你的 DSN"

网络问题 (network)

  • 如果因为网络问题 sentry cli 下载失败,尝试单独运行 install.js 重新安装

    • node_modules/@sentry/cli/scripts/install.js

如果您在阅读过程中发现有任何问题欢迎留言交流????