如何检测JavaScript应用中Sentry上报请求是否被拦截?
环境背景
我们的应用基于Angular/TypeScript开发,使用Sentry.io做错误上报。当应用抛出异常时,全局错误处理器会触发,弹出带文本框的自定义弹窗,用户提交消息后通过Sentry.captureMessage(…)将消息上报至Sentry。
现存问题
近期发现,当Sentry被广告拦截器等原因阻断无法访问时,我们收不到用户提交的消息。
需求目标
希望实现以下两种方案之一:
- 提前检测Sentry可达性,若存在访问问题,直接展示联系支持的指引,不提供Sentry提交入口;
- 保留Sentry提交入口,但在提交请求失败时,引导用户联系支持。
已尝试的非最优方案
我们检查过Sentry.captureMessage(…)的返回值,无论请求成功与否都会返回eventId,try/catch也捕获不到请求失败的异常;后续调用的Sentry.flush(…)返回Promise<boolean>,仅表示事件已被发送,不代表请求成功。
目前通过调用Sentry非公开内部API实现检测:
public sentryIsBlocked(): boolean | null { const client = Sentry.getCurrentHub().getClient(); if (!client) return null; const numberOfNetworkSessionErrors = (client as any)['_outcomes']['network_error:session']; return !!numberOfNetworkSessionErrors && numberOfNetworkSessionErrors > 0; }
该方法存在弊端:
- 依赖非公开API,版本更新后可能失效;
- 仅能检测历史请求问题,无法确认当前请求状态。
可靠解决方案
方案1:包装Sentry传输器实时捕获请求失败
利用Sentry公开的transport配置项,包装默认传输器的发送逻辑,直接捕获请求失败的异常:
import * as Sentry from '@sentry/angular'; Sentry.init({ dsn: '你的DSN地址', transport: (options) => { // 创建默认的Fetch传输实例 const defaultTransport = Sentry.makeFetchTransport(options); const originalSend = defaultTransport.send.bind(defaultTransport); // 重写send方法,添加错误捕获 defaultTransport.send = async (event) => { try { const result = await originalSend(event); // 请求成功,可记录状态 return result; } catch (error) { // 捕获到请求失败,这里触发你的引导逻辑 console.error('Sentry上报请求失败:', error); // 比如设置全局状态,后续弹窗切换为联系支持入口 window.sentryUnreachable = true; throw error; // 保留Sentry的重试逻辑(若不需要可移除) } }; return defaultTransport; }, // 其他初始化配置... });
优势:
- 基于Sentry公开API,版本兼容性有保障;
- 实时捕获每一次上报请求的状态,准确反映当前Sentry可达性。
方案2:预检测Sentry可达性(提前判断)
如果需要在用户打开弹窗前就判断Sentry是否可用,可发起HEAD请求验证:
async function checkSentryReachable(): Promise<boolean> { const client = Sentry.getCurrentHub().getClient(); const dsn = client?.getDsn(); if (!dsn) return false; // 构造Sentry上报端点URL const endpoint = `${dsn.protocol}://${dsn.publicKey}@${dsn.host}/${dsn.projectId}/envelope/`; try { // 使用no-cors模式避免跨域限制,只要请求不抛出异常就说明未被拦截 await fetch(endpoint, { method: 'HEAD', mode: 'no-cors' }); return true; } catch (error) { // 请求被拦截或失败,标记Sentry不可用 return false; } } // 弹窗打开前调用 if (await checkSentryReachable()) { // 展示Sentry提交入口 } else { // 展示联系支持指引 }
注意:no-cors模式下无法获取真实响应状态,但能判断请求是否被广告拦截器阻断。
方案3:结合Outcomes API(需后端配合)
Sentry的Outcomes API是公开的,可用来追踪事件投递状态。前端可以记录上报的eventId,由后端定时查询Outcomes API判断是否投递成功:
Sentry.init({ dsn: '你的DSN地址', beforeSend: (event) => { // 记录当前上报的eventId window.lastReportedEventId = event.event_id; return event; }, });
后端通过Outcomes API查询该eventId的状态后,再通知前端处理用户引导逻辑。此方案适合需要持久化追踪上报结果的场景。
总结
优先选择方案1,它无需额外请求,基于Sentry公开API实现实时错误捕获,既满足实时判断需求,又避免了依赖非公开API的风险。
内容的提问来源于stack exchange,提问作者DerZyklop

