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_TOKEN | Sentry 认证令牌 | abc123... |
SENTRY_URL | Sentry 服务器地址 | https://sentry.10000da.vip |
SENTRY_ORG | 组织名称 | wlyd-fe |
SENTRY_PROJECT | 项目名称 | wlyd-10000mao-user-end |
可选的环境变量
| 环境变量 | 说明 | 默认值 | 示例 |
|---|---|---|---|
SENTRY_UPLOAD_SOURCEMAP | 是否上传 sourcemap | false | true |
VUE_APP_SENTRY_DIST | Distribution(构建版本标识) | production | production |
注意: 版本号从 package.json 的 version 字段自动读取,无需配置环境变量。
运行时环境变量(错误上报)
| 环境变量 | 说明 | 默认值 | 示例 |
|---|---|---|---|
VUE_APP_SENTRY_ENABLE_DEV | 开发环境是否上报 | false | true |
VUE_APP_SENTRY_DSN | Sentry DSN | 硬编码默认值 | https://... |
VUE_APP_SENTRY_DIST | Distribution(必须与构建时一致) | production | production |
注意: 版本号通过 webpack DefinePlugin 从 package.json 的 version 字段自动注入到前端代码中,无需配置环境变量。
环境变量配置示例
.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 配置
工作原理
- 构建时:通过
@sentry/webpack-plugin在构建完成后自动上传 sourcemap 到 Sentry 服务器 - 运行时:错误上报时,Sentry 通过
release+dist从服务器匹配对应的 sourcemap
上传条件
Source map 会在以下条件同时满足时上传:
- ✅ 不是开发服务器模式(
vue-cli-service serve) - ✅
SENTRY_UPLOAD_SOURCEMAP=true - ✅ 配置了必需的环境变量(
SENTRY_AUTH_TOKEN、SENTRY_URL、SENTRY_ORG、SENTRY_PROJECT)
Release 和 Distribution 匹配
⚠️ 重要: 构建时和运行时的 release 和 dist 必须完全一致,否则无法匹配 sourcemap。
-
Release 格式:
user-end@版本号- 构建时:从
package.json的version字段读取 - 运行时:通过 webpack DefinePlugin 从
package.json的version字段注入(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 无法匹配
问题: 错误堆栈无法还原为源代码
原因: 构建时和运行时的 release 或 dist 不一致
解决方案:
- 确保
package.json中的version字段正确(构建时和运行时都会使用这个版本号) - 确保构建时和运行时使用相同的
VUE_APP_SENTRY_DIST环境变量 - 检查 Sentry 后台是否有对应的 release 和 sourcemap
- 如果修改了
package.json的version,需要重新构建并上传 sourcemap
2. 开发环境错误上报
问题: 开发环境的错误没有上报到 Sentry
原因: 开发环境默认不上报,避免污染生产数据
解决方案:
在 .env.development 中设置:
VUE_APP_SENTRY_ENABLE_DEV=true
3. Source Map 上传失败
问题: 构建时 sourcemap 上传失败
可能原因:
- 缺少必需的环境变量
SENTRY_UPLOAD_SOURCEMAP未设置为true- 网络问题或 Sentry 服务器问题
解决方案:
- 检查环境变量是否配置正确
- 确认
SENTRY_UPLOAD_SOURCEMAP=true - 检查网络连接和 Sentry 服务器状态
4. 删除本地 Source Map 后无法调试
问题: 删除本地 sourcemap 后,浏览器开发者工具无法查看原始源代码
说明:
- 这是正常现象,删除本地 sourcemap 后确实无法在浏览器中查看原始源代码
- 但 Sentry 的错误堆栈还原功能完全正常,因为 Sentry 从服务器读取 sourcemap
- 如果需要本地调试,可以临时将
deleteSourcemapsAfterUpload改为false
5. 敏感信息泄露
问题: 错误信息中包含敏感信息(如密码)
解决方案:
代码中已实现自动过滤包含 "password" 的错误信息。如果需要过滤其他敏感信息,可以在 src/sentry.js 的 beforeSend 回调中添加过滤逻辑。
配置参考
环境变量优先级
-
构建时 Source Map 上传:
- 必需:
SENTRY_AUTH_TOKEN、SENTRY_URL、SENTRY_ORG、SENTRY_PROJECT - 开关:
SENTRY_UPLOAD_SOURCEMAP=true
- 必需:
-
Release 和 Distribution:
- 版本号:从
package.json的version字段自动读取(构建时和运行时一致) VUE_APP_SENTRY_DIST:Distribution(构建时和运行时必须一致)
- 版本号:从
-
运行时错误上报:
VUE_APP_SENTRY_ENABLE_DEV:开发环境上报开关VUE_APP_SENTRY_DSN:DSN(可选,有默认值)
配置项说明
| 配置项 | 说明 | 默认值 |
|---|---|---|
deleteSourcemapsAfterUpload | 上传后是否删除本地 sourcemap | true |
silent | 是否静默输出上传日志 | true |
debug | 是否启用调试模式 | false |
cleanArtifacts | 是否清理已存在的 artifacts | true |
相关文件
- 构建配置:
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)
- ✅ 统一使用
VUE_APP_SENTRY_DIST环境变量(构建时和运行时) - ✅ 所有环境统一配置
deleteSourcemapsAfterUpload: true - ✅ 移除冗余的 console 日志
- ✅ 优化环境变量配置,减少重复设置
- ✅ 简化配置逻辑,提高可维护性
- ✅ 版本号统一从
package.json读取:移除VERSION环境变量,通过 webpack DefinePlugin 自动注入版本号到前端代码