性能监控与优化之Sentry使用
创建 Sentry 项目
1. 注册 Sentry 账户
首先需要在 Sentry 官网 创建账户,可以选择:
- 免费版本:适合个人项目和小团队,每月有 5,000 个错误事件限制
- 付费版本:适合企业级应用,提供更多功能和更高的事件限制
2. 创建新项目
登录 Sentry 后,按照以下步骤创建项目:
- 点击右上角的 "Create Project" 按钮
- 选择平台类型(React、Vue、JavaScript 等)
- 配置项目设置:
- 项目名称:为项目选择一个描述性的名称
- 团队:选择项目所属的团队
- 警报规则:设置默认的错误警报规则
3. 获取项目配置信息
创建项目后,Sentry 会提供以下重要信息:
// 项目配置信息示例
const SENTRY_CONFIG = {
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
environment: 'production', // 或 'development', 'staging'
release: '1.0.0',
organization: 'your-org',
project: 'your-project-name'
};
4. 项目结构最佳实践
建议按照以下方式组织 Sentry 项目:
graph TD
A[组织 Organization] --> B[团队 Team]
B --> C[前端项目 Frontend]
B --> D[后端项目 Backend]
B --> E[移动端项目 Mobile]
C --> F[生产环境 Production]
C --> G[测试环境 Staging]
C --> H[开发环境 Development]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#e8f5e8
style E fill:#e8f5e8
5. 环境配置建议
// 环境配置示例
const environments = {
development: {
dsn: 'https://dev-key@o0.ingest.sentry.io/0',
environment: 'development',
debug: true,
tracesSampleRate: 1.0, // 开发环境全量采样
profilesSampleRate: 1.0
},
staging: {
dsn: 'https://staging-key@o0.ingest.sentry.io/0',
environment: 'staging',
debug: false,
tracesSampleRate: 0.5, // 测试环境 50% 采样
profilesSampleRate: 0.5
},
production: {
dsn: 'https://prod-key@o0.ingest.sentry.io/0',
environment: 'production',
debug: false,
tracesSampleRate: 0.1, // 生产环境 10% 采样
profilesSampleRate: 0.1
}
};
// 根据环境获取配置
const getSentryConfig = () => {
const env = process.env.NODE_ENV || 'development';
return environments[env];
};
Sentry SDK
1. 安装 Sentry SDK
根据不同的项目类型,选择合适的 SDK:
React 项目
npm install @sentry/react @sentry/tracing
# 或者使用 yarn
yarn add @sentry/react @sentry/tracing
Vue 项目
npm install @sentry/vue @sentry/tracing
# 或者使用 yarn
yarn add @sentry/vue @sentry/tracing
纯 JavaScript 项目
npm install @sentry/browser @sentry/tracing
# 或者使用 yarn
yarn add @sentry/browser @sentry/tracing
Node.js 项目
npm install @sentry/node @sentry/tracing
# 或者使用 yarn
yarn add @sentry/node @sentry/tracing
2. SDK 核心组件
Sentry SDK 主要包含以下核心组件:
// SDK 核心组件说明
const SentryComponents = {
// 核心 SDK
core: '@sentry/browser', // 或 @sentry/react, @sentry/vue 等
// 性能监控
tracing: '@sentry/tracing',
// 集成组件
integrations: {
breadcrumbs: '面包屑追踪',
globalHandlers: '全局错误处理',
httpContext: 'HTTP 上下文',
dedupe: '去重处理'
},
// 传输层
transports: {
fetch: 'Fetch API 传输',
xhr: 'XMLHttpRequest 传输'
}
};
3. 基础 SDK 初始化
React 项目初始化
import * as Sentry from '@sentry/react';
import { Integrations } from '@sentry/tracing';
Sentry.init({
dsn: 'YOUR_DSN_HERE',
environment: process.env.NODE_ENV,
integrations: [
new Integrations.BrowserTracing(),
],
tracesSampleRate: 1.0,
});
Vue 项目初始化
import * as Sentry from '@sentry/vue';
import { Integrations } from '@sentry/tracing';
const app = createApp(App);
Sentry.init({
app,
dsn: 'YOUR_DSN_HERE',
environment: process.env.NODE_ENV,
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
}),
],
tracesSampleRate: 1.0,
});
4. 高级 SDK 配置
// 高级配置示例
Sentry.init({
dsn: 'YOUR_DSN_HERE',
environment: process.env.NODE_ENV,
release: process.env.REACT_APP_VERSION,
// 性能监控配置
integrations: [
new Sentry.Integrations.BrowserTracing({
// 自动创建路由事务
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes
),
// 自动记录用户交互
tracingOrigins: ['localhost', 'your-api-domain.com', /^\//],
// 自动记录 XHR 和 fetch 请求
tracePropagationTargets: ['localhost', 'your-api-domain.com'],
}),
],
// 采样率设置
tracesSampleRate: 0.1, // 10% 的事务会被发送
profilesSampleRate: 0.1, // 10% 的事务会包含性能分析
// 错误过滤
beforeSend(event, hint) {
// 过滤掉本地开发环境的错误
if (event.environment === 'development') {
return null;
}
// 过滤掉特定的错误
if (event.exception) {
const error = hint.originalException;
if (error && error.message && error.message.includes('Network Error')) {
return null;
}
}
return event;
},
// 事务过滤
beforeSendTransaction(event) {
// 过滤掉过短的事务
if (event.start_timestamp && event.timestamp) {
const duration = event.timestamp - event.start_timestamp;
if (duration < 0.1) { // 小于 100ms
return null;
}
}
return event;
},
// 用户隐私设置
sendDefaultPii: false, // 不发送个人识别信息
// 调试选项
debug: process.env.NODE_ENV === 'development',
// 自定义标签
initialScope: {
tags: {
component: 'frontend',
feature: 'monitoring'
},
user: {
id: '1234',
email: 'user@example.com'
}
}
});
5. 多环境 SDK 配置
// 多环境配置管理
class SentryConfig {
static getConfig() {
const env = process.env.NODE_ENV;
const baseConfig = {
dsn: process.env.REACT_APP_SENTRY_DSN,
environment: env,
release: process.env.REACT_APP_VERSION,
integrations: [
new Sentry.Integrations.BrowserTracing(),
],
};
switch (env) {
case 'development':
return {
...baseConfig,
debug: true,
tracesSampleRate: 1.0,
profilesSampleRate: 1.0,
beforeSend: (event) => {
console.log('Sentry Event:', event);
return event;
}
};
case 'staging':
return {
...baseConfig,
debug: false,
tracesSampleRate: 0.5,
profilesSampleRate: 0.5,
beforeSend: (event) => {
// 测试环境可以记录更多信息
return event;
}
};
case 'production':
return {
...baseConfig,
debug: false,
tracesSampleRate: 0.1,
profilesSampleRate: 0.1,
beforeSend: (event) => {
// 生产环境过滤敏感信息
if (event.user) {
delete event.user.email;
}
return event;
}
};
default:
return baseConfig;
}
}
static init() {
Sentry.init(this.getConfig());
}
}
// 使用配置
SentryConfig.init();
6. SDK 集成验证
// 验证 SDK 是否正确集成
const verifySentryIntegration = () => {
try {
// 检查 Sentry 是否已初始化
if (!Sentry.getCurrentHub().getClient()) {
console.error('Sentry not initialized');
return false;
}
// 测试错误捕获
Sentry.captureMessage('Sentry integration test', 'info');
// 测试用户设置
Sentry.setUser({
id: 'test-user',
email: 'test@example.com'
});
// 测试标签设置
Sentry.setTag('test', 'integration');
console.log('Sentry integration verified successfully');
return true;
} catch (error) {
console.error('Sentry integration failed:', error);
return false;
}
};
// 在应用启动时验证
if (process.env.NODE_ENV === 'development') {
verifySentryIntegration();
}
配置Sentry
1. 环境变量配置
在项目根目录创建环境变量文件:
# .env.development
REACT_APP_SENTRY_DSN=https://your-dev-dsn@sentry.io/project-id
REACT_APP_SENTRY_ENVIRONMENT=development
REACT_APP_SENTRY_RELEASE=dev-1.0.0
# .env.staging
REACT_APP_SENTRY_DSN=https://your-staging-dsn@sentry.io/project-id
REACT_APP_SENTRY_ENVIRONMENT=staging
REACT_APP_SENTRY_RELEASE=staging-1.0.0
# .env.production
REACT_APP_SENTRY_DSN=https://your-prod-dsn@sentry.io/project-id
REACT_APP_SENTRY_ENVIRONMENT=production
REACT_APP_SENTRY_RELEASE=1.0.0
2. 详细配置选项
// sentry.config.js
import * as Sentry from '@sentry/react';
const sentryConfig = {
// 基础配置
dsn: process.env.REACT_APP_SENTRY_DSN,
environment: process.env.REACT_APP_SENTRY_ENVIRONMENT,
release: process.env.REACT_APP_SENTRY_RELEASE,
// 性能监控配置
tracesSampleRate: getTracesSampleRate(),
profilesSampleRate: getProfilesSampleRate(),
// 集成配置
integrations: [
// 浏览器追踪
new Sentry.Integrations.BrowserTracing({
tracingOrigins: ['localhost', 'your-api-domain.com', /^\//],
tracePropagationTargets: ['localhost', 'your-api-domain.com'],
}),
// 重放集成(用户会话录制)
new Sentry.Replay({
maskAllText: true,
blockAllMedia: true,
sampleRate: 0.1,
errorSampleRate: 1.0,
}),
],
// 错误过滤和数据清理
beforeSend: (event, hint) => {
return filterSentryEvent(event, hint);
},
beforeSendTransaction: (event) => {
return filterSentryTransaction(event);
},
// 用户隐私保护
sendDefaultPii: false,
// 调试选项
debug: process.env.NODE_ENV === 'development',
// 自定义标签
initialScope: {
tags: {
component: 'frontend',
version: process.env.REACT_APP_VERSION,
},
},
};
// 根据环境调整采样率
function getTracesSampleRate() {
switch (process.env.NODE_ENV) {
case 'development':
return 1.0;
case 'staging':
return 0.5;
case 'production':
return 0.1;
default:
return 0.1;
}
}
function getProfilesSampleRate() {
switch (process.env.NODE_ENV) {
case 'development':
return 1.0;
case 'staging':
return 0.5;
case 'production':
return 0.1;
default:
return 0.1;
}
}
// 事件过滤函数
function filterSentryEvent(event, hint) {
// 过滤本地开发环境的错误
if (event.environment === 'development') {
console.log('Sentry Event (Development):', event);
return null;
}
// 过滤网络错误
if (event.exception) {
const error = hint.originalException;
if (error && error.message) {
const message = error.message.toLowerCase();
if (message.includes('network error') ||
message.includes('fetch error') ||
message.includes('cors')) {
return null;
}
}
}
// 过滤用户取消的操作
if (event.exception && event.exception.values) {
const exception = event.exception.values[0];
if (exception.type === 'AbortError') {
return null;
}
}
// 清理敏感信息
if (event.request && event.request.headers) {
delete event.request.headers['Authorization'];
delete event.request.headers['Cookie'];
}
return event;
}
// 事务过滤函数
function filterSentryTransaction(event) {
// 过滤过短的事务
if (event.start_timestamp && event.timestamp) {
const duration = event.timestamp - event.start_timestamp;
if (duration < 0.1) {
return null;
}
}
// 过滤特定路由的事务
if (event.transaction && event.transaction.startsWith('/admin/')) {
return null;
}
return event;
}
export default sentryConfig;
3. 应用初始化配置
// src/index.js 或 src/main.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import * as Sentry from '@sentry/react';
import sentryConfig from './config/sentry.config';
import App from './App';
// 初始化 Sentry
Sentry.init(sentryConfig);
// 设置全局错误边界
const SentryApp = Sentry.withErrorBoundary(App, {
fallback: ({ error, resetError }) => (
<div className="error-boundary">
<h2>Something went wrong</h2>
<p>{error.message}</p>
<button onClick={resetError}>Try again</button>
</div>
),
beforeCapture: (scope, error, errorInfo) => {
scope.setTag('errorBoundary', true);
scope.setContext('errorInfo', errorInfo);
},
});
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<SentryApp />);
4. 高级配置选项
// 高级配置示例
const advancedConfig = {
// 自定义传输配置
transport: Sentry.makeFetchTransport,
// 自定义堆栈追踪
stackParser: Sentry.defaultStackParser,
// 自定义集成
integrations: [
new Sentry.Integrations.BrowserTracing({
// 自定义路由命名
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes
),
}),
// 自定义 HTTP 集成
new Sentry.Integrations.Http({
tracing: true,
breadcrumbs: true,
}),
// 自定义控制台集成
new Sentry.Integrations.Console({
levels: ['error', 'warn'],
}),
],
// 自定义作用域配置
beforeBreadcrumb: (breadcrumb, hint) => {
// 过滤敏感信息的面包屑
if (breadcrumb.category === 'console' && breadcrumb.data) {
delete breadcrumb.data.password;
delete breadcrumb.data.token;
}
return breadcrumb;
},
// 自定义错误分组
fingerprint: ['{{ default }}', '{{ error.type }}'],
// 自定义上下文
initialScope: {
tags: {
component: 'frontend',
feature: 'monitoring',
},
contexts: {
app: {
name: 'My App',
version: '1.0.0',
},
},
},
};
5. 源码映射配置
// webpack.config.js 或 vite.config.js
// 确保生产环境生成 source maps
module.exports = {
// ...
devtool: 'source-map',
plugins: [
// Sentry Webpack 插件
new SentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: 'your-org',
project: 'your-project',
include: './dist',
urlPrefix: '~/static/',
}),
],
};
# 上传 source maps 到 Sentry
npx @sentry/cli releases files <release-name> upload-sourcemaps ./dist
在应用中捕获错误
1. 自动错误捕获
Sentry 会自动捕获以下类型的错误:
// 未处理的 Promise 拒绝
fetch('/api/data')
.then(response => response.json())
// 如果这里抛出错误,Sentry 会自动捕获
.then(data => data.nonexistent.property);
// 全局错误
window.addEventListener('error', (event) => {
// Sentry 会自动处理这些错误
console.log('Global error caught:', event.error);
});
// 未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', (event) => {
// Sentry 会自动处理这些错误
console.log('Unhandled promise rejection:', event.reason);
});
2. React 错误边界集成
// 使用 Sentry 的错误边界
import * as Sentry from '@sentry/react';
// 方法 1: 使用 withErrorBoundary HOC
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
if (count > 5) {
throw new Error('Count exceeded maximum value');
}
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
// 包装组件
const MyComponentWithErrorBoundary = Sentry.withErrorBoundary(MyComponent, {
fallback: ({ error, resetError }) => (
<div className="error-fallback">
<h2>出错了!</h2>
<p>错误信息: {error.message}</p>
<button onClick={resetError}>重试</button>
</div>
),
beforeCapture: (scope, error, errorInfo) => {
scope.setTag('errorBoundary', true);
scope.setLevel('error');
scope.setContext('errorInfo', {
componentStack: errorInfo.componentStack,
errorBoundary: 'MyComponent',
});
},
});
// 方法 2: 使用 ErrorBoundary 组件
const App = () => {
return (
<Sentry.ErrorBoundary
fallback={ErrorFallback}
beforeCapture={(scope, error, errorInfo) => {
scope.setTag('section', 'main-app');
scope.setContext('errorInfo', errorInfo);
}}
>
<MyComponent />
</Sentry.ErrorBoundary>
);
};
// 自定义错误回退组件
const ErrorFallback = ({ error, resetError }) => {
return (
<div className="error-fallback">
<h2>应用出现错误</h2>
<details>
<summary>错误详情</summary>
<pre>{error.message}</pre>
</details>
<button onClick={resetError}>重新加载</button>
</div>
);
};
3. 异步错误处理
// API 调用错误处理
const apiService = {
async fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
return await response.json();
} catch (error) {
// 手动捕获 API 错误
Sentry.captureException(error, {
tags: {
section: 'api',
endpoint: 'fetchUserData',
},
extra: {
userId,
url: `/api/users/${userId}`,
},
});
throw error; // 重新抛出错误以便上层处理
}
},
};
// React Hook 中的错误处理
const useUserData = (userId) => {
const [userData, setUserData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const data = await apiService.fetchUserData(userId);
setUserData(data);
setError(null);
} catch (err) {
setError(err);
// 这里的错误已经被 apiService 报告给 Sentry
} finally {
setLoading(false);
}
};
fetchData();
}, [userId]);
return { userData, error, loading };
};
4. 表单验证错误
// 表单错误处理
const ContactForm = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
// 表单验证
validateForm(formData);
// 提交表单
await submitForm(formData);
// 成功处理
Sentry.addBreadcrumb({
message: 'Form submitted successfully',
level: 'info',
data: {
formType: 'contact',
userEmail: formData.email,
},
});
} catch (error) {
if (error.type === 'ValidationError') {
// 验证错误不需要报告给 Sentry
console.warn('Form validation failed:', error.message);
} else {
// 其他错误报告给 Sentry
Sentry.captureException(error, {
tags: {
section: 'form',
formType: 'contact',
},
extra: {
formData: {
...formData,
message: '[REDACTED]', // 不发送敏感信息
},
},
});
}
}
};
const validateForm = (data) => {
if (!data.name.trim()) {
const error = new Error('Name is required');
error.type = 'ValidationError';
throw error;
}
if (!data.email.includes('@')) {
const error = new Error('Valid email is required');
error.type = 'ValidationError';
throw error;
}
};
const submitForm = async (data) => {
const response = await fetch('/api/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`Form submission failed: ${response.status}`);
}
return response.json();
};
return (
<form onSubmit={handleSubmit}>
{/* 表单字段 */}
<button type="submit">Submit</button>
</form>
);
};
5. 全局错误处理策略
// 全局错误处理器
class GlobalErrorHandler {
static init() {
// 处理未捕获的错误
window.addEventListener('error', this.handleError);
window.addEventListener('unhandledrejection', this.handleUnhandledRejection);
// 处理 React 错误
this.setupReactErrorHandler();
// 处理网络错误
this.setupNetworkErrorHandler();
}
static handleError(event) {
const error = event.error || event;
// 过滤不需要报告的错误
if (this.shouldIgnoreError(error)) {
return;
}
// 添加上下文信息
Sentry.withScope((scope) => {
scope.setTag('errorType', 'global');
scope.setContext('errorEvent', {
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
});
Sentry.captureException(error);
});
}
static handleUnhandledRejection(event) {
const error = event.reason;
if (this.shouldIgnoreError(error)) {
return;
}
Sentry.withScope((scope) => {
scope.setTag('errorType', 'unhandledRejection');
scope.setContext('promiseRejection', {
reason: error.message || error,
});
Sentry.captureException(error);
});
}
static shouldIgnoreError(error) {
if (!error || !error.message) return true;
const message = error.message.toLowerCase();
const ignorePatterns = [
'network error',
'fetch error',
'cors error',
'script error',
'non-error promise rejection',
];
return ignorePatterns.some(pattern => message.includes(pattern));
}
static setupReactErrorHandler() {
// 这将在 React 错误边界中处理
}
static setupNetworkErrorHandler() {
// 拦截 fetch 请求
const originalFetch = window.fetch;
window.fetch = async (...args) => {
try {
const response = await originalFetch(...args);
if (!response.ok) {
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
error.response = response;
Sentry.captureException(error, {
tags: {
errorType: 'network',
httpStatus: response.status,
},
extra: {
url: args[0],
method: args[1]?.method || 'GET',
},
});
}
return response;
} catch (error) {
Sentry.captureException(error, {
tags: {
errorType: 'network',
},
extra: {
url: args[0],
method: args[1]?.method || 'GET',
},
});
throw error;
}
};
}
}
// 初始化全局错误处理
GlobalErrorHandler.init();
手动捕获错误
1. 基础错误捕获
// 捕获异常
Sentry.captureException(new Error('Something went wrong'));
// 捕获消息
Sentry.captureMessage('User clicked button', 'info');
// 捕获自定义事件
Sentry.captureEvent({
message: 'Custom event',
level: 'warning',
extra: {
userId: 123,
action: 'button_click',
},
});
2. 带上下文的错误捕获
// 使用 withScope 添加上下文
Sentry.withScope((scope) => {
scope.setTag('section', 'payment');
scope.setLevel('error');
scope.setUser({
id: userId,
email: userEmail,
});
scope.setExtra('paymentAmount', amount);
Sentry.captureException(error);
});
// 使用 configureScope 设置全局上下文
Sentry.configureScope((scope) => {
scope.setTag('component', 'UserDashboard');
scope.setContext('user', {
id: user.id,
role: user.role,
subscription: user.subscription,
});
});
3. 业务逻辑中的错误捕获
// 用户认证错误
const handleLogin = async (credentials) => {
try {
const response = await authService.login(credentials);
return response;
} catch (error) {
Sentry.withScope((scope) => {
scope.setTag('feature', 'authentication');
scope.setLevel('warning');
scope.setContext('loginAttempt', {
email: credentials.email,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
});
if (error.status === 401) {
scope.setTag('authError', 'unauthorized');
Sentry.captureMessage('Login failed: Invalid credentials');
} else if (error.status === 429) {
scope.setTag('authError', 'rateLimit');
Sentry.captureMessage('Login failed: Rate limit exceeded');
} else {
scope.setTag('authError', 'unknown');
Sentry.captureException(error);
}
});
throw error;
}
};
// 数据加载错误
const loadUserData = async (userId) => {
try {
const userData = await apiService.getUser(userId);
return userData;
} catch (error) {
Sentry.withScope((scope) => {
scope.setTag('feature', 'dataLoading');
scope.setContext('userDataRequest', {
userId,
endpoint: `/api/users/${userId}`,
timestamp: new Date().toISOString(),
});
if (error.status === 404) {
scope.setLevel('warning');
Sentry.captureMessage('User not found', {
extra: { userId },
});
} else {
scope.setLevel('error');
Sentry.captureException(error);
}
});
throw error;
}
};
4. 性能监控中的手动捕获
// 手动创建事务
const transaction = Sentry.startTransaction({
name: 'Data Processing',
op: 'task',
});
try {
// 执行业务逻辑
const result = await processData(data);
transaction.setStatus('ok');
transaction.setData('resultCount', result.length);
} catch (error) {
transaction.setStatus('internal_error');
Sentry.withScope((scope) => {
scope.setTag('operation', 'dataProcessing');
scope.setContext('processingData', {
dataSize: data.length,
processingTime: Date.now() - transaction.startTimestamp,
});
Sentry.captureException(error);
});
throw error;
} finally {
transaction.finish();
}
// 手动创建 Span
const span = transaction.startChild({
op: 'db.query',
description: 'Fetch user data',
});
try {
const users = await database.query('SELECT * FROM users');
span.setData('query.rows', users.length);
} catch (error) {
span.setStatus('internal_error');
Sentry.withScope((scope) => {
scope.setTag('operation', 'database');
scope.setContext('databaseQuery', {
query: 'SELECT * FROM users',
error: error.message,
});
Sentry.captureException(error);
});
} finally {
span.finish();
}
5. 自定义错误类型
// 自定义错误类
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
class BusinessLogicError extends Error {
constructor(message, code) {
super(message);
this.name = 'BusinessLogicError';
this.code = code;
}
}
// 使用自定义错误
const validateUserInput = (userData) => {
if (!userData.email) {
const error = new ValidationError('Email is required', 'email');
Sentry.withScope((scope) => {
scope.setTag('errorType', 'validation');
scope.setTag('validationField', 'email');
scope.setLevel('warning');
Sentry.captureException(error);
});
throw error;
}
if (!userData.email.includes('@')) {
const error = new ValidationError('Invalid email format', 'email');
Sentry.withScope((scope) => {
scope.setTag('errorType', 'validation');
scope.setTag('validationField', 'email');
scope.setLevel('warning');
Sentry.captureException(error);
});
throw error;
}
};
// 业务逻辑错误
const processPayment = async (paymentData) => {
if (paymentData.amount <= 0) {
const error = new BusinessLogicError('Invalid payment amount', 'INVALID_AMOUNT');
Sentry.withScope((scope) => {
scope.setTag('errorType', 'business');
scope.setTag('businessError', 'invalidAmount');
scope.setLevel('error');
scope.setContext('paymentData', {
amount: paymentData.amount,
currency: paymentData.currency,
});
Sentry.captureException(error);
});
throw error;
}
};
6. 批量错误处理
// 批量处理错误
const processBatchData = async (dataItems) => {
const results = [];
const errors = [];
for (const item of dataItems) {
try {
const result = await processItem(item);
results.push(result);
} catch (error) {
errors.push({
item,
error,
timestamp: new Date().toISOString(),
});
}
}
// 批量报告错误
if (errors.length > 0) {
Sentry.withScope((scope) => {
scope.setTag('operation', 'batchProcessing');
scope.setLevel('warning');
scope.setContext('batchResults', {
totalItems: dataItems.length,
successCount: results.length,
errorCount: errors.length,
errorRate: (errors.length / dataItems.length) * 100,
});
// 报告批量错误摘要
Sentry.captureMessage(`Batch processing completed with ${errors.length} errors`);
// 对于严重错误,逐个报告
errors.forEach((errorItem, index) => {
if (errorItem.error.severity === 'critical') {
scope.setContext('errorItem', {
index,
item: errorItem.item,
error: errorItem.error.message,
});
Sentry.captureException(errorItem.error);
}
});
});
}
return { results, errors };
};
捕获日志信息
1. 日志级别与消息捕获
// 不同级别的日志消息
Sentry.captureMessage('Application started', 'info');
Sentry.captureMessage('User login successful', 'info');
Sentry.captureMessage('Deprecated API usage detected', 'warning');
Sentry.captureMessage('Database connection failed', 'error');
Sentry.captureMessage('Critical system failure', 'fatal');
// 使用 Sentry.addBreadcrumb 记录操作轨迹
Sentry.addBreadcrumb({
message: 'User clicked submit button',
level: 'info',
category: 'ui',
data: {
buttonId: 'submit-form',
formData: {
email: 'user@example.com',
// 不记录敏感信息
},
},
});
2. 结构化日志记录
// 创建结构化日志工具
class Logger {
static logUserAction(action, userId, details = {}) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'userAction');
scope.setTag('action', action);
scope.setUser({ id: userId });
scope.setContext('actionDetails', details);
Sentry.captureMessage(`User action: ${action}`, 'info');
});
}
static logSystemEvent(event, severity = 'info', context = {}) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'systemEvent');
scope.setTag('event', event);
scope.setContext('systemContext', context);
Sentry.captureMessage(`System event: ${event}`, severity);
});
}
static logApiCall(endpoint, method, responseTime, statusCode) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'apiCall');
scope.setTag('endpoint', endpoint);
scope.setTag('method', method);
scope.setTag('statusCode', statusCode);
const level = statusCode >= 400 ? 'warning' : 'info';
Sentry.captureMessage(`API call: ${method} ${endpoint}`, level);
// 记录性能指标
Sentry.addBreadcrumb({
message: `API Response Time: ${responseTime}ms`,
level: 'info',
category: 'performance',
data: {
endpoint,
method,
responseTime,
statusCode,
},
});
});
}
static logBusinessMetric(metric, value, unit = '', context = {}) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'businessMetric');
scope.setTag('metric', metric);
scope.setContext('metricContext', {
value,
unit,
timestamp: new Date().toISOString(),
...context,
});
Sentry.captureMessage(`Business metric: ${metric} = ${value}${unit}`, 'info');
});
}
}
// 使用示例
Logger.logUserAction('purchase', userId, {
productId: 'prod-123',
amount: 99.99,
currency: 'USD',
});
Logger.logSystemEvent('cache_cleared', 'info', {
cacheType: 'redis',
clearedEntries: 1500,
});
Logger.logApiCall('/api/users', 'GET', 245, 200);
Logger.logBusinessMetric('daily_revenue', 15000, 'USD', {
date: '2024-01-15',
region: 'US',
});
3. 性能日志监控
// 性能日志监控工具
class PerformanceLogger {
static logPageLoad(pageName, loadTime, resources = {}) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'performance');
scope.setTag('performanceType', 'pageLoad');
scope.setTag('page', pageName);
scope.setContext('pageLoadMetrics', {
loadTime,
resources,
timestamp: new Date().toISOString(),
});
const level = loadTime > 3000 ? 'warning' : 'info';
Sentry.captureMessage(`Page load: ${pageName} (${loadTime}ms)`, level);
});
}
static logSlowQuery(queryName, duration, query = '') {
if (duration > 1000) { // 只记录慢查询
Sentry.withScope((scope) => {
scope.setTag('logType', 'performance');
scope.setTag('performanceType', 'slowQuery');
scope.setTag('queryName', queryName);
scope.setContext('queryMetrics', {
duration,
query: query.substring(0, 200), // 截取前200字符
timestamp: new Date().toISOString(),
});
Sentry.captureMessage(`Slow query: ${queryName} (${duration}ms)`, 'warning');
});
}
}
static logMemoryUsage(component, memoryUsage) {
if (memoryUsage > 100 * 1024 * 1024) { // 超过100MB
Sentry.withScope((scope) => {
scope.setTag('logType', 'performance');
scope.setTag('performanceType', 'memoryUsage');
scope.setTag('component', component);
scope.setContext('memoryMetrics', {
memoryUsage,
memoryUsageMB: Math.round(memoryUsage / 1024 / 1024),
timestamp: new Date().toISOString(),
});
Sentry.captureMessage(`High memory usage: ${component} (${Math.round(memoryUsage / 1024 / 1024)}MB)`, 'warning');
});
}
}
}
4. 用户行为日志
// 用户行为追踪
class UserBehaviorLogger {
static logUserInteraction(interaction, element, details = {}) {
Sentry.addBreadcrumb({
message: `User ${interaction}: ${element}`,
level: 'info',
category: 'ui',
data: {
interaction,
element,
timestamp: new Date().toISOString(),
...details,
},
});
}
static logUserJourney(step, userId, metadata = {}) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'userJourney');
scope.setTag('journeyStep', step);
scope.setUser({ id: userId });
scope.setContext('journeyMetadata', {
step,
timestamp: new Date().toISOString(),
...metadata,
});
Sentry.captureMessage(`User journey: ${step}`, 'info');
});
}
static logFeatureUsage(feature, userId, usage = {}) {
Sentry.withScope((scope) => {
scope.setTag('logType', 'featureUsage');
scope.setTag('feature', feature);
scope.setUser({ id: userId });
scope.setContext('featureUsage', {
feature,
usage,
timestamp: new Date().toISOString(),
});
Sentry.captureMessage(`Feature usage: ${feature}`, 'info');
});
}
}
// 使用示例
UserBehaviorLogger.logUserInteraction('click', 'submit-button', {
formId: 'contact-form',
formValid: true,
});
UserBehaviorLogger.logUserJourney('checkout_completed', userId, {
totalAmount: 149.99,
itemsCount: 3,
paymentMethod: 'credit_card',
});
UserBehaviorLogger.logFeatureUsage('dark_mode', userId, {
enabled: true,
previousSetting: 'light',
});
5. 错误日志聚合
// 错误日志聚合工具
class ErrorAggregator {
constructor() {
this.errorCounts = new Map();
this.reportInterval = 60000; // 1分钟报告一次
this.startReporting();
}
logError(errorType, details = {}) {
const key = `${errorType}_${JSON.stringify(details)}`;
const count = this.errorCounts.get(key) || 0;
this.errorCounts.set(key, count + 1);
// 立即报告严重错误
if (details.severity === 'critical') {
Sentry.withScope((scope) => {
scope.setTag('logType', 'criticalError');
scope.setTag('errorType', errorType);
scope.setContext('errorDetails', details);
Sentry.captureMessage(`Critical error: ${errorType}`, 'fatal');
});
}
}
startReporting() {
setInterval(() => {
if (this.errorCounts.size > 0) {
this.reportAggregatedErrors();
this.errorCounts.clear();
}
}, this.reportInterval);
}
reportAggregatedErrors() {
const errorSummary = Array.from(this.errorCounts.entries()).map(([error, count]) => {
try {
const [errorType, detailsStr] = error.split('_');
const details = JSON.parse(detailsStr);
return { errorType, count, details };
} catch {
return { errorType: error, count, details: {} };
}
});
Sentry.withScope((scope) => {
scope.setTag('logType', 'errorSummary');
scope.setContext('errorSummary', {
reportingPeriod: this.reportInterval,
totalErrors: errorSummary.reduce((sum, item) => sum + item.count, 0),
uniqueErrors: errorSummary.length,
errors: errorSummary,
});
Sentry.captureMessage(`Error summary: ${errorSummary.length} unique errors`, 'info');
});
}
}
// 使用示例
const errorAggregator = new ErrorAggregator();
// 记录错误
errorAggregator.logError('validation_error', {
field: 'email',
value: 'invalid-email',
});
errorAggregator.logError('api_error', {
endpoint: '/api/users',
status: 500,
severity: 'critical',
});
使用 Breadcrumbs 添加上下文信息
1. 什么是 Breadcrumbs
Breadcrumbs 是在错误发生之前的一系列事件记录,帮助开发者了解错误发生的上下文和用户操作轨迹。
// 基础 Breadcrumb 使用
Sentry.addBreadcrumb({
message: 'User logged in',
level: 'info',
category: 'auth',
data: {
userId: 12345,
method: 'email',
timestamp: new Date().toISOString(),
},
});
2. 不同类型的 Breadcrumbs
// UI 交互 Breadcrumbs
const logUIInteraction = (element, action, details = {}) => {
Sentry.addBreadcrumb({
message: `User ${action} ${element}`,
level: 'info',
category: 'ui',
data: {
element,
action,
...details,
},
});
};
// 使用示例
logUIInteraction('submit-button', 'clicked', {
formId: 'contact-form',
formValid: true,
});
logUIInteraction('dropdown-menu', 'opened', {
menuId: 'user-menu',
options: ['profile', 'settings', 'logout'],
});
// 网络请求 Breadcrumbs
const logNetworkRequest = (url, method, status, duration) => {
Sentry.addBreadcrumb({
message: `${method} ${url}`,
level: status >= 400 ? 'warning' : 'info',
category: 'http',
data: {
url,
method,
statusCode: status,
duration,
},
});
};
// 使用示例
logNetworkRequest('/api/users', 'GET', 200, 245);
logNetworkRequest('/api/orders', 'POST', 500, 1200);
// 导航 Breadcrumbs
const logNavigation = (from, to, method = 'navigate') => {
Sentry.addBreadcrumb({
message: `Navigation: ${from} -> ${to}`,
level: 'info',
category: 'navigation',
data: {
from,
to,
method,
},
});
};
// 使用示例
logNavigation('/home', '/profile', 'click');
logNavigation('/products', '/cart', 'redirect');
3. 自动 Breadcrumbs 配置
// 在 Sentry 初始化时配置自动 Breadcrumbs
Sentry.init({
dsn: 'YOUR_DSN_HERE',
integrations: [
new Sentry.Integrations.BrowserTracing(),
// 自动记录控制台输出
new Sentry.Integrations.Console({
levels: ['error', 'warn', 'info'],
}),
// 自动记录 DOM 事件
new Sentry.Integrations.Dom({
enableUI: true,
}),
],
// 过滤和修改 Breadcrumbs
beforeBreadcrumb: (breadcrumb, hint) => {
// 过滤敏感信息
if (breadcrumb.category === 'console') {
// 不记录包含密码的控制台输出
if (breadcrumb.message && breadcrumb.message.includes('password')) {
return null;
}
}
// 过滤频繁的网络请求
if (breadcrumb.category === 'fetch' && breadcrumb.data?.url?.includes('/api/health')) {
return null;
}
// 修改 Breadcrumb 内容
if (breadcrumb.category === 'ui' && breadcrumb.data) {
// 移除敏感数据
delete breadcrumb.data.password;
delete breadcrumb.data.creditCard;
}
return breadcrumb;
},
});
4. 用户操作轨迹追踪
// 用户操作轨迹追踪工具
class UserActionTracker {
constructor() {
this.sessionId = this.generateSessionId();
this.actionSequence = 0;
this.setupEventListeners();
}
generateSessionId() {
return Math.random().toString(36).substr(2, 9);
}
setupEventListeners() {
// 追踪点击事件
document.addEventListener('click', (event) => {
const element = event.target;
const elementInfo = this.getElementInfo(element);
this.logAction('click', elementInfo);
});
// 追踪表单提交
document.addEventListener('submit', (event) => {
const form = event.target;
const formInfo = this.getFormInfo(form);
this.logAction('form_submit', formInfo);
});
// 追踪页面可见性变化
document.addEventListener('visibilitychange', () => {
this.logAction('visibility_change', {
hidden: document.hidden,
});
});
}
getElementInfo(element) {
return {
tagName: element.tagName,
id: element.id,
className: element.className,
textContent: element.textContent?.substring(0, 50),
href: element.href,
};
}
getFormInfo(form) {
return {
id: form.id,
action: form.action,
method: form.method,
fieldCount: form.elements.length,
};
}
logAction(action, details = {}) {
this.actionSequence++;
Sentry.addBreadcrumb({
message: `User action: ${action}`,
level: 'info',
category: 'user_action',
data: {
action,
sequence: this.actionSequence,
sessionId: this.sessionId,
timestamp: new Date().toISOString(),
...details,
},
});
}
}
// 初始化用户操作追踪
const userActionTracker = new UserActionTracker();
5. 性能监控 Breadcrumbs
// 性能监控 Breadcrumbs
class PerformanceBreadcrumbs {
static logPageLoad() {
window.addEventListener('load', () => {
const navigation = performance.getEntriesByType('navigation')[0];
Sentry.addBreadcrumb({
message: 'Page load completed',
level: 'info',
category: 'performance',
data: {
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
totalTime: navigation.loadEventEnd - navigation.fetchStart,
},
});
});
}
static logResourceLoad() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 1000) { // 只记录慢速资源
Sentry.addBreadcrumb({
message: `Slow resource: ${entry.name}`,
level: 'warning',
category: 'performance',
data: {
name: entry.name,
duration: entry.duration,
size: entry.transferSize,
type: entry.initiatorType,
},
});
}
}
});
observer.observe({ entryTypes: ['resource'] });
}
static logLongTask() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
Sentry.addBreadcrumb({
message: 'Long task detected',
level: 'warning',
category: 'performance',
data: {
duration: entry.duration,
startTime: entry.startTime,
},
});
}
});
observer.observe({ entryTypes: ['longtask'] });
}
}
// 初始化性能监控
PerformanceBreadcrumbs.logPageLoad();
PerformanceBreadcrumbs.logResourceLoad();
PerformanceBreadcrumbs.logLongTask();
6. 错误上下文重建
// 错误上下文重建工具
class ErrorContextBuilder {
static buildUserJourney(breadcrumbs) {
const userActions = breadcrumbs
.filter(b => b.category === 'user_action')
.map(b => ({
action: b.data.action,
timestamp: b.data.timestamp,
details: b.data,
}));
return {
totalActions: userActions.length,
actions: userActions.slice(-10), // 最近10个操作
sessionDuration: this.calculateSessionDuration(userActions),
};
}
static buildNetworkHistory(breadcrumbs) {
const networkCalls = breadcrumbs
.filter(b => b.category === 'http')
.map(b => ({
url: b.data.url,
method: b.data.method,
status: b.data.statusCode,
duration: b.data.duration,
}));
return {
totalCalls: networkCalls.length,
recentCalls: networkCalls.slice(-5), // 最近5个请求
errorCalls: networkCalls.filter(c => c.status >= 400),
};
}
static buildPerformanceContext(breadcrumbs) {
const performanceEvents = breadcrumbs
.filter(b => b.category === 'performance')
.map(b => ({
type: b.message,
data: b.data,
}));
return {
totalEvents: performanceEvents.length,
events: performanceEvents,
};
}
static calculateSessionDuration(actions) {
if (actions.length < 2) return 0;
const first = new Date(actions[0].timestamp);
const last = new Date(actions[actions.length - 1].timestamp);
return last.getTime() - first.getTime();
}
}
// 在错误处理中使用
Sentry.withScope((scope) => {
const breadcrumbs = Sentry.getCurrentHub().getScope()._breadcrumbs;
const userJourney = ErrorContextBuilder.buildUserJourney(breadcrumbs);
const networkHistory = ErrorContextBuilder.buildNetworkHistory(breadcrumbs);
const performanceContext = ErrorContextBuilder.buildPerformanceContext(breadcrumbs);
scope.setContext('userJourney', userJourney);
scope.setContext('networkHistory', networkHistory);
scope.setContext('performanceContext', performanceContext);
Sentry.captureException(error);
});
调试与验证
1. 本地开发调试
// 开发环境下的调试配置
if (process.env.NODE_ENV === 'development') {
Sentry.init({
dsn: 'YOUR_DEV_DSN',
debug: true, // 开启调试模式
tracesSampleRate: 1.0, // 全量采样
beforeSend: (event, hint) => {
console.log('Sentry Event:', event);
console.log('Sentry Hint:', hint);
return event;
},
});
}
// 手动测试错误捕获
const testSentryCapture = () => {
console.log('Testing Sentry error capture...');
try {
throw new Error('Test error for Sentry');
} catch (error) {
Sentry.captureException(error);
console.log('Error sent to Sentry');
}
};
// 测试消息捕获
const testSentryMessage = () => {
Sentry.captureMessage('Test message from development', 'info');
console.log('Message sent to Sentry');
};
// 测试面包屑
const testSentryBreadcrumb = () => {
Sentry.addBreadcrumb({
message: 'Test breadcrumb',
level: 'info',
category: 'test',
data: {
timestamp: new Date().toISOString(),
testValue: 'development',
},
});
console.log('Breadcrumb added to Sentry');
};
// 在控制台中提供测试函数
if (process.env.NODE_ENV === 'development') {
window.sentryTest = {
testError: testSentryCapture,
testMessage: testSentryMessage,
testBreadcrumb: testSentryBreadcrumb,
};
}
2. 自动化测试
// Sentry 集成测试
const sentryIntegrationTest = async () => {
const tests = [
{
name: 'Error Capture',
test: () => {
const error = new Error('Test error');
Sentry.captureException(error);
return Promise.resolve(true);
},
},
{
name: 'Message Capture',
test: () => {
Sentry.captureMessage('Test message', 'info');
return Promise.resolve(true);
},
},
{
name: 'User Context',
test: () => {
Sentry.setUser({
id: 'test-user-123',
email: 'test@example.com',
});
return Promise.resolve(true);
},
},
{
name: 'Tags',
test: () => {
Sentry.setTag('test-tag', 'test-value');
return Promise.resolve(true);
},
},
{
name: 'Breadcrumbs',
test: () => {
Sentry.addBreadcrumb({
message: 'Test breadcrumb',
level: 'info',
category: 'test',
});
return Promise.resolve(true);
},
},
];
console.log('Starting Sentry integration tests...');
for (const test of tests) {
try {
await test.test();
console.log(`✓ ${test.name} test passed`);
} catch (error) {
console.error(`✗ ${test.name} test failed:`, error);
}
}
console.log('Sentry integration tests completed');
};
// 在应用启动时运行测试
if (process.env.NODE_ENV === 'development') {
setTimeout(sentryIntegrationTest, 2000);
}
3. 性能监控验证
// 性能监控验证
const validatePerformanceMonitoring = () => {
// 模拟慢速操作
const simulateSlowOperation = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Slow operation completed');
}, 2000);
});
};
// 手动创建事务
const transaction = Sentry.startTransaction({
name: 'Test Transaction',
op: 'test',
});
Sentry.getCurrentHub().configureScope((scope) => {
scope.setSpan(transaction);
});
// 创建子 span
const span = transaction.startChild({
op: 'test.slow_operation',
description: 'Simulating slow operation',
});
simulateSlowOperation().then(() => {
span.finish();
transaction.finish();
console.log('Performance transaction completed');
});
};
// 模拟用户交互性能监控
const simulateUserInteraction = () => {
const transaction = Sentry.startTransaction({
name: 'User Interaction Test',
op: 'navigation',
});
// 模拟多个步骤
const steps = [
{ name: 'Load Component', duration: 200 },
{ name: 'Fetch Data', duration: 500 },
{ name: 'Render UI', duration: 100 },
];
let currentTime = 0;
steps.forEach((step, index) => {
setTimeout(() => {
const span = transaction.startChild({
op: 'ui.interaction',
description: step.name,
});
setTimeout(() => {
span.finish();
if (index === steps.length - 1) {
transaction.finish();
console.log('User interaction transaction completed');
}
}, step.duration);
}, currentTime);
currentTime += step.duration;
});
};
// 在开发环境中提供测试函数
if (process.env.NODE_ENV === 'development') {
window.sentryPerformanceTest = {
validatePerformance: validatePerformanceMonitoring,
simulateInteraction: simulateUserInteraction,
};
}
4. 错误边界测试
// 错误边界测试组件
const ErrorBoundaryTest = () => {
const [shouldError, setShouldError] = useState(false);
const ThrowError = () => {
if (shouldError) {
throw new Error('Test error boundary');
}
return <div>Component working normally</div>;
};
return (
<div>
<h3>Error Boundary Test</h3>
<button onClick={() => setShouldError(true)}>
Trigger Error
</button>
<button onClick={() => setShouldError(false)}>
Reset
</button>
<Sentry.ErrorBoundary
fallback={({ error, resetError }) => (
<div style={{ padding: '20px', backgroundColor: '#fee' }}>
<h4>Error Boundary Caught Error</h4>
<p>{error.message}</p>
<button onClick={resetError}>Reset Error</button>
</div>
)}
beforeCapture={(scope, error, errorInfo) => {
scope.setTag('errorBoundary', 'test');
scope.setContext('errorInfo', errorInfo);
}}
>
<ThrowError />
</Sentry.ErrorBoundary>
</div>
);
};
5. 生产环境验证
// 生产环境验证工具
const ProductionValidator = {
validateSentryConnection: async () => {
try {
// 发送测试事件
Sentry.captureMessage('Production validation test', 'info');
// 检查 Sentry 客户端是否正常工作
const hub = Sentry.getCurrentHub();
const client = hub.getClient();
if (!client) {
throw new Error('Sentry client not initialized');
}
console.log('Sentry connection validated successfully');
return true;
} catch (error) {
console.error('Sentry validation failed:', error);
return false;
}
},
validateEnvironmentConfig: () => {
const config = {
dsn: process.env.REACT_APP_SENTRY_DSN,
environment: process.env.NODE_ENV,
release: process.env.REACT_APP_VERSION,
};
const issues = [];
if (!config.dsn) {
issues.push('DSN not configured');
}
if (!config.environment) {
issues.push('Environment not set');
}
if (!config.release) {
issues.push('Release version not set');
}
if (issues.length > 0) {
console.error('Sentry configuration issues:', issues);
return false;
}
console.log('Sentry configuration validated:', config);
return true;
},
validateSourceMaps: () => {
// 检查 source maps 是否正常工作
const testError = new Error('Source map test error');
// 检查堆栈跟踪是否包含源代码信息
const hasSourceInfo = testError.stack?.includes('.js:');
if (!hasSourceInfo) {
console.warn('Source maps may not be working correctly');
return false;
}
console.log('Source maps appear to be working');
return true;
},
runFullValidation: async () => {
console.log('Running full Sentry validation...');
const results = {
connection: await ProductionValidator.validateSentryConnection(),
config: ProductionValidator.validateEnvironmentConfig(),
sourceMaps: ProductionValidator.validateSourceMaps(),
};
const allPassed = Object.values(results).every(result => result);
if (allPassed) {
console.log('✓ All Sentry validations passed');
} else {
console.error('✗ Some Sentry validations failed:', results);
}
return results;
},
};
// 在应用启动时运行验证
if (process.env.NODE_ENV === 'production') {
// 延迟执行验证,确保应用完全加载
setTimeout(() => {
ProductionValidator.runFullValidation();
}, 5000);
}
6. 测试环境集成
// Jest 测试中的 Sentry 集成
// __tests__/sentry.test.js
import * as Sentry from '@sentry/react';
// Mock Sentry 在测试中
jest.mock('@sentry/react', () => ({
captureException: jest.fn(),
captureMessage: jest.fn(),
addBreadcrumb: jest.fn(),
setUser: jest.fn(),
setTag: jest.fn(),
withScope: jest.fn((callback) => callback({
setTag: jest.fn(),
setContext: jest.fn(),
setLevel: jest.fn(),
})),
}));
describe('Sentry Integration', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('should capture exceptions', () => {
const error = new Error('Test error');
Sentry.captureException(error);
expect(Sentry.captureException).toHaveBeenCalledWith(error);
});
test('should capture messages', () => {
Sentry.captureMessage('Test message', 'info');
expect(Sentry.captureMessage).toHaveBeenCalledWith('Test message', 'info');
});
test('should add breadcrumbs', () => {
const breadcrumb = {
message: 'Test breadcrumb',
level: 'info',
category: 'test',
};
Sentry.addBreadcrumb(breadcrumb);
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(breadcrumb);
});
test('should set user context', () => {
const user = {
id: 'test-user',
email: 'test@example.com',
};
Sentry.setUser(user);
expect(Sentry.setUser).toHaveBeenCalledWith(user);
});
});
// Cypress 测试中的 Sentry 集成
// cypress/support/commands.js
Cypress.Commands.add('mockSentry', () => {
cy.window().then((win) => {
win.Sentry = {
captureException: cy.stub().as('sentryException'),
captureMessage: cy.stub().as('sentryMessage'),
addBreadcrumb: cy.stub().as('sentryBreadcrumb'),
};
});
});
// cypress/integration/sentry.spec.js
describe('Sentry Integration E2E', () => {
beforeEach(() => {
cy.mockSentry();
cy.visit('/');
});
it('should capture errors when they occur', () => {
// 触发错误
cy.get('[data-test="error-button"]').click();
// 验证 Sentry 被调用
cy.get('@sentryException').should('have.been.called');
});
it('should add breadcrumbs for user actions', () => {
cy.get('[data-test="user-action"]').click();
cy.get('@sentryBreadcrumb').should('have.been.called');
});
});
监控性能
1. 启用性能监控
// 基础性能监控配置
Sentry.init({
dsn: 'YOUR_DSN_HERE',
integrations: [
new Sentry.Integrations.BrowserTracing({
// 自动追踪路由变化
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes
),
// 设置追踪原点
tracingOrigins: ['localhost', 'your-api-domain.com', /^\/api/],
}),
],
// 设置采样率
tracesSampleRate: 0.1, // 10%
// 设置性能分析采样率
profilesSampleRate: 0.1, // 10%
});
2. 自动性能监控
// 自动监控的性能指标
const PerformanceMonitor = {
// 页面加载性能监控
monitorPageLoad: () => {
window.addEventListener('load', () => {
const navigation = performance.getEntriesByType('navigation')[0];
Sentry.withScope((scope) => {
scope.setTag('performance', 'page_load');
scope.setContext('pageLoad', {
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
ttfb: navigation.responseStart - navigation.requestStart,
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
tls: navigation.secureConnectionStart ? navigation.connectEnd - navigation.secureConnectionStart : 0,
});
// 如果加载时间过长,记录为警告
const totalLoadTime = navigation.loadEventEnd - navigation.fetchStart;
if (totalLoadTime > 3000) {
Sentry.captureMessage(`Slow page load: ${totalLoadTime}ms`, 'warning');
}
});
});
},
// 监控资源加载性能
monitorResourceLoad: () => {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 1000) { // 资源加载超过 1 秒
Sentry.withScope((scope) => {
scope.setTag('performance', 'slow_resource');
scope.setContext('resourceLoad', {
name: entry.name,
duration: entry.duration,
size: entry.transferSize,
type: entry.initiatorType,
});
Sentry.captureMessage(`Slow resource: ${entry.name}`, 'warning');
});
}
}
});
observer.observe({ entryTypes: ['resource'] });
},
// 监控长任务
monitorLongTasks: () => {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
Sentry.withScope((scope) => {
scope.setTag('performance', 'long_task');
scope.setContext('longTask', {
duration: entry.duration,
startTime: entry.startTime,
});
Sentry.captureMessage(`Long task detected: ${entry.duration}ms`, 'warning');
});
}
});
observer.observe({ entryTypes: ['longtask'] });
},
// 监控内存使用
monitorMemoryUsage: () => {
if (performance.memory) {
setInterval(() => {
const memoryInfo = performance.memory;
const usedMB = memoryInfo.usedJSHeapSize / 1024 / 1024;
// 如果内存使用超过 100MB
if (usedMB > 100) {
Sentry.withScope((scope) => {
scope.setTag('performance', 'memory_usage');
scope.setContext('memoryUsage', {
used: usedMB,
total: memoryInfo.totalJSHeapSize / 1024 / 1024,
limit: memoryInfo.jsHeapSizeLimit / 1024 / 1024,
});
Sentry.captureMessage(`High memory usage: ${usedMB.toFixed(2)}MB`, 'warning');
});
}
}, 30000); // 每 30 秒检查一次
}
},
};
// 初始化性能监控
PerformanceMonitor.monitorPageLoad();
PerformanceMonitor.monitorResourceLoad();
PerformanceMonitor.monitorLongTasks();
PerformanceMonitor.monitorMemoryUsage();
3. API 请求性能监控
// API 请求性能监控
class APIPerformanceMonitor {
constructor() {
this.interceptFetch();
this.interceptXHR();
}
interceptFetch() {
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const startTime = performance.now();
const url = args[0];
const options = args[1] || {};
// 创建性能追踪
const transaction = Sentry.getCurrentHub().getScope()?.getTransaction();
const span = transaction?.startChild({
op: 'http.client',
description: `${options.method || 'GET'} ${url}`,
});
try {
const response = await originalFetch(...args);
const duration = performance.now() - startTime;
// 记录性能指标
span?.setData('http.response.status_code', response.status);
span?.setData('http.response.content_length', response.headers.get('content-length'));
// 记录慢请求
if (duration > 2000) {
Sentry.withScope((scope) => {
scope.setTag('performance', 'slow_api');
scope.setContext('apiCall', {
url,
method: options.method || 'GET',
duration,
status: response.status,
});
Sentry.captureMessage(`Slow API call: ${url} (${duration}ms)`, 'warning');
});
}
// 记录错误状态
if (response.status >= 400) {
Sentry.withScope((scope) => {
scope.setTag('performance', 'api_error');
scope.setContext('apiError', {
url,
method: options.method || 'GET',
status: response.status,
duration,
});
Sentry.captureMessage(`API error: ${response.status} ${url}`, 'error');
});
}
span?.setStatus('ok');
return response;
} catch (error) {
span?.setStatus('internal_error');
Sentry.withScope((scope) => {
scope.setTag('performance', 'api_failure');
scope.setContext('apiFailure', {
url,
method: options.method || 'GET',
error: error.message,
});
Sentry.captureException(error);
});
throw error;
} finally {
span?.finish();
}
};
}
interceptXHR() {
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._method = method;
this._url = url;
this._startTime = performance.now();
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
const xhr = this;
xhr.addEventListener('loadend', () => {
const duration = performance.now() - xhr._startTime;
if (duration > 2000) {
Sentry.withScope((scope) => {
scope.setTag('performance', 'slow_xhr');
scope.setContext('xhrCall', {
url: xhr._url,
method: xhr._method,
duration,
status: xhr.status,
});
Sentry.captureMessage(`Slow XHR call: ${xhr._url} (${duration}ms)`, 'warning');
});
}
});
return originalSend.apply(this, arguments);
};
}
}
// 初始化 API 性能监控
const apiMonitor = new APIPerformanceMonitor();
4. React 组件性能监控
// React 组件性能监控 Hook
const usePerformanceMonitoring = (componentName) => {
const renderStartTime = useRef(null);
const mountTime = useRef(null);
useEffect(() => {
// 组件挂载时间
mountTime.current = performance.now();
return () => {
// 组件卸载时间
const unmountTime = performance.now();
const lifeTime = unmountTime - mountTime.current;
if (lifeTime > 10000) { // 组件存在超过 10 秒
Sentry.withScope((scope) => {
scope.setTag('performance', 'component_lifetime');
scope.setContext('componentLifetime', {
componentName,
lifeTime,
});
Sentry.captureMessage(`Long-lived component: ${componentName} (${lifeTime}ms)`, 'info');
});
}
};
}, [componentName]);
const startRender = useCallback(() => {
renderStartTime.current = performance.now();
}, []);
const endRender = useCallback(() => {
if (renderStartTime.current) {
const renderTime = performance.now() - renderStartTime.current;
if (renderTime > 50) { // 渲染时间超过 50ms
Sentry.withScope((scope) => {
scope.setTag('performance', 'slow_render');
scope.setContext('slowRender', {
componentName,
renderTime,
});
Sentry.captureMessage(`Slow render: ${componentName} (${renderTime}ms)`, 'warning');
});
}
}
}, [componentName]);
return { startRender, endRender };
};
// 使用示例
const MyComponent = () => {
const { startRender, endRender } = usePerformanceMonitoring('MyComponent');
const [data, setData] = useState(null);
useEffect(() => {
startRender();
});
useEffect(() => {
endRender();
});
return (
<div>
{/* 组件内容 */}
</div>
);
};
5. 用户体验性能监控
// 用户体验性能监控
class UserExperienceMonitor {
constructor() {
this.monitorCLS();
this.monitorFID();
this.monitorLCP();
this.monitorFCP();
}
// 监控累积布局偏移 (CLS)
monitorCLS() {
let clsValue = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
if (clsValue > 0.1) { // CLS 阈值
Sentry.withScope((scope) => {
scope.setTag('performance', 'cls');
scope.setContext('cls', {
value: clsValue,
threshold: 0.1,
});
Sentry.captureMessage(`Poor CLS: ${clsValue}`, 'warning');
});
}
});
observer.observe({ entryTypes: ['layout-shift'] });
}
// 监控首次输入延迟 (FID)
monitorFID() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const fid = entry.processingStart - entry.startTime;
if (fid > 100) { // FID 阈值
Sentry.withScope((scope) => {
scope.setTag('performance', 'fid');
scope.setContext('fid', {
value: fid,
threshold: 100,
});
Sentry.captureMessage(`Poor FID: ${fid}ms`, 'warning');
});
}
}
});
observer.observe({ entryTypes: ['first-input'] });
}
// 监控最大内容绘制 (LCP)
monitorLCP() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.startTime > 2500) { // LCP 阈值
Sentry.withScope((scope) => {
scope.setTag('performance', 'lcp');
scope.setContext('lcp', {
value: entry.startTime,
threshold: 2500,
element: entry.element?.tagName,
});
Sentry.captureMessage(`Poor LCP: ${entry.startTime}ms`, 'warning');
});
}
}
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
}
// 监控首次内容绘制 (FCP)
monitorFCP() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.startTime > 1800) { // FCP 阈值
Sentry.withScope((scope) => {
scope.setTag('performance', 'fcp');
scope.setContext('fcp', {
value: entry.startTime,
threshold: 1800,
});
Sentry.captureMessage(`Poor FCP: ${entry.startTime}ms`, 'warning');
});
}
}
});
observer.observe({ entryTypes: ['paint'] });
}
}
// 初始化用户体验监控
const uxMonitor = new UserExperienceMonitor();
性能监控与自定义性能指标
启用自动性能监控
// 自动性能监控配置
Sentry.init({
dsn: 'YOUR_DSN_HERE',
integrations: [
new Sentry.Integrations.BrowserTracing({
// 自动追踪所有路由变化
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes
),
// 自动追踪 XHR 和 Fetch 请求
tracingOrigins: [
'localhost',
'your-api-domain.com',
'cdn.example.com',
/^https:\/\/api\./,
],
// 自动追踪用户交互
idleTimeout: 5000,
finalTimeout: 30000,
heartbeatInterval: 5000,
// 自定义路由名称
routeLabel: 'path',
// 过滤不需要的请求
beforeNavigate: (context) => {
// 过滤管理页面
if (context.location.pathname.startsWith('/admin/')) {
return false;
}
return context;
},
}),
// 启用用户会话重放
new Sentry.Replay({
maskAllText: true,
blockAllMedia: true,
sampleRate: 0.1, // 正常会话采样率
errorSampleRate: 1.0, // 错误会话采样率
}),
],
// 性能采样配置
tracesSampleRate: 0.1,
profilesSampleRate: 0.1,
// 过滤事务
beforeSendTransaction: (event) => {
// 过滤过短的事务
if (event.start_timestamp && event.timestamp) {
const duration = event.timestamp - event.start_timestamp;
if (duration < 0.1) {
return null;
}
}
// 过滤特定的事务
if (event.transaction === 'GET /health') {
return null;
}
return event;
},
});
自定义性能事务
// 自定义性能事务管理器
class CustomTransactionManager {
// 创建业务流程事务
static createBusinessTransaction(name, operation, data = {}) {
const transaction = Sentry.startTransaction({
name,
op: operation,
data,
tags: {
transactionType: 'business',
feature: data.feature || 'unknown',
},
});
// 设置上下文
Sentry.getCurrentHub().configureScope((scope) => {
scope.setSpan(transaction);
});
return transaction;
}
// 创建用户交互事务
static createUserInteractionTransaction(action, element, metadata = {}) {
const transaction = Sentry.startTransaction({
name: `User ${action}`,
op: 'ui.action',
data: {
action,
element,
...metadata,
},
tags: {
transactionType: 'interaction',
userAction: action,
},
});
return transaction;
}
// 创建数据处理事务
static createDataProcessingTransaction(processName, dataSize, metadata = {}) {
const transaction = Sentry.startTransaction({
name: `Process ${processName}`,
op: 'data.processing',
data: {
processName,
dataSize,
...metadata,
},
tags: {
transactionType: 'processing',
processType: processName,
},
});
return transaction;
}
}
// 使用示例——业务流程监控
const handleUserRegistration = async (userData) => {
const transaction = CustomTransactionManager.createBusinessTransaction(
'User Registration',
'business.registration',
{
feature: 'authentication',
userType: userData.type,
}
);
try {
// 步骤 1:验证用户输入
const validationSpan = transaction.startChild({
op: 'validation',
description: 'Validate user input',
});
await validateUserInput(userData);
validationSpan.setStatus('ok');
validationSpan.finish();
// 步骤 2:检查用户是否存在
const checkSpan = transaction.startChild({
op: 'db.query',
description: 'Check user existence',
});
const existingUser = await checkUserExists(userData.email);
checkSpan.setData('userExists', !!existingUser);
checkSpan.finish();
if (existingUser) {
throw new Error('User already exists');
}
// 步骤 3:创建用户
const createSpan = transaction.startChild({
op: 'db.insert',
description: 'Create user account',
});
const newUser = await createUser(userData);
createSpan.setData('userId', newUser.id);
createSpan.finish();
// 步骤 4:发送欢迎邮件
const emailSpan = transaction.startChild({
op: 'email.send',
description: 'Send welcome email',
});
await sendWelcomeEmail(newUser.email);
emailSpan.finish();
transaction.setStatus('ok');
transaction.setData('registrationSuccess', true);
return newUser;
} catch (error) {
transaction.setStatus('internal_error');
transaction.setData('registrationSuccess', false);
Sentry.captureException(error);
throw error;
} finally {
transaction.finish();
}
};
自定义性能指标(Span)
// 自定义 Span 管理器
class SpanManager {
// 数据库操作 Span
static createDatabaseSpan(operation, table, query = '') {
const currentTransaction = Sentry.getCurrentHub().getScope()?.getTransaction();
if (!currentTransaction) {
console.warn('No active transaction found for database span');
return null;
}
const span = currentTransaction.startChild({
op: `db.${operation}`,
description: `${operation.toUpperCase()} ${table}`,
data: {
'db.operation': operation,
'db.table': table,
'db.query': query.substring(0, 200), // 截取前200字符
},
});
return span;
}
// 缓存操作 Span
static createCacheSpan(operation, key, ttl = null) {
const currentTransaction = Sentry.getCurrentHub().getScope()?.getTransaction();
if (!currentTransaction) {
return null;
}
const span = currentTransaction.startChild({
op: `cache.${operation}`,
description: `Cache ${operation}: ${key}`,
data: {
'cache.operation': operation,
'cache.key': key,
'cache.ttl': ttl,
},
});
return span;
}
// 算法处理 Span
static createAlgorithmSpan(algorithmName, inputSize, complexity = null) {
const currentTransaction = Sentry.getCurrentHub().getScope()?.getTransaction();
if (!currentTransaction) {
return null;
}
const span = currentTransaction.startChild({
op: 'algorithm.process',
description: `Algorithm: ${algorithmName}`,
data: {
'algorithm.name': algorithmName,
'algorithm.input_size': inputSize,
'algorithm.complexity': complexity,
},
});
return span;
}
}
// 使用示例——数据库操作监控
const getUserData = async (userId) => {
const span = SpanManager.createDatabaseSpan(
'select',
'users',
`SELECT * FROM users WHERE id = ${userId}`
);
try {
const startTime = performance.now();
const userData = await database.query('SELECT * FROM users WHERE id = ?', [userId]);
const duration = performance.now() - startTime;
span?.setData('db.rows_affected', userData.length);
span?.setData('db.duration', duration);
span?.setStatus('ok');
return userData;
} catch (error) {
span?.setStatus('internal_error');
span?.setData('db.error', error.message);
throw error;
} finally {
span?.finish();
}
};
预警设置
设置错误预警
设置性能预警
配置自定义预警(高级)
性能监控流程图
graph TD
A[Sentry 初始化] --> B[启用性能监控]
B --> C[自动事务创建]
B --> D[手动事务创建]
C --> E[路由变化事务]
C --> F[API 请求事务]
C --> G[用户交互事务]
D --> H[业务流程事务]
D --> I[数据处理事务]
D --> J[自定义操作事务]
E --> K[Span 创建]
F --> K
G --> K
H --> K
I --> K
J --> K
K --> L[性能数据收集]
L --> M[数据聚合分析]
M --> N[阈值检查]
N --> O[预警触发]
O --> P[错误预警]
O --> Q[性能预警]
O --> R[业务规则预警]
P --> S[预警增强]
Q --> S
R --> S
S --> T[抑制规则检查]
S --> U[升级规则检查]
T --> V[发送通知]
U --> W[升级通知]
V --> X[邮件/Slack/Webhook]
W --> Y[PagerDuty/SMS/电话]
style A fill:#e1f5fe
style B fill:#f3e5f5
style L fill:#e8f5e8
style O fill:#fff3e0
style S fill:#fce4ec
style X fill:#e0f2f1
style Y fill:#ffebee
错误处理流程图
graph TD
A[应用错误发生] --> B{Sentry 错误捕获}
B --> C[自动捕获]
B --> D[手动捕获]
C --> E[全局错误处理]
C --> F[未处理 Promise 拒绝]
C --> G[React 错误边界]
D --> H[业务逻辑错误]
D --> I[用户操作错误]
D --> J[数据处理错误]
E --> K[错误上下文收集]
F --> K
G --> K
H --> K
I --> K
J --> K
K --> L[Breadcrumbs 添加]
L --> M[用户信息记录]
M --> N[标签和元数据]
N --> O[错误过滤处理]
O --> P[发送到 Sentry]
P --> Q[错误频率监控]
Q --> R[阈值检查]
R --> S[预警触发]
S --> T[通知发送]
T --> U[开发团队处理]
style A fill:#ffebee
style B fill:#f3e5f5
style K fill:#e8f5e8
style P fill:#e1f5fe
style S fill:#fff3e0
style U fill:#e0f2f1
最佳实践总结
1. 配置最佳实践
// 生产环境最佳实践配置
const productionSentryConfig = {
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.APP_VERSION,
// 性能采样率优化
tracesSampleRate: 0.1, // 10% 采样
profilesSampleRate: 0.05, // 5% 性能分析
// 错误过滤
beforeSend: (event, hint) => {
// 过滤敏感信息
if (event.user) {
delete event.user.email;
delete event.user.ip_address;
}
// 过滤低价值错误
if (event.exception) {
const error = hint.originalException;
if (error && error.message) {
const message = error.message.toLowerCase();
if (message.includes('network error') ||
message.includes('script error')) {
return null;
}
}
}
return event;
},
// 事务过滤
beforeSendTransaction: (event) => {
// 过滤过短的事务
if (event.start_timestamp && event.timestamp) {
const duration = event.timestamp - event.start_timestamp;
if (duration < 0.1) {
return null;
}
}
return event;
},
// 集成配置
integrations: [
new Sentry.Integrations.BrowserTracing({
tracingOrigins: ['localhost', 'your-api-domain.com'],
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
useEffect, useLocation, useNavigationType,
createRoutesFromChildren, matchRoutes
),
}),
new Sentry.Replay({
maskAllText: true,
blockAllMedia: true,
sampleRate: 0.1,
errorSampleRate: 1.0,
}),
],
};
2. 监控最佳实践
- 分层监控:对不同类型的问题设置不同的监控策略
- 智能阈值:根据历史数据动态调整阈值
- 上下文丰富:为错误和性能问题提供充分的上下文信息
- 快速响应:设置合理的预警触发和通知机制
3. 团队协作最佳实践
- 角色分工:明确各个角色对不同类型问题的责任
- 响应流程:建立清晰的问题响应和解决流程
- 持续改进:定期分析监控数据,优化监控策略
- 知识分享:将常见问题和解决方案文档化
4. 性能优化建议
- 采样率优化:根据实际需要调整采样率,平衡数据完整性和性能开销
- 数据分类:对不同类型的数据设置不同的保存策略
- 预警频率:避免过于频繁的预警,防止预警疑存
- 自动化处理:对于常见问题实现自动化处理和修复
总结
通过以上全面的 Sentry 配置和使用指南,您可以建立一个完整的应用监控体系,实现错误的快速发现和处理,以及性能的持续优化。记住,监控系统的价值不仅仅在于发现问题,更在于提供可执行的洞察和持续改进的机会。