
前端错误监控体系的搭建从 Sentry SDK 集成到 Source Map 上传闭环错误监控体系的核心价值不是收集错误而是快速定位错误根因。生产环境的代码经过压缩混淆后堆栈信息只剩下单字母变量名和行号偏移。通过 Source Map 将压缩代码映射回源码是还原错误现场的关键环节。一、错误监控体系的整体架构一个完整的错误监控体系涉及四个环节SDK 集成进行错误捕获和上报、Source Map 在构建阶段的上传、Sentry 服务端对 Source Map 的解析还原、以及基于错误数据的告警和分析。sequenceDiagram participant Build as CI/CD 构建 participant Storage as Source Map 存储 participant Browser as 用户浏览器 participant Sentry as Sentry 服务端 participant Dev as 开发者 Build-Build: 编译生成 JS Bundle Build-Storage: 上传 Source Map Build-Build: 关联 Release 版本号 Browser-Browser: 用户触发错误 Browser-Sentry: SDK 上报错误事件 Sentry-Storage: 根据 Release 查找 Source Map Storage-Sentry: 返回对应的 Source Map Sentry-Sentry: 解析压缩堆栈为源码位置 Sentry-Dev: 告警通知含源码位置核心难点有两个Source Map 的上传时机和版本关联、生产环境 Source Map 的安全性控制。上传过早可能导致版本未部署就触发了错误匹配上传过晚则已经产生了无法解析的错误。二、Sentry SDK 的集成与错误分类Sentry SDK 的初始化需要配置 DSN、环境标识、版本关联和采样率。以下是一个生产级的初始化配置import * as Sentry from sentry/react; import { useEffect } from react; import { useLocation, useNavigationType, createRoutesFromChildren, matchRoutes, } from react-router-dom; /** * Sentry 初始化配置 * 在应用入口文件中调用 */ Sentry.init({ // DSN 通过环境变量注入,避免硬编码 dsn: import.meta.env.VITE_SENTRY_DSN, // 关联 Git 版本号和部署环境 release: import.meta.env.VITE_APP_VERSION ?? dev, environment: import.meta.env.MODE, // 采样率控制 // 生产环境 20% 采样以控制事件量 sampleRate: import.meta.env.MODE production ? 0.2 : 1.0, tracesSampleRate: import.meta.env.MODE production ? 0.05 : 1.0, // 错误过滤: 忽略已知的浏览器扩展错误 ignoreErrors: [ /ResizeObserver loop limit exceeded/i, /Non-Error promise rejection/i, /Network request failed/i, /Script error\.?$/i, /^chrome-extension:/i, ], // 数据脱敏: 在发送前移除敏感信息 beforeSend(event, hint) { // 移除请求头中的认证信息 if (event.request?.headers) { delete event.request.headers[Authorization]; delete event.request.headers[Cookie]; } // 移除 URL 中的敏感参数 if (event.request?.url) { try { const url new URL(event.request.url); url.searchParams.delete(token); url.searchParams.delete(apikey); event.request.url url.toString(); } catch { // URL 解析失败时保持原样 } } // 过滤已知的低价值错误 const error hint?.originalException; if (error instanceof Error) { const message error.message; if (message /Script error/i.test(message)) { return null; // 丢弃跨域脚本错误 } } return event; }, // React Router 集成: 为每个路由变化创建事务 integrations: [ Sentry.reactRouterV6BrowserTracingIntegration({ useEffect, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes, }), ], }); /** * 全局错误边界组件 * 捕获 React 组件树中的未处理错误 */ function SentryErrorBoundary({ children, }: { children: React.ReactNode; }) { return ( Sentry.ErrorBoundary fallback{({ error, resetError }) { // 显示用户友好的错误界面 return ( div style{{ padding: 40, textAlign: center, }} h2页面出现异常/h2 p错误已自动上报,技术团队正在处理中/p button onClick{resetError} style{{ marginTop: 16, padding: 8px 24px, cursor: pointer, }} 重试 /button /div ); }} {children} /Sentry.ErrorBoundary ); }除了 SDK 初始化还需要捕获未被 React 错误边界覆盖的错误来源/** * 注册全局未处理错误和 Promise 拒绝的捕获 * 在应用入口调用 */ function setupGlobalErrorHandlers(): void { // 全局错误捕获未被 ErrorBoundary 覆盖的 if (typeof window ! undefined) { const originalOnError window.onerror; window.onerror ( message: string | Event, source?: string, lineno?: number, colno?: number, error?: Error ) { if (error) { Sentry.captureException(error, { tags: { errorType: window_onerror, }, extra: { source, line: lineno, column: colno, }, }); } // 保留原始处理器 if (originalOnError) { originalOnError.call( window, message, source, lineno, colno, error ); } }; // 未处理的 Promise 拒绝 window.addEventListener(unhandledrejection, (event) { Sentry.captureException( event.reason instanceof Error ? event.reason : new Error(String(event.reason)), { tags: { errorType: unhandled_promise_rejection, }, } ); }); } }错误分类是减少告警噪音的关键环节。通过ignoreErrors过滤已知的浏览器扩展错误和Script error通过beforeSend对敏感数据进行脱敏通过采样率控制在保证关键错误覆盖率的同时减少事件量。三、Source Map 的构建上传闭环Source Map 的上传是错误还原的核心。上传时机需与部署版本关联避免版本不匹配。以下是 Vite 项目中集成 Sentry CLI 上传的构建配置// vite.config.ts import { defineConfig } from vite; import { sentryVitePlugin } from sentry/vite-plugin; export default defineConfig({ plugins: [ sentryVitePlugin({ // Source Map 上传配置 org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, // 构建产物目录 sourcemaps: { // 仅上传生产构建的 Source Map filesToDeleteAfterUpload: [**/*.js.map], }, // Release 版本号 release: { name: process.env.VITE_APP_VERSION, // 自动注入 Release 标识到 bundle 中 inject: true, }, }), ], build: { sourcemap: true, // 生成 Source Map rollupOptions: { output: { // 将 Source Map 放在独立文件中而非内联 sourcemapExcludeSources: false, }, }, }, });对于不使用 Sentry 官方插件的项目可以通过 Shell 脚本直接调用 Sentry CLI#!/bin/bash # scripts/upload-sourcemaps.sh # Source Map 上传脚本,由 CI 流水线调用 set -e # --- 配置区 --- SENTRY_ORG${SENTRY_ORG:-your-org} SENTRY_PROJECT${SENTRY_PROJECT:-your-project} RELEASE_VERSION${CI_COMMIT_SHA:-$(git rev-parse HEAD)} BUILD_DIRdist # --- 配置区结束 --- echo 创建 Sentry Release: ${RELEASE_VERSION} # 1. 创建 Release npx sentry/cli releases new ${RELEASE_VERSION} \ --org ${SENTRY_ORG} \ --project ${SENTRY_PROJECT} # 2. 上传 Source Map 文件 # --url-prefix: 匹配部署后的资源 URL 前缀 echo 上传 Source Map 文件... npx sentry/cli releases files ${RELEASE_VERSION} \ upload-sourcemaps ${BUILD_DIR}/assets \ --org ${SENTRY_ORG} \ --project ${SENTRY_PROJECT} \ --url-prefix ~/assets \ --validate \ --strip-common-prefix # 3. 上传后删除 Source Map 文件安全策略 echo 清理 Source Map 文件... find ${BUILD_DIR} -name *.map -type f -delete # 4. 标记 Release 已部署 echo 标记 Release 已部署... npx sentry/cli releases deploys ${RELEASE_VERSION} new \ -e ${DEPLOY_ENV:-production} \ --org ${SENTRY_ORG} # 5. 设置提交关联关联 Git Commit npx sentry/cli releases set-commits ${RELEASE_VERSION} \ --auto \ --org ${SENTRY_ORG} echo Source Map 上传完成,版本: ${RELEASE_VERSION}上传流程的关键点是上传 Source Map 后立即删除.map文件防止生产环境暴露源代码。Sentry 通过 Release 版本号将错误事件与对应的 Source Map 关联因此每次部署都需要使用唯一版本号。四、自定义业务错误上报与性能监控除了 JS 运行时错误业务逻辑中的异常也需要规范上报/** * 业务错误的上报封装 * 将业务异常分类并附带上下文信息上报 */ class BusinessErrorReporter { /** * 上报 API 请求失败 */ static reportApiError( endpoint: string, statusCode: number, errorBody: unknown ): void { Sentry.captureMessage( API 请求失败: ${endpoint}, { level: error, tags: { errorType: api_error, endpoint, statusCode: String(statusCode), }, extra: { errorBody: typeof errorBody object ? JSON.stringify(errorBody) : String(errorBody), timestamp: new Date().toISOString(), }, } ); } /** * 上报业务逻辑异常 * 如金额计算不匹配、状态流转异常等 */ static reportBusinessError( module: string, message: string, context: Recordstring, unknown ): void { Sentry.captureMessage( [${module}] ${message}, { level: warning, tags: { errorType: business_logic_error, module, }, extra: context, } ); } /** * 创建性能事务用于监控首屏加载 * 上报 Web Vitals 指标 */ static reportWebVitals(): void { if (typeof window undefined) return; // 监控 LCP (Largest Contentful Paint) try { const observer new PerformanceObserver((list) { for (const entry of list.getEntries()) { if (entry.entryType largest-contentful-paint) { const transaction Sentry.startTransaction({ name: Web Vitals - LCP, op: pageload, }); Sentry.getCurrentScope().setMeasurement( lcp, entry.startTime, millisecond ); transaction.finish(); } } }); observer.observe({ type: largest-contentful-paint, buffered: true, }); } catch { // PerformanceObserver API 不可用 } // 监控 FID (First Input Delay) try { const fidObserver new PerformanceObserver((list) { for (const entry of list.getEntries()) { const fidEntry entry as PerformanceEventTiming; Sentry.getCurrentScope().setMeasurement( fid, fidEntry.processingStart - fidEntry.startTime, millisecond ); } }); fidObserver.observe({ type: first-input, buffered: true, }); } catch { // PerformanceObserver API 不可用 } } }五、总结错误监控体系的搭建是一个从能捕获到错误到能快速定位根因的渐进过程。Sentry SDK 集成完成了基础的错误捕获和上报beforeSend和ignoreErrors实现了数据脱敏和噪音过滤Source Map 上传闭环确保了压缩堆栈能够还原为源码位置业务错误上报补充了框架层无法覆盖的异常。实际运行中以下几点值得关注版本号的管理需要与 CI/CD 流水线集成确保每次部署的版本号唯一且可追溯Source Map 的安全策略需在上传后删除.map文件同时配置 Sentry 的访问权限限制采样率需要根据事件量动态调整在错误发现率和存储成本之间取得平衡。