Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(三)

简介: Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(三)

上传 Source Maps


Webpack


Sentry 使用 releases 来将正确的 source maps 与您的事件相匹配。release API 旨在允许您在 Sentry 中存储源文件(和 source maps)。


您可以在我们的 Webpack 插件的帮助下完成此操作,该插件在内部使用我们的 Sentry CLI


  1. 从您的 [Account] > API keys 创建一个新的身份验证令牌
  2. 确认您在“Scopes”下选择了 project:write
  3. 使用 npm 安装 @sentry/webpack-plugin
  4. 使用必要的配置创建 .sentryclirc 文件,如本页所述
  5. 更新你的 webpack.config.js


const SentryPlugin = require("@sentry/webpack-plugin");
module.exports = {
  // ... other config above ...
  plugins: [
    new SentryPlugin({
      release: process.env.RELEASE,
      include: "./dist",
    }),
  ],
};


使用我们的 Sentry Webpack 插件文档了解有关插件进一步配置的更多信息。 sentry-webpack-plugin:https://github.com/getsentry/sentry-webpack-plugin

此外,您需要配置 client 以发送 release


Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  release: process.env.RELEASE,
});


您不必使用 RELEASE 环境变量。只要您上传的版本与 SDKinit 调用的版本相匹配,您就可以以任何形式提供它们。


Releases API:https://docs.sentry.io/api/releases/


Sentry CLI


使用 sentry-cli 上传 Source Maps


使用 sentry-cli 上传 source maps 时,您需要设置构建系统以创建版本(release)并上传与该版本对应的各种源文件。要让 Sentry 对您的堆栈跟踪进行解码,请同时提供:


  • 要部署的文件(换句话说,您的编译/压缩/打包(transpilation/minification/bundling) 过程的结果;例如,app.min.js
  • 对应的 source maps

如果 source map 文件不包含您的原始源代码 (sourcesContent),您还必须提供原始源文件。如果源文件丢失,Sentry CLI 将尝试自动将源嵌入到您的 source maps 中。


Sentry 使用 releases 将正确的 source maps 与您的事件相匹配。要创建新版本,请运行以下命令(例如,在发布期间):

releases:https://docs.sentry.io/product/releases/


sentry-cli releases new <release_name>


release 名称在您的组织中必须是唯一的,并且与您的 SDK 初始化代码中的 release 选项相匹配。然后,使用 upload-sourcemaps 命令扫描文件夹中的 source maps,处理它们,并将它们上传到 Sentry



sentry-cli releases files <release_name> upload-sourcemaps /path/to/files


您可以通过导航到 [Project] > Project Settings > Source Maps 找到上传到 Sentry 的工件。


此命令会将所有以 .js.map 结尾的文件上传到指定的版本(release)。如果你想改变这些扩展 — 例如,上传 typescript 源文件 — 使用 --ext 选项:


sentry-cli releases files <release_name> upload-sourcemaps --ext ts --ext map /path/to/files


到目前为止,该版本处于草稿状态(“unreleased”)。上传所有 source maps 后,您的应用程序已成功发布,使用以下命令完成 release


sentry-cli releases finalize <release_name>


为方便起见,您可以将 --finalize 标志传递给新命令,这将立即完成 release

有关更多信息,请参阅我们的 sentry-cli 文档。


Web 应用程序可在多个来源访问的情况并不少见。请参阅我们关于多源的文档以了解如何处理此问题。


公开托管


source maps 提供给 Sentry 的最可靠方法是上传它们,因为它减少了网络流量并确保将使用正确版本的代码和源映射。


默认情况下,Sentry 将在您编译的 JavaScript 文件中查找 source map 指令。这些指令位于最后一行,格式如下:


//# sourceMappingURL=<url>


当 Sentry 遇到这样的指令时,它会解析相对于它所在的源文件的 source map URL,并尝试一个 HTTP 请求来获取它。


例如,如果您有一个位于 http://example.org/js/app.min.js 的压缩的 JavaScript 文件,并且在该文件的最后一行,可以找到以下指令:


//# sourceMappingURL=app.js.map


Sentry 将尝试从http://example.org/js/app.js.map 获取 app.js.map

或者,在 source map 生成期间,您可以指定 source map 所在的完全限定 URL


//# sourceMappingURL=http://example.org/js/app.js.map


虽然从您的服务器向 Sentry 提供 source maps 是最自然的集成,但并不总是可取的:

  • Sentry 可能并不总是能够访问您的服务器。
  • 如果您未在 asset URL 中指定版本,则可能存在版本不匹配
  • 额外的延迟可能意味着源映射并非适用于所有错误。

由于这些原因,最好事先将 source maps 上传到 Sentry(见下文)。


在防火墙后面工作


虽然推荐的解决方案是将您的源工件(打包转译后的代码)上传到 Sentry,但有时需要允许来自 Sentry 的内部 IP 的通信。有关 Sentry public IP 的更多信息,请参阅:


安全访问 Source Maps


如果您想对 source maps 保密并选择不将 source maps 直接上传到 Sentry,您可以在项目设置中启用 “Security Token” 选项。

这将导致从 Sentry 的服务器发出的来自你的 “Allowed Domains” 的 url 的出站请求附加 HTTP header X-Sentry-Token 头:


GET /assets/bundle.min.js
X-Sentry-Token: {token}


token 是您在项目设置中定义的安全值。然后,您可以配置您的 Web 服务器以允许在此 header/token 对存在时访问您的 source maps。您也可以覆盖默认 header 名称 (X-Sentry-Token) 并使用 HTTP Basic Authentication,例如通过传递 Authorization: Basic {encoded_password}


多个 Origin


Web 应用程序可在多个来源访问的情况并不少见。例如:


在这种情况下,相同的 JavaScriptsource map 文件可能位于两个或多个不同的来源。在这种情况下,我们建议在路径上使用我们特殊的波浪号 (~) 前缀。

例如,如果您有以下内容:

您可以使用 ~/js/app.jsURL 上传。这将告诉 Sentry 忽略域并将 artifact 用于任何来源。

此外,您还可以以多个名称上传同一个文件。在引擎盖(hood)下 Sentry 将对这些进行重复数据删除。

~ 前缀告诉 Sentry 对于给定的 URL,路径为 /js/app.js 的协议和主机名的任何组合都应该使用这个工件。


验证文件

确保 source maps 本身有效并正确上传可能非常具有挑战性。为了解决这个问题,我们维护了一个在线验证工具,可用于针对您的托管源测试您的 source mapsourcemaps.io

此外,您可以在使用 sentry-cli 上传 source maps 时使用 --validate 标志,这将尝试在本地解析源映射并查找引用。请注意,在已知情况下,验证标志将在设置正确时指示失败(如果您引用了外部 source maps,则验证工具将指示失败)。

除了验证步骤之外,您还可以检查这些:

  • 确保您的文件的 URL 前缀正确。这很容易出错。
  • 上传压缩文件的匹配 source maps
  • 确保您在服务器上的压缩文件实际上引用了您的文件。


最佳实践


一个简单的设置

在这个简单的项目中,minified/transpiled 的文件及其 source maps 位于同一目录中:


├── build/
│   ├── worker.js
│   ├── worker.js.map
│   ├── app.js
│   ├── app.js.map
│   ├── index.html
├── package.json
├── public/
│   └── index.html
├── sentry.properties
├── src/
│   ├── app.js
│   └── worker.js
├── webpack.config.js


对于这个项目,我们可以使用一个简单的 Sentry 配置:


const SentryWebpackPlugin = require("@sentry/webpack-plugin");
// ...
plugins: [
  new SentryWebpackPlugin({
    authToken: process.env.SENTRY_AUTH_TOKEN,
    org: "example-org",
    project: "example-project",
      include: "build",
    configFile: "sentry.properties",
    release: process.env.SENTRY_RELEASE,
  }),
],
// ...


我们建议使用 Webpack 插件将 source maps 集成到 Sentry。如果您的项目中没有使用 Webpack,则可以使用 Sentry CLI


一致的版本


要让 Sentry 将错误堆栈跟踪与您的 source maps 相关联,请将您的版本号定义为 Webpack 插件选项或 Sentry CLI 参数(无论您使用哪个)。如果您使用 Sentry CLI,您还应该在 Sentry.init() 调用中定义相同的版本号。确保版本号一致性的最简单方法是将其设置为项目中的环境变量:


# ...
SENTRY_RELEASE="1.2.3"
# ...


然后,如果您使用的是 sentry-webpack-plugin


// ...
new SentryWebpackPlugin({
  // ... other options
  release: process.env.SENTRY_RELEASE,
});
// ...


或者,如果您使用的是 Sentry CLI

sh sentry-cli releases new "$SENTRY_RELEASE" sentry-cli releases files "$SENTRY_RELEASE" upload-sourcemaps /path/to/sourcemaps


// ...
Sentry.init({
  // ... other options
  release: process.env.SENTRY_RELEASE,
});
// ...


正确的 Source Paths


您的 release artifactsbundle 文件和源 source maps)的文件名应与堆栈跟踪中报告的路径匹配。您可以使用上传配置来调整文件的名称。 Webpack 插件和 Sentry CLI 都有相同的选项;下面介绍了与 source maps 相关的内容。还可以使用我们的 RewriteFrames 集成来调整堆栈跟踪内的路径。


根据您的设置,您可能需要在开发和生产环境中为 source maps 进行不同的配置,因为堆栈跟踪中的路径可能不同。


Webpack 和 Sentry CLI 的选项


这些选项和示例将有助于集成您的source maps

include

此选项接受一个或多个路径来递归扫描源和 *.map 文件。例如:

  1. 包括您的转译器/捆绑器输出文件的位置:
  • include: './app/.next'
  • include: './build'
  1. 包括来自多个文件夹:
  • 包括:['./src', './lib']
  1. 递归搜索整个项目:
  • include: '.'

rewrite

允许重写匹配的 source maps,以便在可能的情况下将索引映射扁平化并内联缺失的源。默认为 true


应该启用此选项以使 stripPrefixstripCommonPrefix 工作。

urlPrefix

此选项在所有文件名的开头添加一个公共前缀。默认为 ~/,这是一个匹配任何 schemehostname 的通配符(http://my.web.site/path/to/script.jshttp://my.web.site/ 部分)。


当应用程序的入口点(通常是浏览器端的 index.htmlNodeindex.js)位于源/源映射文件之上一个或多个级别时,此选项很有用,如下例所示:


├── build/
│   ├── index.html
│   ├── static/
│   │   ├── app.js
│   │   ├── app.js.map


在这种情况下,请按照以下示例进行配置:


// ...
new SentryWebpackPlugin({
  // ...
  include: "build/static/",
  urlPrefix: "~/static/"
  // ...
}),
// ...


stripPrefix

此选项从 sourcemap 中(例如,在 sources entry 中)引用的文件名中删除给定的前缀。当您需要修剪捆绑器/开发(bundler/development)服务器可能添加到文件名的额外前缀时,这很有用,例如 webpack://_N_E/


请注意,使用 stripPrefix 选项不会更改上传文件的名称。当您将目标文件的父文件夹作为不需要的前缀时,请在包含 Webpack 插件选项或传递给 sentry-clipath/to/sourcemaps 中包含要删除的部分。例如,如果您的文件存储在 ./build/static/js/ 并且您在 Webpack 插件配置中有 include: "build",您的文件将使用类似 ~/static/js/bundle.js 的名称上传。如果您更新您的配置 include: "build/static/js",您的文件将上传为 ~/bundle.js(等等)。


调整帧(Frames)


或者,您可以使用 SentryRewriteFrames 集成来微调堆栈跟踪内的路径。


import { RewriteFrames } from "@sentry/integrations";
Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new RewriteFrames({
      // ... options
    }),
  ],
});


对 Source Maps 进行故障排除


Source maps 有时很难开始。如果您遇到问题:


验证在您的 SDK 中配置了一个 release


要定位和应用上传的 source maps,需要通过 CLI 或 API(以及随其上传的正确工件)创建 release,并且需要在您的 SDK 配置中指定新创建的 release 的名称。

要验证这一点,请从 Sentry UI 打开 issue 并检查 release 是否已配置。如果屏幕右侧的 “Release” 旁边显示 “not configured”“N/A”(或者如果您在标签列表中没有看到 release tag),则需要返回并标记你的错误。如果设置正确,您将看到 "Release: my_example_release"


验证工件(artifacts)已上传


正确配置您的 release 并标记问题后,您可以通过导航到 [Project] » Project Settings » Source Maps 找到上传到 Sentry 的工件。


此外,请确保所有必要的文件都可用。要让 Sentry de-minify 堆栈跟踪,您必须同时提供 minify 的文件(例如 app.min.js)和相应的 source map。如果 source map 文件不包含您的原始源代码 (sourcesContent),您必须另外提供原始源代码文件。或者,sentry-cli 会自动将源代码(如果缺少)嵌入到您的 source maps 中。


验证 sourceMappingURL 是否存在


一些 CDN 会自动从静态文件(包括 JavaScript 文件)中去除注释。这可能会导致删除 JavaScript 文件的 sourceMappingURL 指令,因为它被视为注释。例如,CloudFlare 有一个名为 Auto-Minify 的功能,如果启用它,它将去除 sourceMappingURL

仔细检查您部署的最终 JavaScript 文件是否存在 sourceMappingURL

或者,您可以在 minify 的文件上设置 SourceMap HTTP header,而不是 sourceMappingURL。如果此 header 存在,Sentry 将使用它来发现 source map 的位置。


验证 artifact 发布值是否与您的 SDK 中配置的值匹配


每当您使用分发标识符(SDK 中的 dist 配置选项)时,在 source map 上传期间必须使用相同的值。相反,如果您的 source map 使用 dist 值上传,则必须在您的 SDK 中设置相同的值。要将 dist 值添加到您上传的 source maps,请使用 --dist 标志和 sentry-clidist 选项和 @sentry/webpack-plugin。要在 SDK 中设置 dist 值,请使用 Sentry.init() 中的 dist 选项。


要验证 SDK 中的分发设置是否正确,请在 Sentry UI 中打开一个 issue 并检查 dist 标签是否存在。对于工件,转到项目设置中的 Source Maps 页面,选择您刚刚检查的事件中显示的 release,并验证 dist 值(在 upload time 旁边的小椭圆中)与事件上的值匹配。


验证 artifact 名称与 sourceMappingURL 值匹配


bundledminified 的 JavaScript 文件最后一行的 sourceMappingURL 注释告诉 Sentry(或浏览器)在哪里找到相应的 source map。这可以是完全限定的 URL、相对路径或文件名本身。将 artifact 上传到 Sentry 时,您必须使用文件解析为的值命名源映射文件。


也就是说,如果您的文件类似于:


// -- end script.min.js
//# sourceMappingURL=script.min.js.map


并托管在 http://example.com/js/script.min.js,然后 Sentry 将在 http://example.com/js/script.min.js.map 查找该 source map 文件。因此,您上传的 artifact 必须命名为 http://example.com/js/script.min.js.map(或 ~/js/script.min.js.map)。


或者,如果您的文件类似于:


//-- end script.min.js
//# sourceMappingURL=https://example.com/dist/js/script.min.js.map


那么您上传的 artifact 也应该命名为:

https://example.com/dist/js/script.min.js.map(或 ~/dist/js/script.min.js.map)。

最后,如果您的文件类似于:


//-- end script.min.js
//# sourceMappingURL=../maps/script.min.js.map


那么您上传的 artifact 应命名为 https://example.com/dist/maps/script.min.js.map(或 ~/dist/maps/script.min.js.map)。


验证 artifact 名称与堆栈跟踪帧匹配


如果您已上传 source maps,但它们并未应用于 Sentry 问题中的代码,请查看事件的 JSON 并查找 abs_path 以准确查看我们尝试解析文件的位置 - 对于 例如,http://localhost:8000/scripts/script.js(对于堆栈跟踪中的每一帧,abs_path 将出现一次 - 将其与未 deminified 的文件匹配。)。可以在事件发生日期旁边的 issue 页面顶部找到指向 JSON 视图的链接。上传的 artifact 名称必须与这些值匹配。


如果您的路径中有动态值(例如,https://www.site.com/{some_value}/scripts/script.js),您可能需要使用 rewriteFrames 集成来更改您的 abs_path 值。


使用 sentry-cli


如果您的 sourceMappingURL 注释类似于:


// -- end script.min.js (located at http://localhost:8000/scripts/script.min.js)
//# sourceMappingURL=script.min.js.map


正确上传这些文件的示例 sentry-cli 命令如下所示(假设您在 /scripts 目录中,从上一级目录运行 Web 服务器,这就是我们使用 --url-prefix 选项的原因) :


sentry-cli releases files VERSION upload-sourcemaps . --url-prefix '~/scripts'


此命令上传当前目录中的所有 JavaScript 文件。 Sentry 中的 Artifacts 页面现在应如下所示:


~/scripts/script.js
~/scripts/script.min.js
~/scripts/script.min.js.map


或者,您可以指定要上传的文件。例如:


sentry-cli releases files VERSION upload-sourcemaps script.min.js script.min.js.map --url-prefix '~/scripts'


您还可以使用完全限定的 URL 上传它。例如:


sentry-cli releases files VERSION upload-sourcemaps . --url-prefix 'http://localhost:8000/scripts'


使用 API

您也可以使用 API 上传 artifact


curl -X POST \
  https://sentry.io/api/0/organizations/ORG_SLUG/releases/VERSION/files/ \
  -H 'Authorization: Bearer AUTH_TOKEN' \
  -H 'content-type: multipart/form-data' \
  -F file=@script.min.js.map \
  -F 'name=~/scripts/script.min.js.map'


使用 ~

~Sentry 中用于替换 schemedomain

http://example.com/dist/js/script.js 将匹配 ~/dist/js/script.jshttp://example.com/dist/js/script.js

但不会匹配 ~/script.js


在发生错误之前验证 artifact 已上传


Sentry 期望给定版本中的源代码和 source maps 在该 release 中发生错误之前上传到 Sentry


如果您在 Sentry 捕获错误后上传 artifactSentry 将不会返回并追溯将任何源注释(source annotations)应用于这些错误。只有在 artifact 上传后触发的新错误才会受到影响。


验证您的 source maps 是否正确构建

我们维护一个在线验证工具,可用于针对您的托管源测试您的source mapssourcemaps.io

或者,如果您使用 Sentry CLIsource maps 上传到 Sentry,您可以使用 --validate 命令行选项来验证您的 source maps 是否正确。


验证您的 source maps 在本地工作

如果您发现 Sentry 没有正确映射文件名、行或列映射,您应该验证您的 source maps 是否在本地运行。为此,您可以将 Node.jsMozillasource-map library 结合使用。


首先,将 source-map 作为 npm 模块全局安装:


npm install -g source-map


然后,编写一个脚本来读取您的 source map 文件并测试映射。下面是一个例子:


var fs = require("fs"),
  path = require("path"),
  sourceMap = require("source-map");
// file output by Webpack, Uglify, and so forth
var GENERATED_FILE = path.join(".", "app.min.js.map");
// line and column located in your generated file (for example, the source of your error
// from your minified file)
var GENERATED_LINE_AND_COLUMN = { line: 1, column: 1000 };
var rawSourceMap = fs.readFileSync(GENERATED_FILE).toString();
new sourceMap.SourceMapConsumer(rawSourceMap).then(function(smc) {
  var pos = smc.originalPositionFor(GENERATED_LINE_AND_COLUMN);
  // should see something like:
  // { source: 'original.js', line: 57, column: 9, name: 'myfunc' }
  console.log(pos);
});


如果您在本地获得与通过 Sentry 获得的结果相同(不正确)的结果,请仔细检查您的 source map 生成配置。


验证您的源文件不是太大


对于单个 artifactSentry 接受的最大文件大小为 40 MB

用户通常会达到此限制,因为他们在临时构建阶段传输源文件。例如,在 Webpack/Browserify 合并所有源文件之后,但在 minification 之前。如果可能,请发送原始源文件。


验证 artifact 没有被 gzip


Sentry API 目前仅适用于以纯文本(UTF-8 编码)形式上传的 source maps 和源文件。如果文件以压缩格式(例如 gzip)上传,它们将不会被正确解释。

这有时发生在生成 pre-compressed minified 文件的构建脚本和插件中。例如,Webpackcompression 插件。您需要禁用此类插件并在生成的 source maps/source files 上传到 Sentry 后执行压缩。


验证 worker 与 Web 共享相同的卷(如果通过 Docker 运行自托管 Sentry)


Sentry 在其 worker 中进行 source map 计算。这意味着 worker 需要访问通过前端上传的文件。仔细检查 cron workerweb worker 是否可以从同一个磁盘读取/写入文件。


故障排除


如果您需要帮助解决 Sentry JavaScript SDK integration 问题,您可以阅读此处记录的边缘案例。


调试附加数据


您可以查看事件的 JSON payload 以了解 Sentry 如何在事件中存储其他数据。数据的形状可能与描述不完全匹配。


有关更多详细信息,请参阅有关事件有效负载的完整文档。


最大 JSON Payload 大小


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


CORS 属性和 Header


要了解从不同来源的脚本引发的 JavaScript 异常,请执行以下两项操作:

  1. 添加 crossorigin="anonymous" 脚本属性

<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>


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

  1. 添加 Cross-Origin HTTP header

Access-Control-Allow-Origin: *


跨域资源共享 (CORS) 是一组 API(主要是 HTTP header),用于规定文件应该如何跨域下载和提供服务。


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


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


大多数社区 CDN 正确设置了 Access-Control-Allow-Origin header。


$ 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 请求 header 的问题,当您在浏览器 SDK 中为我们的 Tracing Integration 使用过于通用的配置时可能会发生这种情况。

要解决此问题,请在 SDK 初始化期间更改 trackingOrigins 选项。有关更多详细信息,请参阅我们的性能监控文档中的自动检测。



instrument.js Console Log 语句的行号


如果调试时在您的控制台中显示了 instrument.js,请将 Sentry 添加到您的框架黑盒设置中,例如:/@sentry/,以便 Chrome 在调试时忽略 SDK 堆栈帧。


处理广告拦截器(Ad-Blockers)


当您使用我们的 CDN 时,广告拦截或脚本拦截扩展可能会阻止我们的 SDK 被正确获取和初始化。因此,对 SDK API 的任何调用都将失败,并可能导致您的应用程序出现意外行为。


此外,即使正确下载并初始化 SDK,也可能会阻止需要接收捕获数据的 Sentry 端点。这将阻止任何错误报告、会话运行状况或性能数据的传递,从而使其在 sentry.io 中实际上不可用。

您可以通过上述多种方式解决第一个 issue 。但是,端点阻塞只能使用隧道解决。


使用 tunnel 选项


隧道是一个 HTTP 端点,充当 Sentry 和您的应用程序之间的代理。由于您控制此服务器,因此不会有任何发送到它的请求被阻止的风险。当端点位于同一个源下时(尽管它不必为了隧道工作),浏览器不会将任何对端点的请求视为第三方请求。因此,这些请求将应用不同的安全措施,默认情况下不会触发广告拦截器。可以在下面找到流程的快速摘要。


微信图片_20220612181534.png


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。这是您的服务器组件的示例:


<?php
// Change $host appropriately if you run your own Sentry instance.
$host = "sentry.io";
// Set $known_project_ids to an array with your Sentry project IDs which you
// want to accept through this proxy.
$known_project_ids = array(  );
$envelope = stream_get_contents(STDIN);
$pieces = explode("\n", $envelope, 2);
$header = json_decode($pieces[0], true);
if (isset($header["dsn"])) {
    $dsn = parse_url($header["dsn"]);
    $project_id = intval(trim($dsn["path"], "/"));
    if (in_array($project_id, $known_project_ids)) {
      $options = array(
        'http' => array(
            'header'  => "Content-type: application/x-sentry-envelope\r\n",
            'method'  => 'POST',
            'content' => $envelope
        )
      );
      echo file_get_contents(
          "https://$host/api/$project_id/envelope/",
          false,
          stream_context_create($options));
    }
}


// Requires .NET Core 3.1 and C# 9 or higher
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 包本身被阻止有关,以下任何一种解决方案都可以帮助您解决此问题。


直接使用 Package


处理脚本阻塞扩展的最佳方法是直接通过 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


使用 JavaScript Proxy API


最后一个选项是使用 Proxy 保护,这将确保您的代码不会中断,即使您调用我们的 SDK,它被阻止。除了 Internet Explorer 之外的所有浏览器都支持 Proxy。此外,如果 Proxy 不在您用户的任何浏览器中,它将被悄悄跳过,因此您不必担心它会破坏任何内容。


将此代码段直接放在包含我们的 CDN 包的 <script> 标签上方。可读格式的代码片段如下所示:


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


如果您想直接复制和粘贴代码段,这里将其 minified


<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>


直接使用 Client


为了能够管理多个 Sentry 实例而它们之间没有任何冲突,您需要创建自己的 Client。如果您的应用程序集成在其中,这也有助于防止跟踪任何父应用程序错误。在这个例子中,我们使用 @sentry/browser 但它也适用于 @sentry/node


import { BrowserClient } from "@sentry/browser";
const client = new BrowserClient({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
});
client.captureException(new Error("example"));


虽然上面的示例应该可以正常工作,但 Client 上缺少一些方法,如 configureScopewithScope,因为 Hub 负责状态管理。这就是为什么创建新 Hub 并将 Client 绑定到它可能更容易的原因。结果是一样的,但你也会得到状态管理。


import { BrowserClient, Hub } from "@sentry/browser";
const client = new BrowserClient({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
});
const hub = new Hub(client);
hub.configureScope(function(scope) {
  scope.setTag("a", "b");
});
hub.addBreadcrumb({ message: "crumb 1" });
hub.captureMessage("test");
try {
  a = b;
} catch (e) {
  hub.captureException(e);
}
hub.withScope(function(scope) {
  hub.addBreadcrumb({ message: "crumb 2" });
  hub.captureMessage("test2");
});


处理集成


集成是在 Client 上设置的,如果您需要处理多个 ClientHub,您还必须确保正确进行集成处理。这是一个如何使用多个 Client 和多个运行全局集成的 Hub 的工作示例。


import * as Sentry from "@sentry/browser";
// Very happy integration that'll prepend and append very happy stick figure to the message
class HappyIntegration {
  constructor() {
    this.name = "HappyIntegration";
  }
  setupOnce() {
    Sentry.addGlobalEventProcessor(event => {
      const self = Sentry.getCurrentHub().getIntegration(HappyIntegration);
      // Run the integration ONLY when it was installed on the current Hub
      if (self) {
        event.message = `\\o/ ${event.message} \\o/`;
      }
      return event;
    });
  }
}
HappyIntegration.id = "HappyIntegration";
const client1 = new Sentry.BrowserClient({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [...Sentry.defaultIntegrations, new HappyIntegration()],
  beforeSend(event) {
    console.log("client 1", event);
    return null; // Returning null does not send the event
  },
});
const hub1 = new Sentry.Hub(client1);
const client2 = new Sentry.BrowserClient({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0", // Can be a different DSN
  integrations: [...Sentry.defaultIntegrations, new HappyIntegration()],
  beforeSend(event) {
    console.log("client 2", event);
    return null; // Returning null does not send the event
  },
});
const hub2 = new Sentry.Hub(client2);
hub1.run(currentHub => {
  // The hub.run method makes sure that Sentry.getCurrentHub() returns this hub during the callback
  currentHub.captureMessage("a");
  currentHub.configureScope(function(scope) {
    scope.setTag("a", "b");
  });
});
hub2.run(currentHub => {
  // The hub.run method makes sure that Sentry.getCurrentHub() returns this hub during the callback
  currentHub.captureMessage("x");
  currentHub.configureScope(function(scope) {
    scope.setTag("c", "d");
  });
});


第三方 Promise 库


当您包含和配置 Sentry 时,我们的 JavaScript SDK 会自动附加 global handlerscapture 未捕获的 exceptions 和未处理的 promise rejections。您可以通过在 GlobalHandlers 集成中将 onunhandledrejection 选项更改为 false 并手动挂接到每个事件处理程序,然后直接调用 Sentry.captureExceptionSentry.captureMessage 来禁用此默认行为。


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


具有“非错误异常Non-Error Exception”的事件


如果您看到错误消息 “Non-Error exception (or promise rejection) captured with keys: x, y, z.”,这会发生在您 a) 使用 plain object 调用 Sentry.captureException() 时,b) 抛出一个 plain object,或者 c) 拒绝一个带有 plain objectpromise


您可以在 “Additional Data” 部分的 __serialized__ 条目中查看有问题的非错误对象的内容。


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


支持的浏览器


SentryJavaScript SDK 支持以下浏览器:

  • Android:4.4, 5.0, 6.0, 7.1, 8.1, 9.0, 10.0
  • Firefox:latest
  • Chrome:latest
  • IE:IE 10, IE 11
  • iPhone:iOS 12, IOS 13
  • Edge:latest
  • Safari:latest


支持 <= IE 11


5.7.0 版本之前,我们的 JavaScript SDK 需要一些 polyfills 用于旧版浏览器,如 IE 11 及更低版本。如果您正在使用它,请在加载我们的 SDK 之前升级到最新版本或添加下面的脚本标签。


<script src="https://polyfill.io/v3/polyfill.min.js?features=Promise%2CObject.assign%2CString.prototype.includes%2CNumber.isNaN"></script>


我们需要以下 polyfill


  • Promise
  • Object.assign
  • Number.isNaN
  • String.prototype.includes


此外,请记住在 HTML 页面顶部定义有效的 HTML doctype,以确保 IE 不会进入兼容模式(compatibility mode)

相关文章
|
存储 Web App开发 前端开发
Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(二)
Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(二)
553 0
|
存储 前端开发 JavaScript
Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(一)
Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(一)
1446 0
Sentry For React 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!(一)
|
4月前
|
监控 Ubuntu Docker
Sentry 监控 Docker 方式部署
Sentry 监控 Docker 方式部署
124 0
|
4月前
|
监控 前端开发 JavaScript
Sentry 监控部署与使用(详细流程)
Sentry 监控部署与使用(详细流程)
156 0
|
8月前
|
存储 监控 前端开发
Sentry 监控部署与使用(详细流程)
Sentry 监控部署与使用(详细流程)
1281 0
|
8月前
|
监控 Ubuntu Unix
Sentry 监控 Docker 方式部署
Sentry 监控 Docker 方式部署
330 0
|
9月前
|
人工智能 监控 前端开发
大型网站重构指南 第1.3部分:前端监控和小程序监控 Sentry
大型网站重构指南 第1.3部分:前端监控和小程序监控 Sentry
733 0
|
编解码 监控 前端开发
Sentry 监控 - Alerts 告警
Sentry 监控 - Alerts 告警
546 0
Sentry 监控 - Alerts 告警
|
消息中间件 存储 缓存
Sentry 企业级数据安全解决方案 - Relay 监控 & 指标收集
Sentry 企业级数据安全解决方案 - Relay 监控 & 指标收集
269 0
|
存储 消息中间件 监控
Sentry 监控 - 私有 Docker Compose 部署与故障排除详解
Sentry 监控 - 私有 Docker Compose 部署与故障排除详解
1862 0
Sentry 监控 - 私有 Docker Compose 部署与故障排除详解