1. Sentry 概述与核心价值
Sentry 作为现代应用开发中不可或缺的错误监控工具,已经成为众多开发团队的首选方案。我在多个生产级 Spring Boot 项目中深度使用 Sentry 后,发现它能将错误排查时间从小时级缩短到分钟级。不同于传统的日志系统,Sentry 提供了完整的错误上下文和智能分组功能,让开发者能够快速定位问题根源。
1.1 Sentry 的核心工作机制
Sentry 的工作原理可以类比为医院的急诊分诊系统。当应用抛出异常时,Sentry SDK 会像训练有素的护士一样,立即捕获异常并记录所有关键信息:
- 异常快照:完整记录错误堆栈轨迹,精确到代码行号
- 环境指纹:包括操作系统、浏览器版本、设备信息等
- 用户会话:当前登录用户信息(需配置)
- 面包屑轨迹:错误发生前的关键操作记录
- 性能数据:请求响应时间、数据库查询耗时等
这些信息会被打包成一个"事件",通过 HTTPS 协议发送到 Sentry 服务器。服务端接收到事件后,会进行智能分析:
java复制// 典型的事件数据结构
{
"timestamp": "2023-08-20T14:32:45",
"level": "error",
"exception": {
"type": "NullPointerException",
"value": "Cannot invoke method on null object",
"stacktrace": [...]
},
"tags": {"environment": "production"},
"user": {"id": "user123", "email": "user@example.com"},
"breadcrumbs": [...],
"contexts": {
"runtime": {"name": "Java", "version": "11.0.12"},
"os": {"name": "Linux", "version": "5.4.0"}
}
}
1.2 为什么选择 Sentry 而非传统方案
在采用 Sentry 前,我们团队尝试过多种错误监控方案,包括 ELK 日志系统、自定义错误处理器等。下表对比了各种方案的优劣:
| 方案类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 日志文件 | 简单易用,记录全面 | 难以实时告警,缺乏上下文 | 历史数据归档 |
| ELK 系统 | 强大的搜索分析能力 | 配置复杂,资源消耗大 | 日志集中管理 |
| 自定义处理器 | 完全可控,定制性强 | 开发维护成本高 | 特殊业务需求 |
| Sentry | 开箱即用,实时告警,丰富上下文 | 商业版费用较高 | 现代应用监控 |
Sentry 的独特价值在于:
- 错误智能聚合:自动将相似错误归类,避免告警风暴
- 发布跟踪:精确关联错误与代码版本,快速定位问题提交
- 性能基线:建立性能指标基准,自动发现异常波动
- 用户反馈:收集终端用户的问题描述,重现疑难杂症
提示:对于中小团队,Sentry 的免费版(每月5000个事件)已经能满足基本需求。当业务增长到一定规模后,可以考虑自建 Sentry 服务或购买企业版。
2. Spring Boot 集成实战
2.1 环境准备与依赖配置
在 Spring Boot 2.3+ 项目中集成 Sentry 只需三步:
- 添加 Maven 依赖:
xml复制<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring-boot-starter</artifactId>
<version>6.4.1</version>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-logback</artifactId>
<version>6.4.1</version>
</dependency>
- 配置 application.yml:
yaml复制sentry:
dsn: https://your-public-key@your-host/your-project-id
environment: ${SPRING_PROFILES_ACTIVE:development}
release: my-app@${APP_VERSION:1.0.0}
enable-tracing: true
traces-sample-rate: 0.2
- 验证连接:
java复制@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
Sentry.init(options -> {
options.setDsn("https://your-public-key@your-host/your-project-id");
options.setEnableTracing(true);
});
SpringApplication.run(MyApp.class, args);
}
}
2.2 核心配置详解
Sentry 的 Spring Boot Starter 提供了丰富的配置选项,以下是最关键的几个:
-
DSN (Data Source Name)
格式为:https://{public_key}@{host}/{project_id}
这是项目在 Sentry 中的唯一标识,可以在项目设置中找到。 -
环境区分
通过sentry.environment配置(通常使用 Spring Profile),可以在 Sentry 中按环境过滤问题。 -
版本跟踪
sentry.release应该设置为当前部署的代码版本,便于:- 定位引入问题的提交
- 统计版本稳定性
- 过滤已修复问题
-
采样率配置
traces-sample-rate: 性能监控数据采样率 (0.0-1.0)profiles-sample-rate: CPU 性能分析采样率
注意:生产环境建议设置较低的采样率(如0.1),而开发环境可以设为1.0以便完整捕获问题。
2.3 自动捕获的异常类型
Sentry Spring Boot Starter 会自动捕获以下异常:
- 所有未处理的控制器异常(@ControllerAdvice 范围外)
- Spring MVC 拦截器抛出的异常
- 定时任务(@Scheduled)中的未捕获异常
- 异步方法(@Async)中的异常
- Spring Security 认证/授权异常
对于需要特殊处理的异常,可以自定义异常处理器:
java复制@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleBusinessException(BusinessException ex) {
// 添加自定义上下文后发送到 Sentry
Sentry.withScope(scope -> {
scope.setTag("business_error_code", ex.getCode());
scope.setContext("request_context", Map.of(
"userId", SecurityContext.getCurrentUserId(),
"operation", ex.getOperationType()
));
Sentry.captureException(ex);
});
}
}
3. 高级功能实现
3.1 性能监控与分布式追踪
Sentry 的 APM 功能可以帮助发现性能瓶颈。集成方式:
- 启用 Web 请求自动追踪:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SentryTracingInterceptor());
}
}
- 手动创建业务事务:
java复制@SentrySpan
public Order processOrder(OrderRequest request) {
ITransaction transaction = Sentry.getCurrentTransaction();
try {
// 扣减库存
Span inventorySpan = transaction.startChild("inventory");
inventoryService.reduceStock(request.getItems());
inventorySpan.finish();
// 创建订单
Span orderSpan = transaction.startChild("order");
Order order = orderRepository.save(createOrder(request));
orderSpan.finish();
transaction.setStatus(SpanStatus.OK);
return order;
} catch (Exception e) {
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
throw e;
}
}
- 分布式追踪集成(以 Feign 为例):
java复制@Bean
public Feign.Builder feignBuilder() {
return Feign.builder()
.requestInterceptor(template -> {
// 传播 Sentry 追踪头
ITransaction transaction = Sentry.getCurrentTransaction();
if (transaction != null) {
template.header("sentry-trace", transaction.toSentryTrace());
}
});
}
3.2 用户反馈收集
在错误页面嵌入反馈组件:
html复制<div class="error-feedback">
<h3>遇到问题?</h3>
<form id="feedback-form">
<input type="hidden" id="sentry-event-id" th:value="${eventId}">
<textarea placeholder="请描述您遇到的问题..."></textarea>
<button type="submit">提交反馈</button>
</form>
</div>
<script>
document.getElementById('feedback-form').addEventListener('submit', function(e) {
e.preventDefault();
const eventId = document.getElementById('sentry-event-id').value;
const comments = e.target.querySelector('textarea').value;
fetch('/api/feedback', {
method: 'POST',
body: JSON.stringify({eventId, comments}),
headers: {'Content-Type': 'application/json'}
}).then(/* 处理响应 */);
});
</script>
后端处理:
java复制@RestController
@RequestMapping("/api/feedback")
public class FeedbackController {
@PostMapping
public ResponseEntity<?> submitFeedback(@RequestBody FeedbackRequest request) {
UserFeedback feedback = new UserFeedback(request.getEventId());
feedback.setComments(request.getComments());
Sentry.captureUserFeedback(feedback);
return ResponseEntity.ok().build();
}
}
4. 生产环境最佳实践
4.1 安全与隐私保护
处理敏感数据的推荐方案:
- 全局数据擦除配置:
java复制@Bean
public SentryOptionsConfiguration securityConfig() {
return options -> {
options.setBeforeSend((event, hint) -> {
// 移除敏感头信息
if (event.getRequest() != null) {
event.getRequest().getHeaders().remove("Authorization");
}
// 脱敏用户信息
if (event.getUser() != null) {
event.getUser().setEmail(maskEmail(event.getUser().getEmail()));
}
return event;
});
};
}
- 自定义上下文处理器:
java复制@Component
public class SecureContextProcessor implements SentryEventProcessor {
@Override
public SentryEvent process(SentryEvent event, Hint hint) {
// 过滤信用卡号等敏感信息
if (event.getMessage() != null) {
event.setMessage(
event.getMessage().replaceAll(
"\\b[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}\\b",
"[CREDIT_CARD]"
)
);
}
return event;
}
}
4.2 告警与通知配置
在 Sentry 后台设置智能告警规则:
-
基于错误频率的告警:
- 同一错误在5分钟内出现10次
- 新错误首次出现
-
基于影响的告警:
- 影响超过50%的活跃用户
- 关键事务失败率上升
-
集成通知渠道:
- Slack/Teams 即时消息
- 邮件通知
- Webhook 到内部系统
示例告警条件配置:
code复制When an issue is seen more than 10 times in 1 hour
AND the issue is marked as high priority
THEN trigger a P1 alert
4.3 性能优化技巧
-
SDK 调优:
- 设置合理的采样率(生产环境0.1-0.2)
- 启用压缩减少网络开销
- 使用异步传输模式
-
服务端配置:
yaml复制sentry: async: true queue: max-size: 100 shutdown-timeout: 5000 transport: max-queue-size: 50 timeout: 3000 -
异常过滤:
java复制@Bean public SentryOptionsConfiguration filterConfig() { return options -> { options.setBeforeSend((event, hint) -> { // 忽略特定的业务异常 if (event.getThrowable() instanceof IgnorableException) { return null; } return event; }); }; }
5. 疑难问题排查
5.1 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 事件未发送 | 网络连接问题 | 检查代理设置,验证 DSN |
| 缺少堆栈信息 | 混淆/优化导致 | 配置 ProGuard/R8 保留规则 |
| 性能数据缺失 | 采样率过低 | 调整 traces-sample-rate |
| 用户信息为空 | 未设置用户上下文 | 调用 Sentry.setUser() |
| 重复事件过多 | 错误分组不当 | 自定义指纹生成规则 |
5.2 调试技巧
-
启用 SDK 调试日志:
yaml复制logging: level: io.sentry: DEBUG -
验证事件生成:
java复制// 手动触发测试事件 Sentry.captureMessage("This is a test message", SentryLevel.INFO); -
检查网络请求:
bash复制curl -v https://sentry.io/api/your-project-id/store/ \ -H "Content-Type: application/json" \ -H "X-Sentry-Auth: Sentry sentry_version=7..." \ -d '{"message":"test"}' -
使用 Sentry CLI 工具:
bash复制# 安装 curl -sL https://sentry.io/get-cli/ | bash # 验证配置 sentry-cli info
6. 微服务架构下的实践
6.1 分布式追踪实现
在微服务环境中,需要传播追踪上下文:
- 创建全局过滤器:
java复制@Component
public class TracingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 从请求头中提取追踪上下文
String sentryTrace = httpRequest.getHeader("sentry-trace");
if (sentryTrace != null) {
Sentry.continueTrace(sentryTrace);
}
chain.doFilter(request, response);
}
}
- 配置 HTTP 客户端:
java复制@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(
(request, body, execution) -> {
// 传播追踪头
ITransaction transaction = Sentry.getCurrentTransaction();
if (transaction != null) {
request.getHeaders().add("sentry-trace", transaction.toSentryTrace());
}
return execution.execute(request, body);
}
));
return restTemplate;
}
6.2 跨服务错误关联
使用 Trace ID 关联日志和错误:
java复制@Configuration
public class LoggingConfig {
@Bean
public CorrelationIdFilter correlationIdFilter() {
return new CorrelationIdFilter();
}
}
public class CorrelationIdFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
String correlationId = UUID.randomUUID().toString();
MDC.put("correlationId", correlationId);
// 设置到 Sentry 上下文
Sentry.configureScope(scope -> {
scope.setTag("correlation_id", correlationId);
});
try {
chain.doFilter(request, response);
} finally {
MDC.remove("correlationId");
}
}
}
在日志配置中显示 Trace ID:
xml复制<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} [%X{correlationId}] - %msg%n</pattern>
7. 监控策略与团队协作
7.1 错误分类与处理流程
建立团队错误处理 SOP:
-
严重等级定义:
- P0:系统不可用,影响所有用户
- P1:核心功能不可用
- P2:次要功能问题
- P3:轻微问题,不影响使用
-
分配与跟进:
java复制// 通过标签标记负责人 Sentry.withScope(scope -> { scope.setTag("owner", "backend-team"); scope.setTag("sla", "p1-4h"); Sentry.captureException(ex); }); -
解决状态流转:
- New → Investigating → Resolved → Verified
7.2 数据驱动优化
利用 Sentry 数据进行质量改进:
-
版本稳定性看板:
sql复制SELECT release, count(*) as error_count FROM events GROUP BY release ORDER BY error_count DESC -
错误趋势分析:
sql复制SELECT date_trunc('hour', timestamp) as hour, count(*) as errors FROM events WHERE environment='production' GROUP BY hour ORDER BY hour -
影响用户统计:
sql复制SELECT user.email, count(*) as errors FROM events WHERE user.email IS NOT NULL GROUP BY user.email ORDER BY errors DESC LIMIT 10
8. 扩展与定制开发
8.1 自定义集成示例
集成消息队列监控:
java复制@Component
public class KafkaSentryIntegration {
@KafkaListener(topics = "orders")
public void listen(OrderMessage message) {
ITransaction transaction = Sentry.startTransaction("process.order", "kafka");
try {
// 处理消息
orderService.process(message);
transaction.setStatus(SpanStatus.OK);
} catch (Exception e) {
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
Sentry.captureException(e);
throw e;
} finally {
transaction.finish();
}
}
}
8.2 插件开发指南
创建自定义 Sentry 插件:
java复制public class CustomIntegration implements Integration {
@Override
public void register(IHub hub, SentryOptions options) {
hub.addEventProcessor(new CustomEventProcessor());
}
}
public class CustomEventProcessor implements EventProcessor {
@Override
public SentryEvent process(SentryEvent event, Hint hint) {
// 添加业务特定上下文
event.getContexts().put("business", Map.of(
"region", System.getenv("AWS_REGION"),
"deployment", System.getenv("DEPLOYMENT_ID")
));
return event;
}
}
注册插件:
java复制Sentry.init(options -> {
options.addIntegration(new CustomIntegration());
});
9. 升级与迁移策略
9.1 版本升级指南
从旧版迁移到 Sentry 6.x 的步骤:
-
依赖变更:
xml复制<!-- 移除旧版 --> <dependency> <groupId>io.sentry</groupId> <artifactId>sentry-spring</artifactId> <version>1.7.30</version> </dependency> <!-- 添加新版 --> <dependency> <groupId>io.sentry</groupId> <artifactId>sentry-spring-boot-starter</artifactId> <version>6.4.1</version> </dependency> -
配置迁移:
properties复制# 旧版 sentry.dsn=https://... sentry.stacktrace.app.packages=com.myapp # 新版 sentry.dsn=https://... sentry.stacktrace.app-packages=com.myapp -
API 变更适配:
java复制// 旧版 Sentry.getContext().addExtra("key", "value"); // 新版 Sentry.configureScope(scope -> { scope.setExtra("key", "value"); });
9.2 数据迁移方案
当需要切换 Sentry 项目或实例时:
-
使用 Sentry CLI 导出数据:
bash复制sentry-cli export global -
配置双写策略过渡期:
java复制@Bean public SentryOptionsConfiguration dualWriteConfig() { return options -> { options.setBeforeSend((event, hint) -> { // 旧实例 sendToLegacySentry(event); return event; }); }; } -
灰度切换方案:
yaml复制sentry: dsn: ${NEW_DSN} fallback-dsn: ${OLD_DSN} traffic-ratio: 0.5 # 50%流量到新实例
10. 成本优化与资源管理
10.1 事件配额控制
避免超出免费额度的策略:
-
采样率动态调整:
java复制@Bean public SentryOptionsConfiguration samplingConfig() { return options -> { options.setTracesSampler(context -> { // 重要事务全量采集 if (context.getTransactionContext().getName().contains("checkout")) { return 1.0; } // 其他事务采样 return 0.1; }); }; } -
错误优先级过滤:
java复制options.setBeforeSend((event, hint) -> { // 只发送ERROR级别以上的事件 if (event.getLevel() == SentryLevel.ERROR) { return event; } return null; }); -
按类型配额分配:
java复制private final Map<String, AtomicInteger> errorQuotas = new ConcurrentHashMap<>(); options.setBeforeSend((event, hint) -> { String errorType = event.getThrowable().getClass().getSimpleName(); if (errorQuotas.computeIfAbsent(errorType, k -> new AtomicInteger(0)).incrementAndGet() > 100) { return null; // 每种错误类型每天最多100条 } return event; });
10.2 存储优化建议
-
附件管理:
- 限制单个事件附件大小(默认20MB)
- 禁用不必要的上下文数据
-
数据保留策略:
yaml复制sentry: events: max-retention-days: 30 attachments: max-retention-days: 7 -
本地缓存配置:
java复制options.setCacheDirPath("/var/cache/sentry"); options.setMaxCacheItems(1000);
11. 安全加固方案
11.1 访问控制策略
-
DSN 分级管理:
- 使用不同 DSN 对应不同环境
- 生产环境 DSN 设置 IP 白名单
-
敏感数据脱敏:
java复制options.setBeforeSend((event, hint) -> { // 移除敏感头信息 event.getRequest().getHeaders().remove("Authorization"); // 脱敏身份证号 if (event.getMessage() != null) { event.setMessage(event.getMessage().replaceAll( "[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]", "[ID_CARD]" )); } return event; }); -
传输加密:
yaml复制sentry: transport: enable-compression: true max-queue-size: 50 timeout: 5000
11.2 合规性配置
满足 GDPR 等法规要求:
-
用户数据控制:
java复制// 明确用户授权后才收集 if (user.hasConsentedToTracking()) { Sentry.setUser(UserProxy.create() .setId(user.getId()) .setEmail(user.getEmail())); } -
数据清理 API:
java复制@RestController @RequestMapping("/api/privacy") public class PrivacyController { @DeleteMapping("/user/{userId}") public ResponseEntity<?> forgetUser(@PathVariable String userId) { Sentry.getApi().deleteUserData(userId); return ResponseEntity.ok().build(); } } -
审计日志集成:
java复制@Aspect @Component public class SentryAuditAspect { @AfterReturning("execution(* com.myapp..*(..)) && @annotation(audited)") public void auditSuccess(JoinPoint jp, Audited audited) { Sentry.addBreadcrumb(Breadcrumb.info( "Audit", "Operation completed: " + audited.value() )); } @AfterThrowing(pointcut = "execution(* com.myapp..*(..)) && @annotation(audited)", throwing = "ex") public void auditFailure(JoinPoint jp, Audited audited, Exception ex) { Sentry.captureMessage( "Security audit failed: " + audited.value(), SentryLevel.WARNING ); } }
12. 监控体系集成
12.1 与 Prometheus 集成
通过 Metrics 接口暴露 Sentry 数据:
java复制@Configuration
public class MetricsConfig {
@Bean
public MeterBinder sentryMetrics() {
return registry -> {
Gauge.builder("sentry.events.sent",
() -> Sentry.getMetrics().getEventsSent())
.register(registry);
Gauge.builder("sentry.events.dropped",
() -> Sentry.getMetrics().getEventsDropped())
.register(registry);
};
}
}
12.2 与 Grafana 仪表板集成
-
使用 Sentry API 数据源:
json复制{ "apiUrl": "https://sentry.io/api/0", "authToken": "your-token" } -
创建错误率仪表板:
sql复制SELECT date_trunc('hour', timestamp) as time, count(*) as errors FROM events WHERE $__timeFilter(timestamp) GROUP BY time ORDER BY time -
性能监控视图:
sql复制SELECT transaction, avg(duration) as avg_duration FROM transactions WHERE $__timeFilter(timestamp) GROUP BY transaction
13. 移动端集成方案
13.1 Android 协同监控
统一 Web 和移动端的错误追踪:
-
共享 Release 版本:
gradle复制android { defaultConfig { versionName "1.0.0" buildConfigField "String", "SENTRY_RELEASE", "\"my-app@${versionName}\"" } } -
关联用户会话:
java复制// Android 端 Sentry.setUser(UserProxy.create() .setId(userId) .setEmail(userEmail)); // Web 端 Sentry.setUser({ id: userId, email: userEmail }); -
统一问题看板:
sql复制SELECT event.platform, count(*) as errors FROM events WHERE release='my-app@1.0.0' GROUP BY event.platform
13.2 iOS 端联动
实现跨平台错误关联:
-
配置相同的 Release:
swift复制SentrySDK.start { options in options.dsn = "https://your-key@your-host/your-project-id" options.releaseName = "my-app@1.0.0" } -
传播 Trace 上下文:
swift复制let transaction = SentrySDK.startTransaction(name: "api-call", operation: "http") var request = URLRequest(url: url) request.addValue(transaction.trace.origin.sentryTraceHeader(), forHTTPHeaderField: "sentry-trace") URLSession.shared.dataTask(with: request) { data, response, error in transaction.finish() }.resume()
14. 未来演进方向
14.1 智能错误预测
结合机器学习实现:
-
异常模式识别:
python复制from sklearn.ensemble import IsolationForest clf = IsolationForest() clf.fit(error_features) anomalies = clf.predict(new_errors) -
关联分析:
sql复制SELECT error_type, browser, count(*) as occurrences FROM events GROUP BY CUBE(error_type, browser) -
根因分析:
python复制from sklearn.tree import DecisionTreeClassifier dt = DecisionTreeClassifier() dt.fit(X_train, y_train) important_features = dt.feature_importances_
14.2 自动化修复
-
热修复工作流:
code复制
检测到错误 → 分析模式 → 生成补丁 → 验证 → 部署 -
配置自动化规则:
yaml复制automation-rules: - name: "Retry Failed Transactions" conditions: - "event.type:transaction" - "transaction.status:failed" actions: - type: "retry" delay: "5m" max_attempts: 3 -
与 CI/CD 集成:
yaml复制# GitHub Actions 示例 - name: Check Sentry for new errors uses: getsentry/action-check-new-errors@v1 with: token: ${{ secrets.SENTRY_TOKEN }} organization: my-org project: my-project since: 1h
15. 团队协作最佳实践
15.1 问题分配策略
-
基于服务所有权:
java复制Sentry.withScope(scope -> { scope.setTag("service", "order-service"); scope.setTag("owner", "team-b"); Sentry.captureException(ex); }); -
自动分配规则:
yaml复制assignment-rules: - match: "service:payment-*" owner: "team-a" - match: "transaction:/api/orders*" owner: "team-b" -
升级策略:
code复制2小时未响应 → 通知主管 4小时未解决 → 升级到CTO
15.2 知识库建设
-
错误解决方案模板:
markdown复制## [ErrorType] 解决方案 **问题描述**: **影响范围**: **修复步骤**: 1. 2. **验证方法**: **预防措施**: -
关联文档:
java复制// 在代码中标记已知问题 @KnownIssue(id = "SENTRY-123", solution = "升级到v2.3+修复") public void problematicMethod() { // ... } -
自动化文档生成:
python复制def generate_error_docs(issue_id): issue = sentry.get_issue(issue_id) template = render_template("error_doc.md", issue=issue) save_to_wiki(issue.title + ".md", template)
16. 性能调优实战
16.1 SDK 性能优化
-
异步传输配置:
yaml复制sentry: async: true queue: max-size: 100 shutdown-timeout: 5000 -
本地缓存策略:
java复制options.setCacheDirPath("/var/cache/sentry"); options.setMaxCacheItems(1000); options.setShutdownTimeout(5000); -
批量发送配置:
java复制options.setEnableShutdownHook(true); options.setFlushTimeoutMillis(3000); options.setMaxQueueSize(50);
16.2 网络优化
-
压缩传输:
yaml复制sentry: transport: enable-compression: true -
代理配置:
java复制options.setProxy( new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)) ); -
DNS 缓存:
java复制java.security.Security.setProperty("networkaddress.cache.ttl", "60");
17. 多租户支持方案
17.1 租户隔离实现
-
动态 DSN 选择:
java复制public class TenantAwareSentryClient { private final Map<String, String> tenantDsns; public void captureError(String tenantId, Exception ex) { String dsn = tenantDsns.get(tenantId); Sentry.init(options -> options.setDsn(dsn)); Sentry.captureException(ex); } } -
上下文标记:
java复制Sentry.configureScope(scope -> { scope.setTag("tenant_id", currentTenant.getId()); scope.setContext("tenant", Map.of( "name", currentTenant.getName(), "plan", currentTenant.getPlan() )); }); -
数据隔离查询:
sql复制SELECT * FROM events WHERE tags['tenant_id'] = 'acme-corp'
17.2 配额管理
-
租户级配额:
java复制public class TenantQuotaFilter implements EventProcessor { @Override public SentryEvent process(SentryEvent event, Hint hint) { String tenantId = event.getTag("tenant_id"); if (quotaExceeded(tenantId)) { return null; } return event; } } -
优先级分配:
java复制options.setBeforeSend((event, hint) -> { String tenantId = event.getTag("tenant_id"); if ("premium".equals(getTenantTier(tenantId))) { return event; // 优先处理付费租户 } return Math.random() < 0.5 ? event : null; // 免费租户采样 });
18. 灾难恢复方案
18.1 故障转移策略
-
备用 DSN 配置:
yaml复制sentry: primary-dsn: ${PRIMARY_DSN} fallback-dsn: ${FALLBACK_DSN} -
健康检查:
java复制public class SentryHealthChecker { public boolean check() { try { HttpURLConnection conn = (HttpURLConnection) new URL("https://sentry.io/api/0").openConnection(); return conn.getResponseCode() == 200; } catch (Exception e) { return false; } } } -
自动切换:
java复制@Scheduled(fixedRate = 60000) public void monitorSentry() { if (!healthChecker.check()) { switchToFallback(); } }
18.2 数据备份
- 本地日志归档:
xml复制<appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>/var/log/sentry-fallback.log</file> <encoder>