vue2 项目接入 sentry监控

7 阅读13分钟

Sentry 错误监控使用文档

目录


概述

本项目集成了 Sentry 错误监控系统,用于实时捕获和追踪生产环境中的错误。

功能特性

  • ✅ 自动捕获未处理的 JavaScript 异常
  • ✅ 自动捕获 Vue 组件渲染错误
  • ✅ 自动捕获异步错误和 Promise 拒绝
  • ✅ 自动过滤敏感信息(如密码)
  • ✅ 支持 Source Map 自动上传和匹配
  • ✅ 支持用户信息关联
  • ✅ 支持手动捕获异常并添加上下文

依赖包

{
  "dependencies": {
    "@sentry/vue": "^10.25.0"
  },
  "devDependencies": {
    "@sentry/webpack-plugin": "^4.6.0"
  }
}

配置文件

  • 构建时配置build/sentry.config.js - Source Map 上传配置
  • 运行时配置src/sentry.js - 错误上报配置

快速开始

1. 初始化

Sentry 已在 src/main.js 中自动初始化:

import { initSentry } from '@/sentry'
initSentry(router)

2. 最小配置

生产环境最小配置(.env.production):

# 必需:Source Map 上传配置
SENTRY_AUTH_TOKEN=your-auth-token
SENTRY_URL=https://sentry.10000da.vip
SENTRY_ORG=wlyd-fe
SENTRY_PROJECT=wlyd-10000mao-user-end
SENTRY_UPLOAD_SOURCEMAP=true

其他配置项都有默认值,可以不设置。


环境变量配置

构建时环境变量(Source Map 上传)

必需的环境变量
环境变量说明示例
SENTRY_AUTH_TOKENSentry 认证令牌abc123...
SENTRY_URLSentry 服务器地址https://sentry.10000da.vip
SENTRY_ORG组织名称wlyd-fe
SENTRY_PROJECT项目名称wlyd-10000mao-user-end
可选的环境变量
环境变量说明默认值示例
SENTRY_UPLOAD_SOURCEMAP是否上传 sourcemapfalsetrue
VUE_APP_SENTRY_DISTDistribution(构建版本标识)productionproduction

注意: 版本号从 package.jsonversion 字段自动读取,无需配置环境变量。

运行时环境变量(错误上报)

环境变量说明默认值示例
VUE_APP_SENTRY_ENABLE_DEV开发环境是否上报falsetrue
VUE_APP_SENTRY_DSNSentry DSN硬编码默认值https://...
VUE_APP_SENTRY_DISTDistribution(必须与构建时一致)productionproduction

注意: 版本号通过 webpack DefinePlugin 从 package.jsonversion 字段自动注入到前端代码中,无需配置环境变量。

环境变量配置示例

.env.production(生产环境)
# Source Map 上传配置(构建时)
SENTRY_AUTH_TOKEN=your-auth-token
SENTRY_URL=https://sentry.10000da.vip
SENTRY_ORG=wlyd-fe
SENTRY_PROJECT=wlyd-10000mao-user-end
SENTRY_UPLOAD_SOURCEMAP=true

# Distribution 配置(构建时和运行时都需要)
VUE_APP_SENTRY_DIST=production

# 运行时配置(可选)
# VUE_APP_SENTRY_DSN=https://...  # 如果不设置,使用代码中的默认值

# 注意:版本号从 package.json 的 version 字段自动读取,无需配置
.env.uat(UAT 环境)
# Source Map 上传配置(可选)
SENTRY_AUTH_TOKEN=your-auth-token
SENTRY_URL=https://sentry.10000da.vip
SENTRY_ORG=wlyd-fe
SENTRY_PROJECT=wlyd-10000mao-user-end
SENTRY_UPLOAD_SOURCEMAP=false  # UAT 环境通常不上传

# Distribution 配置
VUE_APP_SENTRY_DIST=uat

# 注意:版本号从 package.json 的 version 字段自动读取,无需配置
.env.development(开发环境)
# Source Map 上传配置(开发环境通常不上传)
SENTRY_UPLOAD_SOURCEMAP=false

# 运行时配置
VUE_APP_SENTRY_ENABLE_DEV=false  # 开发环境默认不上报,避免污染生产数据
VUE_APP_SENTRY_DIST=development

Source Map 配置

工作原理

  1. 构建时:通过 @sentry/webpack-plugin 在构建完成后自动上传 sourcemap 到 Sentry 服务器
  2. 运行时:错误上报时,Sentry 通过 release + dist 从服务器匹配对应的 sourcemap

上传条件

Source map 会在以下条件同时满足时上传:

  1. ✅ 不是开发服务器模式(vue-cli-service serve
  2. SENTRY_UPLOAD_SOURCEMAP=true
  3. ✅ 配置了必需的环境变量(SENTRY_AUTH_TOKENSENTRY_URLSENTRY_ORGSENTRY_PROJECT

Release 和 Distribution 匹配

⚠️ 重要: 构建时和运行时的 releasedist 必须完全一致,否则无法匹配 sourcemap。

  • Release 格式user-end@版本号

    • 构建时:从 package.jsonversion 字段读取
    • 运行时:通过 webpack DefinePlugin 从 package.jsonversion 字段注入(process.env.APP_VERSION
    • 无需配置环境变量,版本号自动从 package.json 获取
  • Distribution

    • 构建时和运行时都使用 VUE_APP_SENTRY_DIST(默认 production
    • 必须保持一致

示例:

# package.json
{
  "version": "1.0.0"
}

# 构建时(无需配置 VERSION)
VUE_APP_SENTRY_DIST=production
# → Release: user-end@1.0.0, Dist: production

# 运行时(无需配置 VERSION)
VUE_APP_SENTRY_DIST=production
# → Release: user-end@1.0.0, Dist: production

Source Map 删除策略

当前配置: 所有环境在上传 sourcemap 后都会删除本地 sourcemap 文件(deleteSourcemapsAfterUpload: true

原因:

  • ✅ 节省部署包大小
  • ✅ 提高安全性(不暴露源代码)
  • 不影响 Sentry 功能(Sentry 从服务器读取 sourcemap,不依赖本地文件)

注意:

  • 删除本地 sourcemap 后,本地无法通过浏览器开发者工具查看原始源代码
  • 但 Sentry 的错误堆栈还原功能完全正常,因为 Sentry 从服务器读取 sourcemap

构建命令

# 生产环境构建(会上传 source map)
pnpm run build:prod

# UAT 环境构建(根据 .env.uat 配置决定)
pnpm run build:uat

# 开发环境构建(根据 .env.development 配置决定)
pnpm run build:dev

错误捕获与上报

自动捕获

Sentry 会自动捕获以下错误:

  • 未捕获的 JavaScript 异常
  • Vue 组件渲染错误
  • 异步错误
  • Promise 拒绝错误

手动捕获

在需要添加上下文信息时,可以手动捕获异常:

import { captureException } from '@/sentry'

try {
  await someAsyncOperation()
} catch (error) {
  captureException(error, {
    component: 'MyComponent',
    action: 'handleSubmit',
    userId: user.id
  })
}

高级用法

使用 Sentry 的高级 API:

import { Sentry } from '@/sentry'

Sentry.withScope((scope) => {
  scope.setTag('page', 'checkout')
  scope.setLevel('warning')
  Sentry.captureMessage('Checkout process started')
})

用户信息设置

在用户登录后设置用户信息,方便在 Sentry 中追踪特定用户的错误:

import { setSentryUser } from '@/sentry'

// 用户登录后设置
setSentryUser({
  id: user.id,
  email: user.email,
  username: user.username
})

// 用户登出后清空
setSentryUser(null)

使用示例

示例 1:完整的错误处理

import { captureException } from '@/sentry'

async function submitForm(formData) {
  try {
    const response = await api.submit(formData)
    return response
  } catch (error) {
    // 捕获错误并添加上下文
    captureException(error, {
      component: 'FormComponent',
      action: 'submitForm',
      formData: {
        // 注意:不要包含敏感信息(如密码)
        field1: formData.field1,
        field2: formData.field2
      }
    })
    // 继续处理错误(显示用户友好的错误提示)
    throw error
  }
}

示例 2:用户信息设置

import { setSentryUser } from '@/sentry'

// 在登录成功后
async function handleLogin(credentials) {
  try {
    const user = await api.login(credentials)
    
    // 设置 Sentry 用户信息
    setSentryUser({
      id: user.id,
      email: user.email,
      username: user.username
    })
    
    return user
  } catch (error) {
    // 错误会自动被 Sentry 捕获
    throw error
  }
}

// 在登出时
function handleLogout() {
  setSentryUser(null)
}

常见问题

1. Source Map 无法匹配

问题: 错误堆栈无法还原为源代码

原因: 构建时和运行时的 releasedist 不一致

解决方案:

  1. 确保 package.json 中的 version 字段正确(构建时和运行时都会使用这个版本号)
  2. 确保构建时和运行时使用相同的 VUE_APP_SENTRY_DIST 环境变量
  3. 检查 Sentry 后台是否有对应的 release 和 sourcemap
  4. 如果修改了 package.jsonversion,需要重新构建并上传 sourcemap

2. 开发环境错误上报

问题: 开发环境的错误没有上报到 Sentry

原因: 开发环境默认不上报,避免污染生产数据

解决方案:.env.development 中设置:

VUE_APP_SENTRY_ENABLE_DEV=true

3. Source Map 上传失败

问题: 构建时 sourcemap 上传失败

可能原因:

  1. 缺少必需的环境变量
  2. SENTRY_UPLOAD_SOURCEMAP 未设置为 true
  3. 网络问题或 Sentry 服务器问题

解决方案:

  1. 检查环境变量是否配置正确
  2. 确认 SENTRY_UPLOAD_SOURCEMAP=true
  3. 检查网络连接和 Sentry 服务器状态

4. 删除本地 Source Map 后无法调试

问题: 删除本地 sourcemap 后,浏览器开发者工具无法查看原始源代码

说明:

  • 这是正常现象,删除本地 sourcemap 后确实无法在浏览器中查看原始源代码
  • 但 Sentry 的错误堆栈还原功能完全正常,因为 Sentry 从服务器读取 sourcemap
  • 如果需要本地调试,可以临时将 deleteSourcemapsAfterUpload 改为 false

5. 敏感信息泄露

问题: 错误信息中包含敏感信息(如密码)

解决方案: 代码中已实现自动过滤包含 "password" 的错误信息。如果需要过滤其他敏感信息,可以在 src/sentry.jsbeforeSend 回调中添加过滤逻辑。


配置参考

环境变量优先级

  1. 构建时 Source Map 上传

    • 必需:SENTRY_AUTH_TOKENSENTRY_URLSENTRY_ORGSENTRY_PROJECT
    • 开关:SENTRY_UPLOAD_SOURCEMAP=true
  2. Release 和 Distribution

    • 版本号:从 package.jsonversion 字段自动读取(构建时和运行时一致)
    • VUE_APP_SENTRY_DIST:Distribution(构建时和运行时必须一致)
  3. 运行时错误上报

    • VUE_APP_SENTRY_ENABLE_DEV:开发环境上报开关
    • VUE_APP_SENTRY_DSN:DSN(可选,有默认值)

配置项说明

配置项说明默认值
deleteSourcemapsAfterUpload上传后是否删除本地 sourcemaptrue
silent是否静默输出上传日志true
debug是否启用调试模式false
cleanArtifacts是否清理已存在的 artifactstrue

相关文件

  • 构建配置build/sentry.config.js
  • 运行时配置src/sentry.js
  • Webpack 配置vue.config.js
  • 初始化src/main.js

build/sentry.config.js

const path = require('path')
const { sentryWebpackPlugin } = require('@sentry/webpack-plugin')

/**
 * 从环境变量读取 Sentry 配置
 * @returns {Object|null} 返回配置对象,如果配置不存在则返回 null
 */
function readSentryConfig() {
  const authToken = process.env.SENTRY_AUTH_TOKEN
  const url = process.env.SENTRY_URL
  const org = process.env.SENTRY_ORG
  const project = process.env.SENTRY_PROJECT

  // 如果缺少必要配置,返回 null
  if (!authToken || !url || !org || !project) {
    return null
  }

  return {
    authToken,
    url,
    org,
    project
  }
}

/**
 * 配置 Sentry Webpack 插件
 * @param {Object} webpackConfig - Webpack 配置对象
 * @param {string} version - 版本号(从 package.json 读取)
 * @returns {void}
 */
function configureSentryPlugin(webpackConfig, version) {
  // 开发服务器(vue-cli-service serve)不上传 sourcemap
  // 因为开发服务器不会生成 dist 目录,且代码是实时编译的
  // 注意:build:dev 是构建命令,不是开发服务器,需要允许上传
  const isDevServer = process.env.VUE_CLI_SERVICE_MODE === 'serve' ||
                      process.argv.includes('serve') ||
                      (process.env.npm_lifecycle_event === 'dev') // 只匹配 'dev',不匹配 'build:dev'

  if (isDevServer) {
    return
  }

  // 根据环境变量决定是否上传 source map
  if (process.env.SENTRY_UPLOAD_SOURCEMAP !== 'true') {
    return
  }

  // 使用 package.json 的 version
  const releaseName = `user-end@${version}`

  // 使用绝对路径
  const distPath = path.resolve(__dirname, '..', 'dist')

  // 从环境变量读取 Sentry 配置
  const sentryConfig = readSentryConfig()
  if (!sentryConfig) {
    return
  }

  // ============================================
  // 临时逻辑 - 允许 development/uat 模式下上传 sourcemap
  // 说明:当前配置允许在 development 和 uat 模式下上传 sourcemap
  // 后续可能改为:只有 production 模式才上传 sourcemap
  // 如果后续需要修改,可以:
  // 1. 删除或注释掉下面的 if 逻辑
  // 2. 在函数开头添加:if (process.env.NODE_ENV !== 'production') return
  // ============================================
  // 如果用户明确设置了 SENTRY_UPLOAD_SOURCEMAP=true,则强制上传
  // @sentry/webpack-plugin 会检查 process.env.NODE_ENV === 'development' 来跳过上传
  // 因此需要同时设置 NODE_ENV 和 webpack mode 为 production 以允许上传
  // 注意:这不会影响其他地方的判断,因为插件会在构建完成后执行
  const originalNodeEnv = process.env.NODE_ENV
  const originalWebpackMode = webpackConfig.mode
  const isDevelopment = originalNodeEnv === 'development' || originalWebpackMode === 'development'
  const isUat = originalWebpackMode === 'uat'

  if (isDevelopment || isUat) {
    process.env.NODE_ENV = 'production'
    webpackConfig.mode = 'production'
  }
  // ============================================

  // 添加 Sentry webpack 插件(sentryWebpackPlugin 是函数,不是构造函数)
  // 直接从环境变量传递配置参数
  const plugin = sentryWebpackPlugin({
    authToken: sentryConfig.authToken,
    org: sentryConfig.org,
    project: sentryConfig.project,
    url: sentryConfig.url,
    // 使用绝对路径,插件会在构建完成后(dist 目录已创建)执行上传
    include: distPath,
    // 明确指定要上传的文件扩展名(CSS sourcemap 通常不需要上传到 Sentry)
    ext: ['js', 'map'],
    // 忽略不需要的文件
    ignore: ['node_modules', 'vue.config.js', 'pdfjs'],
    // 配置 urlPrefix,匹配 publicPath(vue.config.js 中配置为 '/yw/'urlPrefix: '~/yw',
    // 上传后是否删除 sourcemap
    // true: 删除 sourcemap(节省空间,更安全,Sentry 从服务器读取 sourcemap,不影响功能)
    // false: 保留 sourcemap(会随构建产物部署到服务器,占用空间,且可能暴露源代码)
    // 注意:删除本地 sourcemap 不会影响 Sentry 功能,因为 Sentry 从服务器读取 sourcemap
    deleteSourcemapsAfterUpload: true,
    release: {
      name: releaseName,
      // 设置 distribution,用于区分同一个 release 的不同构建版本
      // 注意:与 src/sentry.js 中的 dist 配置保持一致
      // 使用 VUE_APP_SENTRY_DIST(构建时和运行时都可以访问)
      dist: process.env.VUE_APP_SENTRY_DIST || 'production'
    },
    // 清理已存在的 artifacts(避免重复上传)
    cleanArtifacts: true,
    // 设置为 true 会静默输出,不显示详细的上传报告(但仍会显示错误)
    silent: true,
    // 禁用调试模式,减少详细的 sourcemap 查找日志
    debug: false
  })

  webpackConfig.plugins.push(plugin)
}

module.exports = {
  configureSentryPlugin,
  readSentryConfig
}


src/sentry.js

import Vue from 'vue'
import * as Sentry from '@sentry/vue'

/**
 * 初始化Sentry配置
 * @param {Object} router - VueRouter实例
 */
export const initSentry = (router) => {
  const isDevelopment = process.env.NODE_ENV === 'development'

  // 开发环境上报开关:通过环境变量 VUE_APP_SENTRY_ENABLE_DEV 控制
  const enableDevReporting = process.env.VUE_APP_SENTRY_ENABLE_DEV === 'true'

  // 如果是开发环境且未开启上报开关,则不初始化 Sentry
  if (isDevelopment && !enableDevReporting) {
    return
  }

  // 从环境变量读取 DSN,如果没有则使用默认值
  const dsn = process.env.VUE_APP_SENTRY_DSN || 'https://7ddfabc82f9794b3e8f21af1d9e06084@sentry.10000da.vip/9'

  Sentry.init({
    // Sentry 项目 DSN,从环境变量读取,如果没有则使用默认值
    dsn,
    // Vue 应用实例(Vue 2 需要传入 Vue 构造函数)
    Vue,
    // 性能监控采样率:开发环境 100%,生产环境 20%
    tracesSampleRate: isDevelopment ? 1.0 : 0.2,
    // 环境标识
    environment: process.env.NODE_ENV || 'production',
    // 版本号,与 sourcemap 关联(必须与构建时上传 sourcemap 的 release 名称一致)
    // 使用 package.json 的 version(通过 webpack DefinePlugin 注入)
    release: 'user-end@' + (process.env.APP_VERSION || '1.0.0'),
    // 注意:与 build/sentry.config.js 中的 dist 配置保持一致
    dist: process.env.VUE_APP_SENTRY_DIST || 'production',

    // 在发送前处理事件,可过滤敏感信息
    beforeSend(event) {
      // 添加主应用标记
      if (!event.tags) {
        event.tags = {}
      }
      event.tags.appType = 'main'
      event.tags.appName = 'user-end-main'

      // 过滤包含敏感信息的错误(如密码相关)
      // 防御性检查:确保 exception 和 values 存在
      if (
        event.exception &&
        event.exception.values &&
        event.exception.values.length > 0 &&
        event.exception.values[0].value &&
        typeof event.exception.values[0].value === 'string' &&
        event.exception.values[0].value.includes('password')
      ) {
        return null // 返回 null 表示忽略此事件
      }
      return event
    }
  })
}

/**
 * 手动设置用户信息
 * @param {Object} userData - 用户信息对象,包含 id、email、username 等字段
 * @description 用于在用户登录后设置用户信息,方便在 Sentry 中追踪特定用户的错误
 */
export const setSentryUser = (userData) => {
  // 清空用户信息
  if (!userData) {
    Sentry.setUser(null)
    return
  }

  // 设置用户信息
  Sentry.setUser({
    id: userData.id,
    email: userData.email,
    username: userData.username
  })
}

/**
 * 手动捕获异常
 * @param {Error} error - 错误对象
 * @param {Object} context - 上下文信息,用于添加上下文数据(如组件名、操作等)
 * @description
 * 使用场景:
 * 1. 在 try-catch 中捕获已知错误并添加上下文信息
 * 2. 对于未处理的错误,Sentry 会自动捕获,无需手动调用此函数
 *
 * 示例:
 * try {
 *   // 可能出错的代码
 * } catch (error) {
 *   captureException(error, { component: 'MyComponent', action: 'handleSubmit' })
 * }
 */
export const captureException = (error, context = {}) => {
  // 防御性编程:防止 Sentry 本身出错影响业务逻辑
  try {
    Sentry.withScope((scope) => {
      // 添加上下文信息
      if (context && Object.keys(context).length > 0) {
        scope.setExtras(context)
      }
      Sentry.captureException(error)
    })
  } catch (err) {
    // Sentry 本身出错时静默处理,不影响业务逻辑
  }
}

// 导出Sentry实例以便直接调用高级API
export { Sentry }

vue.config.js

const webpack = require('webpack') const version = require('./package.json').version

configureWebpack: (config) = >{
	// 根据环境变量决定是否生成 sourcemap(development、uat、production 模式都支持)
	// 可通过环境变量 GENERATE_SOURCEMAP 控制(默认 true)
	// GENERATE_SOURCEMAP=false 则不生成 sourcemap
	const shouldGenerateSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'const isProduction = process.env.NODE_ENV === 'production'

	// 配置 sourcemap 生成
	if (shouldGenerateSourceMap) {
		// production 模式使用 productionSourceMap 配置
		// development 和 uat 模式需要手动配置 devtool
		if (!isProduction) {
			// development 和 uat 模式使用 source-map 生成独立的 sourcemap 文件
			config.devtool = 'source-map'
		}
		// production 模式由 productionSourceMap: true 控制
	} else {
		// 不生成 sourcemap
		config.devtool = false
	}

	config.externals = {
		'./cptable': 'var cptable'
	}
	config.watchOptions = {
		ignored: /C:\\DumpStack.log.tmp/
	}

	config.module.rules.filter(rule = >{
		return rule.test.toString().indexOf('scss') !== -1
	}).forEach(rule = >{
		rule.oneOf.forEach(oneOfRule = >{
			const sassLoaderIndex = oneOfRule.use.findIndex(useItem = >useItem.loader && useItem.loader.includes('sass-loader')) if (sassLoaderIndex !== -1) {
				oneOfRule.use.splice(sassLoaderIndex, 0, {
					loader: require.resolve('css-unicode-loader')
				})
			}
		})
	})

	// 配置 Sentry 插件(sourcemap 上传)
	configureSentryPlugin(config, version)

	// 注入版本号到前端代码(通过 DefinePlugin)
	config.plugins.push(new webpack.DefinePlugin({
		'process.env.APP_VERSION': JSON.stringify(version)
	}))

	// 配置性能提示:禁用资源大小警告
	// 由于项目包含大量业务代码和图片资源,文件较大是正常的
	config.performance = {
		hints: false // 禁用性能提示
	}
}

src/main.js

// 引入sentry
import { initSentry } from '@/sentry' 
// 初始化Sentry
initSentry(router)

更新日志

最新优化(2024)

  1. ✅ 统一使用 VUE_APP_SENTRY_DIST 环境变量(构建时和运行时)
  2. ✅ 所有环境统一配置 deleteSourcemapsAfterUpload: true
  3. ✅ 移除冗余的 console 日志
  4. ✅ 优化环境变量配置,减少重复设置
  5. ✅ 简化配置逻辑,提高可维护性
  6. 版本号统一从 package.json 读取:移除 VERSION 环境变量,通过 webpack DefinePlugin 自动注入版本号到前端代码