故障排除

如果您需要帮助解决与 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 的 统计信息订阅 页面。您可能已经用完了配额。

升级到新的 Sentry SDK 版本

如果您将 Sentry SDK 升级到一个新的主要版本,可能会遇到需要您进行调整的破坏性更改。 请查阅我们的 迁移指南,以了解所有需要知道的内容,确保您可以顺利使用最新的 Sentry 功能。

调试附加数据

您可以查看事件的 JSON 负载,以了解 Sentry 如何在事件中存储附加数据。数据结构可能与描述不完全一致。

红色框突出显示了如何找到与事件关联的 JSON

更多详情,请参阅 关于事件负载的完整文档

最大 JSON 负载大小

maxValueLength 的默认值为 250,但如果您的消息更长,您可以根据需要调整此值。请注意,并不是每个单独的值都会受此选项的影响。

CORS 属性和头部

要获取来自不同源的脚本抛出的 JavaScript 异常的可见性,请执行以下两个步骤:

  1. 添加 crossorigin="anonymous" 脚本属性
Copied
 <script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>

脚本属性告诉浏览器“匿名”获取目标文件。潜在的用户识别信息(如 Cookie 或 HTTP 凭据)在请求此文件时不会由浏览器传输到服务器。

  1. 添加跨域 HTTP 头部
Copied
Access-Control-Allow-Origin: *

跨域资源共享 (CORS) 是一组 API(主要是 HTTP 头部),用于规定文件应如何在不同源之间下载和提供服务。

通过设置 Access-Control-Allow-Origin: *,服务器向浏览器表明任何源都可以获取此文件。或者,您可以将其限制为您控制的特定源:

Copied
 Access-Control-Allow-Origin: https://www.example.com

CDN 通常会正确设置 Access-Control-Allow-Origin 头部。

Copied
 $ 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 请求,因此不会有事件被阻止,因为广告拦截器不会将这些事件视为第三方请求。

Copied
Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  tunnel: "/tunnel",
});

配置完成后,所有事件将被发送到 /tunnel 端点。然而,此解决方案需要在服务器端进行额外的配置,因为事件现在需要被解析并重定向到 Sentry。以下是一个服务器组件的示例:

Copied

// 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 或其他类似工具轻松获取它:

Copied
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> 标签 上方。可读格式的代码片段如下所示:

Copied
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);
}

如果您想直接复制和粘贴代码片段,以下是压缩后的版本:

Copied
<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.captureExceptionSentry.captureMessage

如果您使用第三方库来实现 Promise,可能还需要管理您的配置。此外,请记住,浏览器通常会实施安全措施,这些措施可能会在从不同源提供脚本文件时阻止错误报告。

带有 '非 Error 异常' 的事件

如果您看到带有消息“非 Error 异常(或 Promise 拒绝)捕获,键为:x, y, z。”的错误,这发生在以下情况:a) 使用普通对象调用 Sentry.captureException();b) 抛出普通对象;c) 使用普通对象拒绝 Promise。

您可以在“附加数据”部分的 __serialized__ 入口中查看相关非 Error 对象的内容。

为了更好地了解这些错误事件,我们建议根据 __serialized__ 数据的内容,找到将普通对象传递或抛给 Sentry 的位置,并将普通对象转换为 Error 对象。

捕获资源 404 错误

默认情况下,Sentry 不会捕获资源(如图片或 CSS 文件)加载失败时的错误。如果您希望捕获这些错误,可以使用以下代码。(注意:我们建议尽早加载 Sentry,但此方法特别要求在其他资源之前加载 Sentry 才能生效。)

Copied
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 包,并且遇到以下错误:

Copied
Error: Could not resolve './{}.js' from node_modules/@sentry/utils/esm/index.js

这可能是由于您的 Vite 配置中的 define 选项替换了 Sentry SDK 使用的某些变量(如 global),从而导致构建错误。Vite 建议仅将 define 用于常量,而不应将 processglobal 放入选项中。要修复此构建错误,请删除或更新您的 define 选项,如下所示:

vite.config.ts
Copied
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-middlerequire-in-the-middle 这样的包。这些错误通常是由于 pnpm 的严格依赖管理及其提升行为引起的。

虽然将这些包添加为直接依赖项可能会消除警告信息,但这通常并不能解决潜在的功能问题。要彻底解决问题,您可以尝试以下方法:

  1. 检查依赖树:确保所有相关依赖项都正确安装,并且没有版本冲突。
  2. 调整 pnpm 配置:根据需要调整 pnpm 的配置文件(如 .npmrcpnpm-workspace.yaml),以允许特定包的正确解析。
  3. 使用别名:在 package.json 中使用 overrides 字段来覆盖某些包的版本,确保兼容性。

更多详情,请参阅 pnpm 官方文档 以获取更多信息和最佳实践。

Copied
pnpm add import-in-the-middle require-in-the-middle

作为变通方法,在项目根目录中创建或修改 .npmrc 文件:

Copied
shamefully-hoist=true

如果您需要更多帮助,可以在 GitHub 上提问。使用付费计划的客户还可以联系支持团队。