故障排除
如果您需要帮助解决与 Sentry JavaScript SDK 集成相关的问题,可以阅读以下记录的边缘情况。
SDK 没有发送任何数据
如果您已经配置了 Sentry SDK,但它没有向 Sentry 发送任何数据:
检查您是否已配置 DSN 并将其传递给
Sentry.init()
中的dsn
选项。如果您使用环境变量来传递 DSN,请确保这些环境变量在所有相关环境中都已设置。此外,如果您在框架内使用环境变量,请检查框架是否会将环境变量包含在您的包中。通常这意味着您需要为环境变量添加框架定义的特殊前缀(例如,在 Next.js 中使用
NEXT_PUBLIC_
,在 Nuxt 中使用NUXT_
,在 Vite 项目中使用VITE_
,在 Create React App 中使用REACT_APP_
等)。检查您是否已禁用所有广告拦截器。
在
Sentry.init()
选项中设置debug: true
,并在启动应用程序时观察控制台输出。SDK 可能会告诉您为什么它没有发送任何数据。
升级到新的 Sentry SDK 版本
如果您将 Sentry SDK 升级到一个新的主要版本,可能会遇到需要您进行调整的破坏性更改。 请查阅我们的 迁移指南,以了解所有需要知道的内容,确保您可以顺利使用最新的 Sentry 功能。
最大 JSON 负载大小
maxValueLength
的默认值为 250,但如果您的消息更长,您可以根据需要调整此值。请注意,并不是每个单独的值都会受此选项的影响。
CORS 属性和头部
要获取来自不同源的脚本抛出的 JavaScript 异常的可见性,请执行以下两个步骤:
- 添加
crossorigin="anonymous"
脚本属性
<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>
脚本属性告诉浏览器“匿名”获取目标文件。潜在的用户识别信息(如 Cookie 或 HTTP 凭据)在请求此文件时不会由浏览器传输到服务器。
- 添加跨域 HTTP 头部
Access-Control-Allow-Origin: *
跨域资源共享 (CORS) 是一组 API(主要是 HTTP 头部),用于规定文件应如何在不同源之间下载和提供服务。
通过设置 Access-Control-Allow-Origin: *
,服务器向浏览器表明任何源都可以获取此文件。或者,您可以将其限制为您控制的特定源:
Access-Control-Allow-Origin: https://www.example.com
CDN 通常会正确设置 Access-Control-Allow-Origin
头部。
$ curl --head https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.js | \
grep -i "access-control-allow-origin"
Access-Control-Allow-Origin: *
意外的 OPTIONS 请求
如果您的应用程序开始出现行为异常,表现为执行额外的 OPTIONS 请求,这很可能是因为不必要的 sentry-trace
请求头导致的。这种情况通常发生在您在浏览器 SDK 中使用了过于通用的 Tracing 集成配置。
要解决此问题,请在 SDK 初始化时更改 tracePropagationTargets
选项。更多详情,请参阅我们追踪文档中的 自动 instrumentation。
`instrument.js` 的控制台日志语句行号
如果在调试时 instrument.js
显示在您的控制台中,可以通过将 Sentry 添加到 框架忽略列表 中来解决此问题。添加以下模式:/@sentry/
这样,Chrome 在调试时会忽略 SDK 的堆栈帧。
处理广告拦截器
当您使用我们的 CDN 时,广告拦截或脚本拦截扩展可能会阻止我们的 SDK 正确获取和初始化。因此,任何对 SDK API 的调用都会失败,并可能导致您的应用程序行为异常。
此外,即使 SDK 已正确下载和初始化,Sentry 端点也可能被阻止接收捕获的数据。这将阻止任何错误报告、会话健康数据或性能数据的传输,导致这些数据在 sentry.io 上不可用。
此外,某些浏览器(如 Brave)内置了广告拦截器,可能会阻止发送到我们端点的请求。即使用户从阻止列表中移除了您的域名,Brave 仍然可能继续阻止 来自服务工作者的请求。
您可以通过 使用我们的 NPM 包 并将我们的 SDK 与您的应用程序一起打包来绕过 CDN 包被拦截的问题。然而,端点拦截只能通过以下解释的隧道方法来解决。
使用 `tunnel` 选项
当端点位于同一源下(虽然这不是隧道工作的必要条件),浏览器不会将发送到该端点的任何请求视为第三方请求。因此,这些请求将应用不同的安全措施,默认情况下不会触发广告拦截器。以下是一个简要的流程说明。
从 JavaScript SDK 版本 6.7.0
开始,您可以使用 tunnel
选项告诉 SDK 将事件发送到配置的 URL,而不是使用 DSN。这允许 SDK 从查询参数中移除 sentry_key
,这是广告拦截器阻止发送事件的主要原因之一。此选项还会停止 SDK 发送预检请求,而预检请求是之前需要在查询参数中发送 sentry_key
的要求之一。
要启用 tunnel
选项,请在 Sentry.init
调用中提供相对或绝对 URL。当您使用相对 URL 时,它是相对于当前源的,这是我们推荐的形式。使用相对 URL 不会触发预检 CORS 请求,因此不会有事件被阻止,因为广告拦截器不会将这些事件视为第三方请求。
Sentry.init({
dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
tunnel: "/tunnel",
});
配置完成后,所有事件将被发送到 /tunnel
端点。然而,此解决方案需要在服务器端进行额外的配置,因为事件现在需要被解析并重定向到 Sentry。以下是一个服务器组件的示例:
// This functionality is now built-in to the Sentry.AspNetCore package.
// See https://docs.sentry.io/platforms/dotnet/guides/aspnetcore/#tunnel
// docs for more information.
// This example shows how you could implement it yourself:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
// Change host appropriately if you run your own Sentry instance.
const string host = "sentry.io";
// Set knownProjectIds to a list with your Sentry project IDs which you
// want to accept through this proxy.
var knownProjectIds = new HashSet<string>() { };
var client = new HttpClient();
WebHost.CreateDefaultBuilder(args).Configure(a =>
a.Run(async context =>
{
context.Request.EnableBuffering();
using var reader = new StreamReader(context.Request.Body);
var header = await reader.ReadLineAsync();
var headerJson = JsonSerializer.Deserialize<Dictionary<string, object>>(header);
if (headerJson.TryGetValue("dsn", out var dsnString)
&& Uri.TryCreate(dsnString.ToString(), UriKind.Absolute, out var dsn))
{
var projectId = dsn.AbsolutePath.Trim('/');
if (knownProjectIds.Contains(projectId) && string.Equals(dsn.Host, host, StringComparison.OrdinalIgnoreCase)) {
context.Request.Body.Position = 0;
await client.PostAsync($"https://{dsn.Host}/api/{projectId}/envelope/",
new StreamContent(context.Request.Body));
}
}
})).Build().Run();
如果您的使用场景与 SDK 包本身被阻止有关,以下任何一种解决方案都可以帮助您解决此问题。
应对脚本拦截扩展的最佳方法是通过 npm
直接使用 SDK 包并将其与您的应用程序一起打包。这样可以确保代码始终按预期存在。
第二种方法是从我们的 CDN 下载 SDK 并自行托管。这样,SDK 仍然会与您的其他代码分开,但您可以确保它不会被阻止,因为它的源将与您网站的源相同。
您可以使用 curl
或其他类似工具轻松获取它:
curl https://browser.sentry-cdn.com/5.20.1/bundle.min.js -o sentry.browser.5.20.1.min.js -s
最后一种选项是使用 Proxy
守护,这可以确保即使在调用被阻止的 SDK 时,您的代码也不会中断。Proxy
被所有浏览器支持,除了 Internet Explorer(不过该浏览器中也没有广告拦截扩展)。此外,如果用户的浏览器不支持 Proxy
,它将被静默跳过,因此您不必担心它会破坏任何东西。
请将以下代码片段立即放置在包含我们 CDN 包的 <Expandable>
标签 上方。可读格式的代码片段如下所示:
if ("Proxy" in window) {
var handler = {
get: function (_, key) {
return new Proxy(function (cb) {
if (key === "flush" || key === "close") return Promise.resolve();
if (typeof cb === "function") return cb(window.Sentry);
return window.Sentry;
}, handler);
},
};
window.Sentry = new Proxy({}, handler);
}
如果您想直接复制和粘贴代码片段,以下是压缩后的版本:
<script>
if ("Proxy" in window) {
var n = {
get: function (o, e) {
return new Proxy(function (n) {
return "flush" === e || "close" === e
? Promise.resolve()
: "function" == typeof n
? n(window.Sentry)
: window.Sentry;
}, n);
},
};
window.Sentry = new Proxy({}, n);
}
</script>
第三方 Promise 库
当您包含并配置 Sentry 时,我们的 JavaScript SDK 会自动附加全局处理器以 捕获 未捕获的异常和未处理的 Promise 拒绝。您可以通过将 onunhandledrejection
选项设置为 false
来禁用此默认行为,并手动挂钩每个事件处理器,然后直接调用 Sentry.captureException
或 Sentry.captureMessage
。
如果您使用第三方库来实现 Promise,可能还需要管理您的配置。此外,请记住,浏览器通常会实施安全措施,这些措施可能会在从不同源提供脚本文件时阻止错误报告。
带有 '非 Error 异常' 的事件
如果您看到带有消息“非 Error 异常(或 Promise 拒绝)捕获,键为:x, y, z。”的错误,这发生在以下情况:a) 使用普通对象调用 Sentry.captureException()
;b) 抛出普通对象;c) 使用普通对象拒绝 Promise。
您可以在“附加数据”部分的 __serialized__
入口中查看相关非 Error 对象的内容。
为了更好地了解这些错误事件,我们建议根据 __serialized__
数据的内容,找到将普通对象传递或抛给 Sentry 的位置,并将普通对象转换为 Error 对象。
捕获资源 404 错误
默认情况下,Sentry 不会捕获资源(如图片或 CSS 文件)加载失败时的错误。如果您希望捕获这些错误,可以使用以下代码。(注意:我们建议尽早加载 Sentry,但此方法特别要求在其他资源之前加载 Sentry 才能生效。)
document.body.addEventListener(
"error",
(event) => {
if (!event.target) return;
if (event.target.tagName === "IMG") {
Sentry.captureException(
new Error(`Failed to load image: ${event.target.src}`),
);
} else if (event.target.tagName === "LINK") {
Sentry.captureException(
new Error(`Failed to load css: ${event.target.href}`),
);
}
},
true, // useCapture - necessary for resource loading errors
);
请记住在调用 addEventListener()
时传递 true
作为第二个参数。如果不这样做,事件处理程序将不会被调用,因为它是添加到事件目标的祖先而不是事件目标本身上,而且与 JavaScript 运行时错误不同,由加载失败导致的 error
事件不会冒泡,因此必须在 捕获
阶段捕获。更多详情,请参阅 W3C 规范。
使用 Vite 时的构建错误
如果您正在使用 Vite 打包工具 和 Sentry NPM 包,并且遇到以下错误:
Error: Could not resolve './{}.js' from node_modules/@sentry/utils/esm/index.js
这可能是由于您的 Vite 配置中的 define
选项替换了 Sentry SDK 使用的某些变量(如 global
),从而导致构建错误。Vite 建议仅将 define
用于常量,而不应将 process
或 global
放入选项中。要修复此构建错误,请删除或更新您的 define
选项,如下所示:
vite.config.ts
export default defineConfig({
build: {
sourcemap: true
},
- define: {
- global: {}
- },
plugins: [react()]
})
缺少模块 `diagnostics_channel`
如果您在使用任何 JavaScript SDK 时遇到构建错误,提示类似于“找不到模块 diagnostics_channel”的信息,请尝试配置您的构建工具以外部化或忽略 diagnostics_channel
模块。 Sentry Node.js SDK 使用这个内置模块来instrument Node.js 的 fetch 请求。 某些较旧版本的 Node.js 不包含 diagnostics_channel
API,这可能导致在打包代码时出现构建错误。
大多数打包工具都有选项可以外部化特定模块(如 diagnostics_channel
):
Terser 插件构建错误
如果您使用了自定义的 Webpack 插件,该插件使用 Terser 或其他压缩工具,您可能会遇到由于已知的 Webpack 错误导致的构建错误。此问题已在 Webpack 版本 5.85.1 及以上版本中修复。
有关此问题的更多详情,请参阅 webpack/terser-plugin 构建错误。
pnpm:解决 'import-in-the-middle' 外部包错误
当使用 pnpm 时,您可能会遇到与无法外部化的包相关的错误,特别是像 import-in-the-middle
和 require-in-the-middle
这样的包。这些错误通常是由于 pnpm 的严格依赖管理及其提升行为引起的。
虽然将这些包添加为直接依赖项可能会消除警告信息,但这通常并不能解决潜在的功能问题。要彻底解决问题,您可以尝试以下方法:
- 检查依赖树:确保所有相关依赖项都正确安装,并且没有版本冲突。
- 调整 pnpm 配置:根据需要调整 pnpm 的配置文件(如
.npmrc
或pnpm-workspace.yaml
),以允许特定包的正确解析。 - 使用别名:在
package.json
中使用overrides
字段来覆盖某些包的版本,确保兼容性。
更多详情,请参阅 pnpm 官方文档 以获取更多信息和最佳实践。
pnpm add import-in-the-middle require-in-the-middle
作为变通方法,在项目根目录中创建或修改 .npmrc
文件:
shamefully-hoist=true
注意:虽然 shamefully-hoist=true
通常不是依赖管理的最佳解决方案,但有时为了与某些期望类似于 npm 或 yarn 的 Node.js 模块解析行为的包保持兼容性,这是必要的。
如果您需要更多帮助,可以在 GitHub 上提问。使用付费计划的客户还可以联系支持团队。