性能监控与优化之Sentry使用

264 阅读20分钟

性能监控与优化之Sentry使用

创建 Sentry 项目

1. 注册 Sentry 账户

首先需要在 Sentry 官网 创建账户,可以选择:

  • 免费版本:适合个人项目和小团队,每月有 5,000 个错误事件限制
  • 付费版本:适合企业级应用,提供更多功能和更高的事件限制

2. 创建新项目

登录 Sentry 后,按照以下步骤创建项目:

  1. 点击右上角的 "Create Project" 按钮
  2. 选择平台类型(React、Vue、JavaScript 等)
  3. 配置项目设置:
    • 项目名称:为项目选择一个描述性的名称
    • 团队:选择项目所属的团队
    • 警报规则:设置默认的错误警报规则

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 配置和使用指南,您可以建立一个完整的应用监控体系,实现错误的快速发现和处理,以及性能的持续优化。记住,监控系统的价值不仅仅在于发现问题,更在于提供可执行的洞察和持续改进的机会。