快速开始
Sentry 的 React SDK 支持自动报告错误和异常。SDK 是 @sentry/browser 的包装器,增加了与 React 相关的功能。 @sentry/browser 中可用的所有方法都可以从 @sentry/react 中导入。
安装
Sentry 通过在应用程序 runtime 使用 SDK 捕获数据。
- # 使用 npm
- npm install --save @sentry/react @sentry/tracing
- # 使用 yarn
- yarn add @sentry/react @sentry/tracing
- export const _frontmatter = {}
配置
配置应该在应用程序的生命周期中尽早进行。
- import React from "react";
- import ReactDOM from "react-dom";
- import * as Sentry from "@sentry/react";
- import { Integrations } from "@sentry/tracing";
- import App from "./App";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new Integrations.BrowserTracing()],
- // 我们建议在生产中调整此值,或使用 tracesSampler 进行更精细的控制
- tracesSampleRate: 1.0,
- });
- ReactDOM.render(<App />, document.getElementById("root"));
- // 也可以与 React Concurrent Mode 一起使用
- // ReactDOM.createRoot(document.getElementById('root')).render(<App />);
一旦完成,所有未处理的异常都会被 Sentry 自动捕获。
添加 Error Boundary
如果您使用的是 React 16 或更高版本,则可以使用 Error Boundary 组件将组件树内部的 Javascript 错误自动发送到 Sentry,并设置回退 UI。
设置 React Router
React Router 集成旨在与我们的跟踪包一起使用。请在下方配置部分了解有关 React Router Integration 及其选项的更多信息。
应用 Redux
要将 Sentry 应用于 Redux,请在下方配置部分了解有关 Redux Integration 及其选项的更多信息。
验证
此代码段包含一个故意错误,因此您可以在设置后立即测试一切是否正常:
- return <button onClick={methodDoesNotExist}>Break the world</button>;
组件
Sentry React SDK 公开自定义组件,以便与 React 框架进行一级集成。
React Error Boundary
React SDK 导出一个错误边界组件,该组件利用 React component API 自动捕获 JavaScript 错误并将其从 React 组件树内部发送到 Sentry。
- import React from "react";
- import * as Sentry from "@sentry/react";
- <Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
- <Example />
- </Sentry.ErrorBoundary>;
Sentry Error Boundary 也可用作高阶组件。
- import React from "react";
- import * as Sentry from "@sentry/react";
- Sentry.withErrorBoundary(Example, { fallback: <p>an error has occurred</p> });
Note:在 development 模式下,React 会重新抛出在错误边界内捕获的错误。这将导致使用上述设置向 Sentry 报告两次错误,但这不会发生在您的生产版本中。
在下面的示例中,当
- import React from "react";
- import * as Sentry from "@sentry/react";
- import { Example } from "../example";
- function FallbackComponent() {
- return <div>An error has occurred</div>;
- }
- const myFallback = <FallbackComponent />;
- // Alternatively:
- // const myFallback = () => <FallbackComponent />;
- class App extends React.Component {
- render() {
- return (
- <Sentry.ErrorBoundary fallback={myFallback} showDialog>
- <Example />
- </Sentry.ErrorBoundary>
- );
- }
- }
- export default App;
选项
ErrorBoundary 组件公开了各种可以传入以进行额外配置的属性。没有必需的选项,但我们强烈建议您设置 fallback 组件。
showDialog (boolean)
- 当 Error Boundary 捕捉到错误时,是否应呈现 Sentry User Feedback Widget。
dialogOptions (Object)
- 传递到 Sentry User Feedback Widget 的选项。在下方查看所有可能的自定义选项。
https://docs.sentry.io/platforms/javascript/guides/react/enriching-events/user-feedback/#customizing-the-widget
fallback (React.ReactNode or Function)
- 当错误边界捕获错误时要呈现的 React 元素。可以是实际的 React 元素(即
),也可以是返回 React 元素的函数。如果您提供一个函数,Sentry 将使用附加信息和帮助程序调用它(参见下面的示例)。
onError (Function)
- 当 Error Boundary 遇到错误时调用的函数。如果您想将错误传播到 Redux 之类的状态管理库中,或者您想检查由于错误而可能发生的任何副作用,onError 非常有用。
onUnmount (Function)
- 在 ErrorBoundary componentWillUnmount() 上调用的函数。
beforeCapture (Function) *(5.20.0 及以上版本可用)
在将错误发送到 Sentry 之前调用的函数,允许将额外的标签(tag)或上下文(context)添加到错误中。
示例
设置 Fallback 函数(渲染属性)
下面是一个示例,其中使用渲染属性方法的 fallback 属性用于在错误时显示 fallback UI。使用组件通过渲染属性提供的 resetError() API 重置时,fallback UI 会返回到标准组件状态。
- import React from "react";
- import * as Sentry from "@sentry/react";
- class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- message: "This is my app",
- };
- }
- render() {
- return (
- <Sentry.ErrorBoundary
- fallback={({ error, componentStack, resetError }) => (
- <React.Fragment>
- <div>You have encountered an error</div>
- <div>{error.toString()}</div>
- <div>{componentStack}</div>
- <button
- onClick={() => {
- this.setState({ message: "This is my app" });
- {/* When resetError() is called it will remove the Fallback component */}
- {/* and render the Sentry ErrorBoundary's children in their initial state */}
- resetError();
- }}
- >
- Click here to reset!
- </button>
- </React.Fragment>
- )}
- >
- <div>{this.state.message}</div>
- {/* on click, this button sets an Object as a message, not a string. */}
- {/* which will cause an error to occur in the component tree */}
- <button
- onClick={() => this.setState({ message: { text: "Hello World" } })}
- >
- Click here to change message!
- </button>
- </Sentry.ErrorBoundary>
- );
- }
- }
- export default App;
使用多个错误边界
(5.20.0 及以上版本可用)
当使用多个错误边界时,我们建议使用 beforeCapture 设置标签/上下文(tags/context),以便您可以知道错误发生在哪个错误边界。在下面的示例中,我们根据错误渲染的路径将标记(tag)附加到错误(error)。
- import React from 'react';
- import * as Sentry from '@sentry/react';
- function App({ props }) {
- return (
- <React.Fragment>
- <Sentry.ErrorBoundary
- beforeCapture={(scope) => {
- scope.setTag("location", "first");
- scope.setTag("anotherTag", "anotherValue");
- }}
- >
- <Route to="path/to/first" component={First} />
- </Sentry.ErrorBoundary>
- <Sentry.ErrorBoundary
- beforeCapture={(scope) => {
- scope.setTag("location", "second");
- }}
- >
- <Route to="path/to/second" component={Second} />
- </Sentry.ErrorBoundary>
- </React.Fragment>
- );
- }
- export default App;
React Profiler
@sentry/react 导出一个 withProfiler 高阶组件,该组件将 React 相关的 span 附加到作用域上的当前活动事务(transaction)。
在下面的示例中,withProfiler 高阶组件用于检测 App 组件。
- import React from "react";
- import * as Sentry from "@sentry/react";
- class App extends React.Component {
- render() {
- return (
- <FancyComponent>
- <NestedComponent someProp={2} />
- <AnotherComponent />
- </FancyComponent>
- );
- }
- }
- export default Sentry.withProfiler(App);
React Profiler 目前使用三种不同类型的操作码(op-codes)生成 span:react.mount、react.render 和 react.update。
react.mount
- 表示被分析组件 mount 所需时间的跨度。
react.render
- 表示被分析组件在页面上的时间跨度。只有在事务发生时 mount 和 unmount 被分析的组件时,才会生成这个跨度。
react.update
- 表示被分析组件更新时的跨度。只有当被分析组件已 mount 时才生成此 span。
在 React Strict Mode 下,某些组件方法将被调用两次。这可能会导致在事务中出现重复的 react.mount 跨度。 React Strict Mode 仅在开发模式下运行,因此这不会影响您的生产跟踪。
Profiler 选项
withProfiler 高阶组件有多种选项可用于进一步定制。它们可以作为第二个参数传入 withProfiler函数。
- export default Sentry.withProfiler(App, { name: "CustomAppName" });
name (string)
- 被分析的组件的名称。默认情况下,名称取自组件的 displayName 属性或组件的 name 属性。
includeRender (boolean)
- 是否应该由 Profiler 创建 react.render 跨度。默认设置为 true。
includeUpdates (boolean)
- react.update spans 是否应该由 Profiler 创建。默认设置为 true。对于将经历多次重新渲染的组件(例如文本输入text input组件),我们建议将此属性设置为 false,因为生成的 span 可能非常嘈杂。
配置
基本选项
SDK 可以使用多种选项进行配置。这些选项在 SDK 中基本上是标准化的,但在更好地适应平台特性方面存在一些差异。选项是在 SDK 首次初始化时设置的。
选项作为对象传递给 init() 函数:
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- maxBreadcrumbs: 50,
- debug: true,
- });
常见选项
跨 SDK 的常用选项列表。这些在所有 SDK 中的工作方式或多或少都相同,但为了更好地支持平台,将存在一些细微差别。可以从环境变量(SENTRY_DSN、SENTRY_ENVIRONMENT、SENTRY_RELEASE)中读取的选项会自动读取。
dsn
DSN 告诉 SDK 将事件发送到哪里。如果没有提供这个值,SDK 将尝试从 SENTRY_DSN 环境变量中读取它。如果这个变量也不存在,SDK 就不会发送任何事件。
在没有进程环境(如浏览器)的运行时中,fallback 不会应用。
更多:https://docs.sentry.io/product/sentry-basics/dsn-explainer/#dsn-utilization
debug
打开或关闭调试模式。如果启用了调试,如果发送事件时出现问题,SDK 将尝试打印出有用的调试信息。默认值总是 false。一般不建议在生产环境中打开它,尽管打开 debug 模式不会引起任何安全问题。
release
设置 release。某些 SDK 会尝试自动配置 release,但是最好手动设置 release,以确保该 release 与您的 deploy integrations 或 source map uploads 同步。Release 名称是字符串,但是 Sentry 会检测到某些格式,并且它们的呈现方式可能有所不同。
更多:https://docs.sentry.io/product/releases/
默认情况下,SDK 会尝试从环境变量 SENTRY_RELEASE 中读取该值(在浏览器 SDK 中,将从 window.SENTRY_RELEASE 中读取该值,如果可用)。
environment
设置环境。此字符串为自由形式,默认情况下不设置。一个 release 可以与多个环境相关联,以便在 UI 中将它们分开(可以考虑staging 与 prod 或类似的方式)。
默认情况下,SDK 将尝试从 SENTRY_ENVIRONMENT 环境变量中读取该值(浏览器 SDK 除外)。
tunnel
设置将用于传输捕获事件的 URL,而不是使用 DSN。这可用于解决广告拦截器(ad-blockers)或对发送到 Sentry 的事件进行更精细的控制。此选项需要实现自定义服务器端点。
更多:https://docs.sentry.io/platforms/javascript/troubleshooting/#dealing-with-ad-blockers
sampleRate
配置错误事件的采样率,范围为 0.0 到 1.0。默认值为 1.0,表示发送了 100% 的错误事件。如果设置为 0.1,则仅发送 10% 的错误事件。事件是随机选择的。
maxBreadcrumbs
这个变量控制应该捕获的面包屑总数。默认值为 100。
attachStacktrace
当启用时,堆栈跟踪将自动附加到所有记录的消息。堆栈跟踪总是附加到异常;然而,当设置此选项时,堆栈跟踪也会与消息一起发送。例如,该选项意味着堆栈跟踪显示在所有日志消息的旁边。
该选项默认为 off。
Sentry 中的分组对于有和没有堆栈跟踪的事件是不同的。因此,当您为某些事件启用或禁用此 flag 时,您将获得新组。
denyUrls
与不应该发送到 Sentry 的错误 URL 相匹配的字符串或正则表达式模式列表。默认情况下,将发送所有错误。这是一个 “contains(包含)” 匹配整个文件 URL。因此,如果你添加 foo.com,它也会匹配 https://bar.com/myfile/foo.com。默认情况下,将发送所有错误。
allowUrls
匹配错误 URL 的字符串列表或正则表达式模式的遗留别名,这些错误 URL 应该专门发送给 Sentry。默认情况下,将发送所有错误。这是一个 “contains(包含)” 匹配整个文件 URL。因此,如果您将 foo.com 添加到它,它也将匹配 https://bar.com/myfile/foo.com。默认情况下,所有错误将被发送。
autoSessionTracking
设置为 true 时,SDK 将向 Sentry 发送 session 事件。这在所有浏览器 SDK 中都受支持,每个页面加载和页面导航都向 Sentry 发出一个 session。在移动 SDK 中,当应用进入后台超过 30 秒时,会话结束。
initialScope
要设置为初始作用域的数据。初始作用域可以定义为对象或回调函数,如下所示。
对象:
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- debug: true,
- initialScope: {
- tags: {"my-tag": "my value"},
- user: {id: 42, email: "john.doe@example.com"},
- }
- });
回调函数:
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- debug: true,
- initialScope: scope => {
- scope.setTags({ a: 'b' });
- return scope;
- },
- });
maxValueLength
单个值在被截断之前可以具有的最大字符数(默认为 250)。
normalizeDepth
Sentry SDK 将任何上下文数据标准化到给定的深度。任何包含结构比这更深的数据的 key 都将使用其类型([Object] 或 [Array])进行修剪和标记,而不会进一步遍历树。默认情况下,步行执行 3 级深度。
集成配置
对于许多平台,SDK 集成可以与之一起配置。在一些平台上,这是 init() 调用的一部分,而在另一些平台上,则应用不同的模式。
integrations
在某些 SDK 中,在库初始化时通过此参数配置集成。
更多:
defaultIntegrations
这可以用来禁用默认添加的集成。当设置为 false 时,不会添加默认的集成。
Hooks
这些选项可用于以各种方式 hook SDK,以定制事件的报告。
beforeSend
使用 SDK-specific 事件对象调用此函数,可以返回修改后的事件对象或不返回任何内容,以跳过报告事件。例如,这可以用于在发送前手动剥离 PII。
beforeBreadcrumb
在将面包屑添加到作用域之前,使用 SDK 特定的面包屑(SDK-specific breadcrumb)对象调用此函数。当该函数未返回任何内容时,将删除 breadcrumb。要传递 breadcrumb,请返回第一个参数,其中包含 breadcrumb 对象。回调通常会获得第二个参数(称为“hint”),该参数包含创建 breadcrumb 的原始对象,以进一步自定义面包屑的外观。
传输选项
Transports 被用来发送事件到 Sentry。可以在某种程度上对传输进行定制,以更好地支持高度特定的部署。
transport
切换出用于发送事件的 transport。如何运作取决于 SDK。例如,它可以用于捕获事件以进行单元测试,或通过需要代理身份验证的更复杂的设置发送事件。
跟踪选项
tracesSampleRate
0 到 1 之间的数字,控制给定事务发送到 Sentry 的概率百分比。(0 表示 0%,1 表示 100%)同样适用于应用程序中创建的所有事务。必须定义这个或 tracesSampler 以启用跟踪。
tracesSampler
一个函数负责确定一个给定的事务将被发送到 Sentry 的概率百分比。它将自动被传递有关事务和创建它的上下文的信息,并且必须返回一个介于 0(被发送的概率为 0%)和 1(被发送的概率为 100%) 之间的数字。还可以用于过滤事务,对不需要的事务返回 0。必须定义这个或 tracesSampleRate 来启用跟踪。
集成
默认集成
Sentry 的所有 SDK 都提供集成,可扩展 SDK 的功能。
默认情况下启用系统集成以集成到标准库或解释器本身。它们被记录在案,因此您既可以了解它们的作用,也可以在它们引起问题时禁用它们。
默认启用
InboundFilters
Import name: Sentry.Integrations.InboundFilters
通过这种集成,您可以根据给定异常中的类型,消息或 URL 忽略特定错误。
默认情况下,它忽略以 Script error 或 Javascript error: Script error 开头的错误。
要配置这个集成,直接使用 ignoreErrors,denyUrls,和 allowUrls SDK 选项。请记住,denyURL 和 allowURL 只对捕获的异常有效,而不是原始消息事件。
FunctionToString
Import name: Sentry.Integrations.FunctionToString
这种集成使 SDK 可以提供原始的函数和方法名称,即使我们的错误(error)或面包屑处理程序(breadcrumbs handlers)包装了它们也是如此。
TryCatch
Import name: Sentry.Integrations.TryCatch
这个集成封装了原生 time 和 events APIs (setTimeout, setInterval, requestAnimationFrame, addEventListener/removeEventListener) 在 try/catch 块处理 async 异常。
Breadcrumbs
Import name: Sentry.Integrations.Breadcrumbs
这种集成封装了原生 API 以捕获面包屑。默认情况下,Sentry SDK 封装了所有 API。
可用选项:
- {
- // 记录对 `console.log`、`console.debug` 等的调用
- console: boolean;
- // 记录所有点击和按键事件
- // - 当提供带有 `serializeAttribute` key 的对象时,
- // 面包屑集成将在 DOM 元素中查找给定的属性,同时生成面包屑路径。
- // 匹配的元素后跟它们的自定义属性,而不是它们的 `id` 或 `class` 名称。
- dom: boolean | { serializeAttribute: string | string[] };
- // 记录使用 `Fetch API` 完成的 `HTTP` 请求
- fetch: boolean;
- // 记录对 `history.pushState` 的调用
- history: boolean;
- // 每当我们向服务器发送事件时记录
- sentry: boolean;
- // 记录使用 XHR API 完成的 HTTP 请求
- xhr: boolean;
- }
GlobalHandlers
Import name: Sentry.Integrations.GlobalHandlers
这个集成附加了全局处理程序来捕获未捕获的 exceptions 和未处理的 rejections。
可用的选项:
- {
- onerror: boolean;
- onunhandledrejection: boolean;
- }
LinkedErrors
Import name: Sentry.Integrations.LinkedErrors
此集成允许您配置 linked 错误。它们将被递归地读取到指定的限制,并由特定的 key 执行查找。默认情况下,Sentry SDK 将限制设置为 5,使用的键 key 是 cause。
可用的选项:
- {
- key: string;
- limit: number;
- }
这是如何实现的代码示例:
- document
- .querySelector("#get-reviews-btn")
- .addEventListener("click", async event => {
- const movie = event.target.dataset.title;
- try {
- const reviews = await fetchMovieReviews(movie);
- renderMovieReviews(reviews);
- } catch (e) {
- const fetchError = new Error(`Failed to fetch reviews for: ${movie}`);
- fetchError.cause = e;
- Sentry.captureException(fetchError);
- renderMovieReviewsError(fetchError);
- }
- });
UserAgent
Import name: Sentry.Integrations.UserAgent
这种集成将 user-agent 信息附加到事件中,这使我们能够正确地分类并使用特定的操作系统(OS),浏览器(browser)和版本(version)信息对其进行标记。
Dedupe
Import name: Sentry.Integrations.Dedupe
这种集成消除了某些事件的重复数据。如果您收到许多重复的错误,这会很有帮助。请注意,Sentry 只会比较堆栈跟踪(stack traces)和指纹(fingerprints)。默认情况下为浏览器启用此集成。
- import * as Sentry from "@sentry/browser";
- import { Dedupe as DedupeIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new DedupeIntegration()],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/dedupe.min.js"
- integrity="sha384-3IMGY+DN27Yns7KDiKL3sOWXBYlILQ/bxLogt02NG7DL7qEJHIMbpnXfqNlO0J8G"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new Dedupe()],
- });
修改系统集成
要禁用系统集成,请在调用 init() 时设置 defaultIntegrations: false。
要覆盖它们的设置,请提供一个带有您的配置到集成选项的新实例。例如,要关闭浏览器捕获控制台调用:integrations: [new Sentry.Integrations.Breadcrumbs({ console: false })]。
删除集成
此示例删除了用于向事件添加面包屑的默认启用集成:
- Sentry.init({
- // ...
- integrations: function(integrations) {
- // integrations will be all default integrations
- return integrations.filter(function(integration) {
- return integration.name !== "Breadcrumbs";
- });
- },
- });
可插拔集成
这些可插拔的集成是为特定的应用程序和/或框架增加功能的代码片段。我们对它们进行了记录,这样您就可以看到它们的功能,并且可以启用它们。
如何启用
安装 @sentry/integrations 包,并提供一个带有你配置到 integrations 选项的新实例。加载 SDK 之后,包括插件。
示例:
- import * as Sentry from "@sentry/browser";
- import { ReportingObserver as ReportingObserverIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new ReportingObserverIntegration()],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/reportingobserver.min.js"
- integrity="sha384-20D83MPBNSRANJFguhj0o9Qo7p9MCemwdMMQXotwA8742WuIwga85k+T7qEgIMWK"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new ReportingObserver()],
- });
ExtraErrorData
Import name: Sentry.Integrations.ExtraErrorData
这个集成从错误对象中提取所有非原生(non-native)属性,并将它们作为 extra 数据附加到事件中。
可用的选项:
- import * as Sentry from "@sentry/browser";
- import { ExtraErrorData as ExtraErrorDataIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new ExtraErrorDataIntegration(
- {
- // limit of how deep the object serializer should go. Anything deeper than limit will
- // be replaced with standard Node.js REPL notation of [Object], [Array], [Function] or
- // a primitive value. Defaults to 3.
- depth: number;
- }
- )],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/extraerrordata.min.js"
- integrity="sha384-DMO/ZWwA4ztkOtskx1Uad3cH6lbfSA/PGdW2IZ7A/c2qd/BU6zh5xiJ5D4nxJbye"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new ExtraErrorData(
- {
- // limit of how deep the object serializer should go. Anything deeper than limit will
- // be replaced with standard Node.js REPL notation of [Object], [Array], [Function] or
- // a primitive value. Defaults to 3.
- depth: number;
- }
- )],
- });
CaptureConsole
Import name: Sentry.Integrations.CaptureConsole
这种集成捕获所有的 Console API 调用,并使用 captureMessage 调用将它们重定向到 Sentry。然后,它会重新触发以保留默认的原生行为。
- import * as Sentry from "@sentry/browser";
- import { CaptureConsole as CaptureConsoleIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new CaptureConsoleIntegration(
- {
- // array of methods that should be captured
- // defaults to ['log', 'info', 'warn', 'error', 'debug', 'assert']
- levels: string[];
- }
- )],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/captureconsole.min.js"
- integrity="sha384-FJ5n80A08NroQF9DJzikUUhiCaQT2rTIYeJyHytczDDbIiejfcCzBR5lQK4AnmVt"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new CaptureConsole(
- {
- // array of methods that should be captured
- // defaults to ['log', 'info', 'warn', 'error', 'debug', 'assert']
- levels: string[];
- }
- )],
- });
Debug
Import name: Sentry.Integrations.Debug
通过这种集成,您可以检查已处理事件的内容,该事件将被传递到 beforeSend 并有效地发送到 Sentry SDK。无论何时注册,它都将始终作为最后的集成运行。
可用的选项:
- import * as Sentry from "@sentry/browser";
- import { Debug as DebugIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new DebugIntegration(
- {
- // trigger DevTools debugger instead of using console.log
- debugger: boolean;
- // stringify event before passing it to console.log
- stringify: boolean;
- }
- )],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/debug.min.js"
- integrity="sha384-OIzIETBTnmaXcnCVlI4DzHq1+YxDdBS6uyZPp8yS60YZNUqzIQvrudJplBqEZ09K"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new Debug(
- {
- // trigger DevTools debugger instead of using console.log
- debugger: boolean;
- // stringify event before passing it to console.log
- stringify: boolean;
- }
- )],
- });
Offline
Import name: Sentry.Integrations.Offline
此集成使用 Web 浏览器的在线和离线事件来检测何时没有可用的网络连接。如果离线,它会将事件保存到 Web 浏览器的客户端存储(通常是 IndexedDB),然后在网络连接恢复时自动上传事件。
Online and offline events
https://developer.mozilla.org/en-US/docs/Web/API/Navigator/Online_and_offline_events
此插件不会尝试为其他场景提供本地存储或重试。例如,如果浏览器有本地连接但没有互联网连接,那么它可能会报告它在线,并且在这种情况下,Sentry 的 Offline 插件不会尝试保存或重试任何发送失败。
- import * as Sentry from "@sentry/browser";
- import { Offline as OfflineIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new OfflineIntegration(
- {
- // limit how many events will be localled saved. Defaults to 30.
- maxStoredEvents: number;
- }
- )],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/offline.min.js"
- integrity="sha384-rRq5WRQ3OncIj4lduaVZMtyfVwZnqeWXM0nXyXckOrhFLS2mlKEYX+VAlbLlIZL4"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new Offline(
- {
- // limit how many events will be localled saved. Defaults to 30.
- maxStoredEvents: number;
- }
- )],
- });
RewriteFrames
Import name: Sentry.Integrations.RewriteFrames
这种集成允许您对堆栈跟踪的每个帧应用转换。在流线型(streamlined)场景中,它可用于更改其来源的文件帧的名称,或者可以使用迭代函数为其提供任意变换。
在 Windows 机器上,您必须使用 Unix 路径并跳过 root 选项中的卷号才能启用。例如 C:\\Program Files\\Apache\\www 将不起作用,但是 /Program Files/Apache/www 将起作用。
可用的选项:
- import * as Sentry from "@sentry/browser";
- import { RewriteFrames as RewriteFramesIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new RewriteFramesIntegration(
- {
- // root path that will be stripped from the current frame's filename by the default iteratee if the filename is an absolute path
- root: string;
- // a custom prefix that will be used by the default iteratee (default: `app://`)
- prefix: string;
- // function that takes the frame, applies a transformation, and returns it
- iteratee: (frame) => frame;
- }
- )],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/rewriteframes.min.js"
- integrity="sha384-WOm9k3kzVt1COFAB/zCXOFx4lDMtJh/2vmEizIwgog7OW0P/dPwl3s8f6MdwrD7q"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new RewriteFrames(
- {
- // root path that will be stripped from the current frame's filename by the default iteratee if the filename is an absolute path
- root: string;
- // a custom prefix that will be used by the default iteratee (default: `app://`)
- prefix: string;
- // function that takes the frame, applies a transformation, and returns it
- iteratee: (frame) => frame;
- }
- )],
- });
使用示例:
例如,如果文件的完整路径是 /www/src/app/file.js:
ReportingObserver
Import name: Sentry.Integrations.ReportingObserver
此集成挂钩到 ReportingObserver API 并将捕获的事件发送到 Sentry。它可以配置为仅处理特定的 issue 类型。
可用的选项:
- import * as Sentry from "@sentry/browser";
- import { ReportingObserver as ReportingObserverIntegration } from "@sentry/integrations";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new ReportingObserverIntegration(
- {
- types: <'crash'|'deprecation'|'intervention'>[];
- }
- )],
- });
CDN
- <script
- src="https://browser.sentry-cdn.com/6.12.0/bundle.min.js"
- integrity="sha384-S3qfdh3AsT1UN84WIYNuOX9vVOoFg3nB17Jp5/pTFGDBGBt+dtz7MGAV845efkZr"
- crossorigin="anonymous"
- ></script>
- <script
- src="https://browser.sentry-cdn.com/6.12.0/reportingobserver.min.js"
- integrity="sha384-20D83MPBNSRANJFguhj0o9Qo7p9MCemwdMMQXotwA8742WuIwga85k+T7qEgIMWK"
- crossorigin="anonymous"
- ></script>
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [new ReportingObserver(
- {
- types: <'crash'|'deprecation'|'intervention'>[];
- }
- )],
- });
React Router 集成
(适用于 5.21.0 及以上版本)
自 5.21.0 版起,@sentry/react 包中包含 React Router 支持。
- 注意:
- React Router 集成旨在与我们的跟踪 SDK @sentry/tracing 一起使用。有关如何设置和安装 SDK 的更多详细信息,请参阅 React Performance 入门。
- https://docs.sentry.io/platforms/javascript/guides/react/performance/
我们支持 React Router 3、4 和 5 的集成。
React Router v4/v5
要使用 router integration,请使用自定义 history 导入和设置自定义路由检测。确保将 Router 组件与 createBrowserHistory(或等效的)结合使用。
- import { Router } from 'react-router-dom';
- import { createBrowserHistory } from 'history';
- import * as Sentry from '@sentry/react';
- import { Integrations } from '@sentry/tracing';
- const history = createBrowserHistory();
- Sentry.init({
- integrations: [
- new Integrations.BrowserTracing({
- // Can also use reactRouterV3Instrumentation or reactRouterV4Instrumentation
- routingInstrumentation: Sentry.reactRouterV5Instrumentation(history),
- }),
- ],
- // We recommend adjusting this value in production, or using tracesSampler
- // for finer control
- tracesSampleRate: 1.0,
- });
- // ...
- // In your App render:
- render() {
- return (
- // Use custom history with a Router component
- <Router history={history}>
- <Components />
- </Router>
- );
- }
现在您应该使用 react router 检测从 BrowserTracing 集成生成 pageload/navigation 事务。
参数化事务名称
要获取参数化的交易名称(例如 /teams/:teamid/user/:userid 而不是 /teams/123/user/345),您必须授予 SDK 访问路由渲染匹配路径的权限。这是因为 SDK 没有可以在 React Router v4/v5 中使用的静态路由配置。有两种方法可以实现这一点:
1.传递路由配置对象
您可以根据 react-router-config 传递一组路由配置对象。到检测函数调用。您还需要提供从 react-router-dom 或 react-router 包导出的 matchPath 函数。
https://github.com/remix-run/react-router/tree/main/packages/react-router-config
- import { Route, Router, Switch, matchPath } from 'react-router-dom';
- import { createBrowserHistory } from 'history';
- import * as Sentry from '@sentry/react';
- import { Integrations } from '@sentry/tracing';
- const history = createBrowserHistory();
- // Array of Route Config Objects
- // Make sure the order of the routes is correct. The longest url under the same parent should be placed first and in decreasing order.
- const routes = [{ path: '/users/:userid' }, { path: '/users' }, { path: '/' }];
- Sentry.init({
- integrations: [
- new Integrations.BrowserTracing({
- routingInstrumentation: Sentry.reactRouterV5Instrumentation(history, routes, matchPath),
- }),
- ],
- // We recommend adjusting this value in production, or using tracesSampler
- // for finer control
- tracesSampleRate: 1.0,
- });
- // In your App render:
- render() {
- return (
- <Router history={history}>
- <Switch>
- <Route path="/users/:userid" component={() => <div>UserId</div>} />
- <Route path="/users" component={() => <div>Users</div>} />
- <Route path="/" component={() => <div>Home</div>} />
- </Switch>
- </Router>
- );
- }
2.使用 Sentry Route 组件
使用 withSentryRouting 高阶组件创建一个 SentryRoute 组件,该组件将在渲染时更新匹配路径。
- import {Route, Router, Switch } from 'react-router-dom';
- import { createBrowserHistory } from 'history';
- import * as Sentry from '@sentry/react';
- import { Integrations } from '@sentry/tracing';
- // Create Custom Sentry Route component
- const SentryRoute = Sentry.withSentryRouting(Route);
- const history = createBrowserHistory();
- Sentry.init({
- integrations: [
- new Integrations.BrowserTracing({
- routingInstrumentation: Sentry.reactRouterV5Instrumentation(history),
- }),
- ],
- // We recommend adjusting this value in production, or using tracesSampler
- // for finer control
- tracesSampleRate: 1.0,
- });
- render() {
- return (
- <Router history={history}>
- <Switch>
- <SentryRoute path="/users/:userid" component={() => <div>UserId</div>} />
- <SentryRoute path="/users" component={() => <div>Users</div>} />
- <SentryRoute path="/" component={() => <div>Home</div>} />
- </Switch>
- </Router>
- );
- }
React Router v3
要使用 router integration,请导入并设置自定义路由工具并将 history、您的 routes 和 match 函数传递给它。React Router v3 对 React Router >= 3.2.0 和 < 4.0.0 的支持保持不变。
Redux 集成
(5.20.0 及以上版本可用)
自 5.20.0 版起,Redux 支持包含在 @sentry/react 包中。要将 Sentry 应用于 Redux,请在初始化 Redux store 的同一位置使用 Sentry.createReduxEnhancer。
- import { createStore, compose } from "redux";
- import * as Sentry from "@sentry/react";
- // ...
- const sentryReduxEnhancer = Sentry.createReduxEnhancer({
- // Optionally pass options listed below
- });
- const store = createStore(rootReducer, sentryReduxEnhancer);
- // ...
如果您有其他增强器或中间件,例如 thunk:
- const store = createStore(
- rootReducer,
- compose(applyMiddleware(thunk), sentryReduxEnhancer)
- );
- 注意:
- Sentry 使用 redux enhancer。通过 enhancer,如上所示。
- 不要将它传递给 applyMiddleware 并且在将它传递给 createStore 方法时不要调用该方法。
Normalization 深度
默认情况下,Sentry SDK 将任何上下文规范化为 3 的深度,在发送 Redux 状态的情况下,您可能希望增加该深度。您可以通过将 normalizeDepth 传递给 Sentry.init 调用来实现:
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- normalizeDepth: 10, // Or however deep you want your state context to be.
- });
Redux Enhancer 选项
将选项对象作为第一个参数传递给 Sentry.createReduxEnhancer 以对其进行配置。
注意:
我们建议不要向 Sentry 发送敏感信息,但如果您这样做,我们将尽力过滤掉诸如用户密码之类的内容。
actionTransformer (Function)
用于从 action 中删除敏感信息。传递给函数的第一个参数是 Redux action。返回 null 以不向 Sentry 发送操作。默认情况下,我们发送所有 action。
- const sentryReduxEnhancer = Sentry.createReduxEnhancer({
- actionTransformer: action => {
- if (action.type === "GOVERNMENT_SECRETS") {
- // Return null to not log the action to Sentry
- return null;
- }
- if (action.type === "SET_PASSWORD") {
- // Return a transformed action to remove sensitive information
- return {
- ...action,
- password: null,
- };
- }
- return action;
- },
- });
stateTransformer (Function)
用于从 state 中删除敏感信息。传递给函数的第一个参数是 Redux state。返回 null 以不将 state 附加到发送给 Sentry 的事件。请注意,如果您选择不向 Sentry 发送 state,您的错误可能没有附加最新版本的 state。默认情况下,我们附加所有 state 更改。
- const sentryReduxEnhancer = Sentry.createReduxEnhancer({
- stateTransformer: state => {
- if (state.topSecret.doNotSend) {
- // Return null to not send this version of the state.
- return null;
- }
- // Transform the state to remove sensitive information
- const transformedState = {
- ...state,
- topSecret: {
- ...state.topSecret,
- // Replace sensitive information with something else
- nuclearLaunchCodes: "I love pizza",
- // or just remove it entirely
- hiddenTreasureLocation: null,
- },
- // You should also remove large data that is irrelevant to debugging to not clutter your Sentry issues
- giganticState: null,
- };
- return transformedState;
- },
- });
configureScopeWithState (Function)
在每次 state 更新时调用,使用 Redux state 配置 Sentry Scope。第一个参数是作用域,与调用 Sentry.configureScope 时得到的作用域实例相同,第二个参数是最新的 Redux state。
- const sentryReduxEnhancer = Sentry.createReduxEnhancer({
- configureScopeWithState: (scope, state) => {
- // Set tag if the user is using imperial units.
- if (state.settings.useImperialUnits) {
- scope.setTag("user.usesImperialUnits", true);
- }
- },
- });
自定义集成
使用以下格式向 JavaScript 添加自定义集成:
- // All integration that come with an SDK can be found on Sentry.Integrations object
- // Custom integration must conform Integration interface: https://github.com/getsentry/sentry-javascript/blob/master/packages/types/src/integration.ts
- Sentry.init({
- // ...
- integrations: [new MyAwesomeIntegration()],
- });
rrweb:Session 重播
Sentry 提供了与 rrweb 的概念验证集成 - 一个用于记录和重放用户会话的工具包。这在诊断丰富的单页应用程序中的复杂用户行为时非常有用。
更多信息:
https://www.rrweb.io/
https://docs.sentry.io/platforms/javascript/guides/react/configuration/filtering/#using-hints
https://docs.sentry.io/platforms/javascript/guides/react/enriching-events/attachments/
配置
要开始,您需要添加 @sentry/rrweb 和 rrweb 包:
- npm install --save @sentry/rrweb rrweb
接下来注册与 Sentry SDK 的集成。这将根据您使用的框架而有所不同:
- // If you're using one of our integration packages, like `@sentry/react` or
- // `@sentry/angular`, substitute its name for `@sentry/browser` here
- import * as Sentry from "@sentry/browser";
- import SentryRRWeb from "@sentry/rrweb";
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations: [
- new SentryRRWeb({
- // ...options
- }),
- ],
- // ...
- });
捕获事件的重播后,您会在事件的“重播(Replay)”部分下的“问题详细信息(Issue Details)”中找到它。
- 更多:https://github.com/getsentry/sentry-rrweb
采样
为了满足您组织的需求,您可能更喜欢对回放进行采样。最简单的方法是在初始化 Sentry SDK 时做出采样决定。例如,以下是 Sentry 本身如何使用抽样来仅为员工捕获这些信息:
- const hasReplays = getCurrentUser().isStaff;
- let integrations = [];
- if (hasReplays) {
- console.log("[sentry] Instrumenting session with rrweb");
- integrations.push(new SentryRRWeb());
- }
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- integrations,
- });
- Sentry.setTag("rrweb.active", hasReplays ? "yes" : "no");
您会注意到我们还设置了 rrweb.active 标签,这有助于我们识别附加了重播(replay)的事件,否则我们将无法找到它们。配置完成后,您就可以在搜索查询中简单地使用 rrweb.active:yes。
Release & 运行状况
Release 是部署到环境中的代码版本。当您向 Sentry 提供有关您的版本的信息时,您可以:
- 确定新版本中引入的问题和回归
- 预测哪个提交导致了问题以及谁可能负责
- 通过在提交消息中包含问题编号来解决问题
- 部署代码时接收电子邮件通知
此外,release用于将 source maps 应用于被压缩的 JavaScript 以查看原始的、未转换的源代码。
绑定版本
配置客户端 SDK 时包含 release ID(通常称为“版本 version”)。
release 名称不能:
- 包含换行符、制表符、正斜杠 (/) 或反斜杠 (\)
- 是(全部)句号 (.)、双句号 (..) 或空格 ( )
- 超过 200 个字符
该值可以是任意的,但我们推荐以下任一命名策略:
语义版本控制: package@version 或 package@version+build(例如,my.project.name@2.3.12+1234)
- package 是 project/app 的唯一标识符(iOS 上的 CFBundleIdentifier,Android 上的 packageName)
- version 是类似于 semver 的结构
. - build 是标识 app 迭代的数字(iOS 上的 CFBundleVersion,Android 上的 versionCode)
Commit SHA: 如果您使用 DVCS,我们建议使用标识哈希 identifying hash(例如,commit SHA,da39a3ee5e6b4b0d3255bfef95601890afd80709)。您可以让 Sentry CLI 使用 sentry-clireleases proposal-version 为支持的版本控制系统自动确定此哈希值。
每个组织的发布都是全局性的;为它们添加特定于项目的前缀,以便于区分。
- Sentry.init({
- release: "my-project-name@2.3.12",
- });
在 Node/npm 环境中使用 JavaScript 执行此操作的一种常见方法是使用 process.env.npm_package_version ,如下所示:
- 更多:https://docs.npmjs.com/misc/scripts#packagejson-vars
- Sentry.init({
- release: "my-project-name@" + process.env.npm_package_version,
- });
您如何使版本(version)可用于您的代码取决于您。例如,您可以使用在构建过程中设置的环境变量。
这用 release 值标记每个事件。我们建议您在部署之前告诉 Sentry 一个新 release,因为这将解锁我们关于 releases 的文档中讨论的更多功能。但是,如果您不这样做,Sentry 将在第一次看到具有该 release ID 的事件时自动在系统中创建一个 release 实体。
- release:https://docs.sentry.io/product/releases/
配置您的 SDK 后,您可以安装 repository integration 或手动为 Sentry 提供您自己的 commit metadata。阅读我们关于设置 releases 的文档,以获取有关集成 integrations、关联提交 associating commits以及在部署 releases 时通知 Sentry 的更多信息。
- 设置 releases: https://docs.sentry.io/product/releases/setup/
Release 运行状况
通过观察用户采用情况、应用程序使用情况、崩溃百分比和会话数据来监控 release 的运行状况。 release 运行状况将深入了解与用户体验相关的崩溃和错误的影响,并通过 release 详细信息、图表和过滤器揭示每个新问题的趋势。
- health of releases : https://docs.sentry.io/product/releases/health/
- crashes:https://docs.sentry.io/product/releases/health/#crash
- session data:https://docs.sentry.io/product/releases/health/#session
SDK 将在 SDK 初始化时自动管理会话的开始和结束。
我们为每个页面加载创建一个会话。对于单页应用程序,我们将为每次导航更改(History API)创建一个新会话。
我们将会话标记为:
- 如果unhandled error 或 unhandled promise rejection 冒泡到全局处理程序,则崩溃。
- 如果 SDK 捕获包含异常的事件(这包括手动捕获的错误),则会出现错误。
要接收有关用户采用的数据,例如用户崩溃率百分比和采用特定版本的用户数,请在初始化 SDK 时将用户设置在 initialScope 上。
默认情况下,JavaScript SDK 正在发送会话。要禁用发送会话,请将 autoSessionTracking 标志设置为 false:
- Sentry.init({
- autoSessionTracking: false // default: true
- });
环境
Sentry 在收到带有 environment 标签的事件时会自动创建环境。环境区分大小写。环境名称不能包含换行符、空格或正斜杠,不能是字符串“None”或超过 64 个字符。您无法删除环境,但可以隐藏它们。
- hidden-environments: https://docs.sentry.io/product/sentry-basics/environments/#hidden-environments
- Sentry.init({
- environment: "production",
- });
环境可帮助您在 sentry.io 的问题详细信息页面中更好地过滤问题、版本和用户反馈,您可以在我们涵盖使用环境的文档中了解更多信息。
- environments:https://docs.sentry.io/product/sentry-basics/environments/
过滤
将 Sentry 添加到您的应用程序可为您提供大量关于错误和性能的非常有价值的信息,否则您将无法获得这些信息。大量的信息是好的——只要它是正确的信息,并且数量合理。
Sentry SDK 有几个配置选项可以帮助您过滤事件。
我们还提供入站过滤器 Inbound Filters来过滤 sentry.io 中的事件。不过,我们建议在客户端级别进行过滤,因为它消除了发送您实际上不想要的事件的开销。了解有关事件中可用字段的更多信息。
- Inbound Filters: https://docs.sentry.io/product/data-management-settings/filtering/
- fields available in an event:https://develop.sentry.dev/sdk/event-payloads/
过滤错误事件
通过使用 beforeSend 回调方法和配置、启用或禁用集成来配置您的 SDK 以过滤错误事件。
使用 beforeSend
所有 Sentry SDK 都支持 beforeSend 回调方法。beforeSend 在事件发送到服务器之前立即调用,因此它是您可以编辑其数据的最后位置。它将事件对象作为参数接收,因此您可以使用该参数根据自定义逻辑和事件上可用的数据修改事件的数据或完全删除它(通过返回 null)。
- Sentry.init({
- // ...
- beforeSend(event, hint) {
- const error = hint.originalException;
- if (
- error &&
- error.message &&
- error.message.match(/database unavailable/i)
- ) {
- event.fingerprint = ["database-unavailable"];
- }
- return event;
- },
- });
还要注意,正如我们的 breadcrumbs 文档中所讨论的,breadcrumbs 可以被过滤。
- breadcrumbs: https://docs.sentry.io/product/error-monitoring/breadcrumbs/
Event Hints
before-send 回调传递 event 和第二个参数 hint,该参数包含一个或多个 hints。
通常,hint 保存原始异常,以便可以提取附加数据或影响分组。在本例中,如果捕获到某种类型的异常,指纹将被强制为一个公共值:
- Sentry.init({
- // ...
- beforeSend(event, hint) {
- const error = hint.originalException;
- if (
- error &&
- error.message &&
- error.message.match(/database unavailable/i)
- ) {
- event.fingerprint = ["database-unavailable"];
- }
- return event;
- },
- });
有关哪些 hints 可用的信息,请参阅:
- hints in JavaScript: https://docs.sentry.io/platforms/javascript/guides/react/configuration/filtering/#using-hints
当 SDK 创建用于传输(transmission)的事件或面包屑时,该传输通常是从某种源对象创建的。例如,错误事件通常是从日志记录或异常实例中创建的。为了更好地定制,SDK 将这些对象发送到某些回调(beforeSend、beforeBreadcrumb 或 SDK 中的事件处理器系统)。
使用 Hints
Hints 可在两个地方获得:
- beforeSend / beforeBreadcrumb
- eventProcessors
事件和面包屑 hints 是包含用于组合事件或面包屑的各种信息的对象。通常 hints 保存原始异常,以便可以提取附加数据或影响分组。
对于事件,例如 event_id、originalException、syntheticException(在内部用于生成更清晰的堆栈跟踪)以及您附加的任何其他任意数据。
对于面包屑,hints 的使用取决于实现。对于 XHR 请求,hint 包含 xhr 对象本身;对于用户交互,提示包含 DOM 元素和事件名称等。
在本例中,如果捕获到某种类型的异常,指纹将被强制为一个公共值:
- Sentry.init({
- // ...
- beforeSend(event, hint) {
- const error = hint.originalException;
- if (
- error &&
- error.message &&
- error.message.match(/database unavailable/i)
- ) {
- event.fingerprint = ["database-unavailable"];
- }
- return event;
- },
- });
Hints for Events
originalException
导致 Sentry SDK 创建事件的原始异常。这对于更改 Sentry SDK 分组事件的方式或提取附加信息很有用。
syntheticException
当引发字符串(string)或非错误(non-error)对象时,Sentry 会创建一个合成异常(synthetic exception),以便您可以获得基本的堆栈跟踪。此异常存储在此处以供进一步提取数据。
Hints for Breadcrumbs
event
对于从浏览器事件创建的面包屑,Sentry SDK 通常将事件作为 hint 提供给面包屑。例如,这可用于将目标 DOM 元素中的数据提取到面包屑中。
level / input
对于从控制台日志(console.log)拦截创建的面包屑。这保存了原始 console log level 和 log function 的原始输入数据。
response / input
对于从 HTTP 请求创建的面包屑。它保存响应对象(来自 fetch API)和 fetch 函数的输入参数。
request / response / event
对于从 HTTP 请求创建的面包屑。这包含请求和响应对象(来自 node HTTP API)以及 node event(response或error)。
xhr
对于通过遗留 XMLHttpRequest API 完成的 HTTP 请求创建的面包屑。这保存了原始的 xhr 对象。
整理 Sentry
您可以构建一个允许的域列表,这些域可能会引发可接受的异常。例如,如果您的脚本是从 cdn.example.com 加载的并且您的站点是 example.com,您可以将 allowUrls 设置为:
- Sentry.init({
- allowUrls: [
- /https?:\/\/((cdn|www)\.)?example\.com/
- ]
- });
如果您想永远阻止特定的 URL,您也可以使用 denyUrls。
- Note 在 5.17.0 版本之前,allowUrls 和 denyUrls 分别称为 whitelistUrls 和 blacklistUrls。出于向后兼容性的原因,这些选项仍受支持,但它们将在 6.0 版中删除。有关更多信息,请参阅
- Inclusive Language Policy:https://develop.sentry.dev/inclusion/
此外,我们的社区还为日常事务编制了一份常见的忽略规则列表,例如 Facebook、Chrome extensions 等。这很有用,建议您检查一下这些内容,看看它们是否适用于您。这不是我们 SDK 的默认值;这只是一个广泛示例的一个亮点。
- Here is the original gist:https://gist.github.com/impressiver/5092952
- Sentry.init({
- ignoreErrors: [
- // Random plugins/extensions
- "top.GLOBALS",
- // See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error.html
- "originalCreateNotification",
- "canvas.contentDocument",
- "MyApp_RemoveAllHighlights",
- "http://tt.epicplay.com",
- "Can't find variable: ZiteReader",
- "jigsaw is not defined",
- "ComboSearch is not defined",
- "http://loading.retry.widdit.com/",
- "atomicFindClose",
- // Facebook borked
- "fb_xd_fragment",
- // ISP "optimizing" proxy - `Cache-Control: no-transform` seems to
- // reduce this. (thanks @acdha)
- // See http://stackoverflow.com/questions/4113268
- "bmi_SafeAddOnload",
- "EBCallBackMessageReceived",
- // See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
- "conduitPage",
- ],
- denyUrls: [
- // Facebook flakiness
- /graph\.facebook\.com/i,
- // Facebook blocked
- /connect\.facebook\.net\/en_US\/all\.js/i,
- // Woopra flakiness
- /eatdifferent\.com\.woopra-ns\.com/i,
- /static\.woopra\.com\/js\/woopra\.js/i,
- // Chrome extensions
- /extensions\//i,
- /^chrome:\/\//i,
- // Other plugins
- /127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
- /webappstoolbarba\.texthelp\.com\//i,
- /metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
- ],
- });
使用采样过滤 Transaction 事件
为了防止某些 transactions 被报告给 Sentry,请使用 tracesSampler 配置选项,它允许您提供一个函数来评估当前 transaction 并在它不是您想要的时候删除它。(它还允许您以不同的采样率对不同的 transaction 进行抽样。)
注意: tracesSampler 和 tracesSampleRate 配置选项是互斥的。如果您定义了一个 tracesSampler 来过滤掉某些 transaction,您还必须通过返回您希望对它们进行采样的速率来处理未过滤 transaction 的情况。
最简单的形式,仅用于过滤 transaction,它看起来像这样:
- Sentry.init({
- // ...
- tracesSampler: samplingContext => {
- if ("...") {
- // Drop this transaction, by setting its sample rate to 0%
- return 0;
- } else {
- // Default sample rate for all others (replaces tracesSampleRate)
- return 0.1;
- }
- };
- });
关闭与清空
大多数 SDK 的默认行为是在后台通过网络异步发送事件。这意味着如果应用程序意外关闭,某些事件可能会丢失。SDK 提供了处理这种情况的机制。
close 方法可选地接受以毫秒为单位的 timeout,并返回一个 promise,该 promise 在刷新所有挂起事件或 timeout 生效时 resolve。
- Sentry.close(2000).then(function() {
- // perform something after close
- });
调用 close 后,不能再使用当前客户端。仅在关闭应用程序之前立即调用 close 很重要。
或者,flush 方法清空事件队列,同时保持客户端启用以供继续使用。
采样
将 Sentry 添加到您的应用程序可为您提供大量关于错误和性能的非常有价值的信息,否则您将无法获得这些信息。大量的信息是好的——只要它是正确的信息,并且数量合理。
采样 Error 事件
要将具有代表性的错误样本发送到 Sentry,请将 SDK 配置中的 sampleRate 选项设置为 0(发送的错误的 0%)和 1(发送的错误的 100%)之间的数字。这是一个静态比率,它同样适用于所有错误。例如,要对 25% 的错误进行抽样:
- Sentry.init({ sampleRate: 0.25 });
- 更改错误采样率需要重新部署。此外,设置 SDK 采样率会限制对事件源的可见性。为您的项目设置速率限制(仅在 volume 高时丢弃事件)可能更适合您的需求。
采样 Transaction 事件
我们建议对您的 transaction 进行抽样,原因有两个:
- 捕获单个跟踪涉及的开销最小,但捕获每个页面加载或每个 API 请求的跟踪可能会给您的系统增加不必要的负载。
- 启用采样可以让您更好地管理发送到 Sentry 的事件数量,因此您可以根据组织的需求定制您的数量。
选择采样率的目标是在性能和数量问题与数据准确性之间找到平衡。您不想收集太多数据,但希望收集足够的数据以得出有意义的结论。如果您不确定要选择什么速率,请从一个较低的值开始,随着您对流量模式和流量的了解越来越多,逐渐增加它。
配置 Transaction 采样率
Sentry SDK 有两个配置选项来控制发送到 Sentry 的 transaction 量,让您可以获取具有代表性的样本:
1.统一采样率(tracesSampleRate):
- 提供均匀的事务横截面,无论它们在您的应用程序中的哪个位置或在什么情况下发生。
- 使用默认继承(inheritance)和优先(precedence)行为
2.采样函数(tracesSampler)其中:
- 以不同的速率采样不同的 transaction
- 完全过滤掉一些 transaction
- 修改默认优先级和继承行为
inheritance: https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/#inheritance
precedence: https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/#precedence
Filters: https://docs.sentry.io/platforms/javascript/guides/react/configuration/filtering/
设置统一采样率
为此,请将 Sentry.init() 中的 tracesSampleRate 选项设置为 0 到 1 之间的数字。设置此选项后,创建的每个 transaction 都有该百分比的机会被发送到 Sentry。(因此,例如,如果您将 tracesSampleRate 设置为 0.2,大约 20% 的 transaction 将被记录和发送。)看起来像这样:
- Sentry.init({
- // ...
- tracesSampleRate: 0.2,
- });
设置采样函数
要使用采样函数,请将 Sentry.init() 中的 tracesSampler 选项设置为一个函数,该函数将接受 samplingContext 对象并返回介于 0 和 1 之间的采样率。例如:
- Sentry.init({
- // ...
- tracesSampler: samplingContext => {
- // Examine provided context data (including parent decision, if any) along
- // with anything in the global namespace to compute the sample rate or
- // sampling decision for this transaction
- if ("...") {
- // These are important - take a big sample
- return 0.5;
- } else if ("...") {
- // These are less important or happen much more frequently - only take 1%
- return 0.01;
- } else if ("...") {
- // These aren't something worth tracking - drop all transactions like this
- return 0;
- } else {
- // Default sample rate
- return 0.1;
- }
- };
- });
为方便起见,该函数还可以返回一个布尔值。返回 true 等同于返回 1,并且将保证 transaction 将发送到 Sentry。返回 false 相当于返回 0,并保证 transaction 不会被发送到 Sentry。
采样 Context 数据
默认采样 Context 数据
在 transaction 事务时传递给 tracesSampler 的 SamplingContext 对象中包含的信息因平台和集成(integration)而异。
对于基于浏览器的 SDK,它至少包括以下内容:
- // contents of `samplingContext`
- {
- transactionContext: {
- name: string; // human-readable identifier, like "GET /users"
- op: string; // short description of transaction type, like "pageload"
- }
- parentSampled: boolean; // if this transaction has a parent, its sampling decision
- location: Location | WorkerLocation; // the window.location or self.location object
- ... // custom context as passed to `startTransaction`
- }
自定义采样 Context 数据
使用自定义检测创建 transaction 时,您可以通过将数据作为可选的第二个参数传递给 startTransaction 来将数据添加到 samplesContext。如果您希望采样器可以访问某些数据,但又不想将其作为标签(tag)或数据(data)附加到 transaction 中,例如敏感信息或太大而无法与 transaction 一起发送的信息,这将非常有用。例如:
- Sentry.startTransaction(
- {
- // `transactionContext` - will be recorded on transaction
- name: 'Search from navbar',
- op: 'search',
- tags: {
- testGroup: 'A3',
- treatmentName: 'eager load',
- },
- },
- // `customSamplingContext` - won't be recorded
- {
- // PII
- userId: '12312012',
- // too big to send
- resultsFromLastSearch: { ... }
- },
- );
继承
无论 transaction 的抽样决策如何,该决策都将传递到其子跨度,并从那里传递到它们随后在其他服务中引起的任何 transaction。(有关如何完成传播的更多信息,请参阅连接服务。)
- Connecting Services: https://docs.sentry.io/platforms/javascript/performance/
如果当前正在创建的 transaction 是那些后续事务之一(换句话说,如果它有父 transaction),则上游(父)采样决策将始终包含在采样上下文数据中,以便您的 tracesSampler 可以选择是否和何时继承该决策。(在大多数情况下,继承是正确的选择,以避免部分跟踪痕迹。)
在某些 SDK 中,为了方便起见,tracesSampler 函数可以返回一个布尔值,这样如果这是所需的行为,则可以直接返回父级的决策。
- tracesSampler: samplingContext => {
- // always inherit
- if (samplingContext.parentSampled !== undefined) {
- return samplingContext.parentSampled
- }
- ...
- // rest of sampling logic here
- }
如果您使用的是 tracesSampleRate 而不是 tracesSampler,则决策将始终被继承。
强制抽样决策
如果您在 transaction 创建时知道是否要将 transaction 发送到 Sentry,您还可以选择将采样决策直接传递给 transaction 构造函数(注意,不是在 customSamplingContext 对象中)。如果您这样做,transaction 将不受 tracesSampleRate 的约束,也不会运行 tracesSampler,因此您可以指望通过的决策不会被覆盖。
- Sentry.startTransaction({
- name: "Search from navbar",
- sampled: true,
- });
优先级
transaction 以多种方式结束抽样决策。
- 根据 tracesSampleRate 中设置的静态采样率随机采样
- 根据 tracesSampler 采样函数返回的采样率随机采样
- tracesSampler 返回的绝对决策(100% 机会或 0% 机会)
- 如果 transaction 有父级,继承其父级的抽样决策
- 绝对决策传递给 startTransaction
当有可能不止一个发挥作用时,以下优先规则适用:
- 如果将抽样决策传递给 startTransaction(请参阅上面的强制抽样决策),则将使用该决策,而不管其他任何事情
- 如果定义了 tracesSampler,则将使用其决策。它可以选择保留或忽略任何父采样决策,或使用采样上下文数据来做出自己的决策或为 transaction 选择采样率。
- 如果未定义 tracesSampler,但存在父采样决策,则将使用父采样决策。
- 如果未定义 tracesSampler 并且没有父采样决策,则将使用 tracesSampleRate。
Sentry Testkit
在为您的应用程序构建测试时,您希望断言正确的流跟踪(flow-tracking)或错误正在发送到 Sentry,但没有真正将其发送到 Sentry 服务器。这样您就不会在测试运行或其他 CI 操作期间用错误报告淹没 Sentry。
注意:Sentry 合作伙伴 Wix 维护 Sentry Testkit。
- Wix:https://wix.github.io/sentry-testkit/
Sentry Testkit 是一个 Sentry 插件,它允许拦截 Sentry 的 report 并进一步检查正在发送的数据。它使 Sentry 能够在您的应用程序中原生工作,并且通过覆盖默认 Sentry 的传输机制(transport mechanism),报告不会真正发送,而是本地记录到内存中。这样,您可以稍后获取记录的报告以供您自己使用、验证或您在本地开发/测试环境中可能拥有的任何其他用途。
- Sentry Testkit: https://wix.github.io/sentry-testkit/
安装
- npm install sentry-testkit --save-dev
在测试中使用
- const sentryTestkit = require("sentry-testkit");
- const { testkit, sentryTransport } = sentryTestkit();
- // initialize your Sentry instance with sentryTransport
- Sentry.init({
- dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
- transport: sentryTransport,
- //... other configurations
- });
- // then run any scenario that should call Sentry.catchException(...)
- expect(testkit.reports()).toHaveLength(1);
- const report = testkit.reports()[0];
- expect(report).toHaveProperty(/*...*/);
您也可以在 sentry-testkit 存储库的测试部分看到更多使用示例。
- testing section: https://github.com/wix/sentry-testkit/tree/master/test
Testkit API
Sentry Testkit 由一个非常简单直接的 API 组成。请参阅 Sentry Testkit Docs 中的完整 API 描述和文档。
Sentry Testkit Docs: https://wix.github.io/sentry-testkit/
用法
Sentry 的 SDK 与您的运行时环境挂钩,并根据平台自动报告错误、未捕获的异常和未处理的拒绝以及其他类型的错误。
关键术语:
- event 是向 Sentry 发送数据的一个实例。通常,此数据是错误(error)或异常(exception)。
- issue 是一组相似的事件。
- 事件的报告称为捕获(capturing)。当一个事件被捕获时,它被发送到 Sentry。
最常见的捕获形式是捕获错误。可以捕获为错误的内容因平台而异。一般来说,如果你有一些看起来像异常的东西,它可以被捕获。对于某些 SDK,您还可以省略 captureException 的参数,Sentry 将尝试捕获当前异常。它对于手动向 Sentry 报告错误或消息也很有用。
在捕获事件时,您还可以记录导致该事件的面包屑(breadcrumbs)。面包屑与事件不同:它们不会在 Sentry 中创建事件,而是会被缓冲,直到发送下一个事件。在我们的面包屑文档中了解有关面包屑的更多信息。
- breadcrumbs: https://docs.sentry.io/platforms/javascript/guides/react/enriching-events/breadcrumbs/
捕获 Errors
通过包含和配置 Sentry,我们的 React SDK 会自动附加全局处理程序(global handlers)来捕获未捕获的异常和未处理的 promise 拒绝,如官方 ECMAScript 6 标准中所述。您可以通过在 GlobalHandlers 集成中将 onunhandledrejection 选项更改为 false 并手动挂接到每个事件处理程序,然后直接调用 Sentry.captureException 或 Sentry.captureMessage 来禁用此默认行为。
您可以将 Error 对象传递给 captureException() 以将其捕获为事件。也可以传递非 Error(non-Error) 对象和字符串(string),但请注意 Sentry 中的结果事件(resulting events)可能会丢失堆栈跟踪。
- import * as Sentry from "@sentry/react";
- try {
- aFunctionThatMightFail();
- } catch (err) {
- Sentry.captureException(err);
- }
捕获 Messages
另一种常见的操作是捕获裸消息。消息是应该发送给 Sentry 的文本信息。通常不会发出消息,但它们对某些团队很有用。
- Sentry.captureMessage("Something went wrong");
设置 Level
级别 - 类似于日志级别 - 通常基于集成默认添加。您还可以在事件中覆盖它。
要设置超出范围的级别,您可以为每个事件调用 captureMessage():
- Sentry.captureMessage("this is a debug message", "debug");
要在作用域内设置级别,您可以调用 setLevel():
- Sentry.configureScope(function(scope) {
- scope.setLevel(Sentry.Severity.Warning);
- });
或每个事件:
- Sentry.withScope(function(scope) {
- scope.setLevel("info");
- Sentry.captureException("info");
- });
SDK 指纹
所有事件都有一个指纹。具有相同指纹的事件被组合成一个 issue。
默认情况下,Sentry 将运行一种内置分组算法,以根据事件中可用的信息(如堆栈跟踪stacktrace、异常exception和消息message)生成指纹。要扩展默认分组行为或完全更改它,您可以使用以下选项的组合:
1.在您的 SDK 中,使用 SDK 指纹识别,如下所述
2.在您的项目中,使用指纹规则或堆栈跟踪规则
- Fingerprint Rules: https://docs.sentry.io/product/data-management-settings/event-grouping/fingerprint-rules/
- Stack Trace Rules:https://docs.sentry.io/product/data-management-settings/event-grouping/stack-trace-rules/
在受支持的sdk中,您可以覆盖 Sentry 的默认分组,该分组将指纹属性作为字符串数组传递。指纹数组的长度不受限制。这类似于指纹规则功能,它总是可用的,可以实现类似的结果。
- fingerprint-rules:https://docs.sentry.io/product/data-management-settings/event-grouping/fingerprint-rules/
基本示例
在最基本的情况下,直接传递值:
- function makeRequest(method, path, options) {
- return fetch(method, path, options).catch(function(err) {
- Sentry.withScope(function(scope) {
- // group errors together based on their request and response
- scope.setFingerprint([method, path, String(err.statusCode)]);
- Sentry.captureException(err);
- });
- });
- }
您可以使用变量替换将动态值填充到通常在服务器上计算的指纹中。例如,可以添加值 {{ default }} 以将整个正常生成的分组哈希添加到指纹中。这些值与服务器端指纹识别相同。有关更多信息,请参阅:
- Variables: https://docs.sentry.io/product/data-management-settings/event-grouping/fingerprint-rules/#variables
以更大的粒度对错误进行分组
您的应用程序查询远程过程调用模型 (RPC) 接口或外部应用程序编程接口 (API) 服务,因此堆栈跟踪通常是相同的(即使传出请求非常不同)。
以下示例将进一步拆分 Sentry 将创建的默认组(由 {{ default }} 表示),并考虑到错误对象的一些属性:
- class MyRPCError extends Error {
- constructor(message, functionName, errorCode) {
- super(message);
- // The name of the RPC function that was called (e.g. "getAllBlogArticles")
- this.functionName = functionName;
- // For example a HTTP status code returned by the server.
- this.errorCode = errorCode;
- }
- }
- Sentry.init({
- ...,
- beforeSend: function(event, hint) {
- const exception = hint.originalException;
- if (exception instanceof MyRPCError) {
- event.fingerprint = [
- '{{ default }}',
- String(exception.functionName),
- String(exception.errorCode)
- ];
- }
- return event;
- }
- });
更进一步地分组错误
通用错误(例如数据库连接错误)具有许多不同的堆栈跟踪,并且永远不会组合在一起。
以下示例将通过从数组中省略 {{ default }} 来完全覆盖 Sentry 的分组:
- class DatabaseConnectionError extends Error {}
- Sentry.init({
- ...,
- beforeSend: function(event, hint) {
- const exception = hint.originalException;
- if (exception instanceof DatabaseConnectionError) {
- event.fingerprint = ['database-connection-error'];
- }
- return event;
- }
- });
Source Maps
生成 Source Maps
大多数现代 JavaScript 编译器都支持 source maps。以下是一些常用工具的说明。
我们建议使用 Sentry 的 Webpack 插件来配置 source maps 并在构建过程中自动上传它们。
- sentry-webpack-plugin: https://github.com/getsentry/sentry-webpack-plugin
- source-map-support
- 要依赖 Sentry 的 source map 解析,您的代码不能使用 source-map-support 包。该包以一种阻止我们的处理器正确解析它的方式覆盖捕获的堆栈跟踪。
- source-map-support:https://www.npmjs.com/package/source-map-support
Webpack
Sentry 提供了一个方便的 Webpack 插件,可以配置 source maps 并自动将它们上传到 Sentry。
要使用该插件,您首先需要安装它:
- npm install --save-dev @sentry/webpack-plugin
- // or
- yarn add --dev @sentry/webpack-plugin
然后,配置它webpack.config.js:
- const SentryWebpackPlugin = require("@sentry/webpack-plugin");
- module.exports = {
- // other webpack configuration
- devtool: 'source-map',
- plugins: [
- new SentryWebpackPlugin({
- // sentry-cli configuration - can also be done directly through sentry-cli
- // see https://docs.sentry.io/product/cli/configuration/ for details
- authToken: process.env.SENTRY_AUTH_TOKEN,
- org: "example-org",
- project: "example-project",
- release: process.env.SENTRY_RELEASE,
- // other SentryWebpackPlugin configuration
- include: ".",
- ignore: ["node_modules", "webpack.config.js"],
- }),
- ],
- };
此外,Webpack 插件会自动设置 window.SENTRY_RELEASE,因此您的 Sentry.init 调用不需要包含 release 值。
- 将 Webpack 插件设置为最后运行的插件;否则,插件收到的 source maps 可能不是最终的。
高级用法
如果您更喜欢手动上传 source maps,请配置 Webpack 去输出 source maps:
- module.exports = {
- devtool: 'source-map',
- output: {
- // Make maps auto-detectable by sentry-cli
- filename: "[name].js",
- sourceMapFilename: "[name].js.map",
- // Other `output` configuration
- },
- // Other webpack configuration
- };
如果您使用 SourceMapDevToolPlugin 对 source map 生成进行更细粒度的控制,请关闭 noSources,以便 Sentry 可以在事件堆栈跟踪中显示正确的源代码上下文。
- SourceMapDevToolPlugin:https://webpack.js.org/plugins/source-map-dev-tool-plugin
Rollup
您可以配置 Rollup 以生成 source maps,然后您可以使用 sentry-cli 上传 source maps:
- Rollup:https://rollupjs.org/
- upload using sentry-cli:https://docs.sentry.io/product/cli/releases/#sentry-cli-sourcemaps
- export default {
- entry: "./src/app.js",
- output: {
- file: "bundle.js",
- format: "cjs",
- sourceMap: true,
- },
- };
SystemJS
SystemJS 可以配置为输出 source maps,然后您可以使用 sentry-cli 上传 source maps:
- builder.bundle("src/app.js", "dist/app.min.js", {
- minify: true,
- sourceMaps: true,
- sourceMapContents: true,
- });
此示例配置将您的原始、未转换的源代码内联到生成的 source map 文件中。Sentry 需要 source map 和您的原始源文件来执行反向转换。如果您选择不内联源文件,则除了 source map 外,您还必须使这些源文件可供 Sentry 使用(见下文)。
- SystemJS:https://github.com/systemjs/builder
- upload using sentry-cli:https://docs.sentry.io/product/cli/releases/#sentry-cli-sourcemaps
TypeScript
TypeScript 编译器可以输出 source maps,然后您可以使用 sentry-cli 上传源映射。
将 sourceRoot 属性配置为 / 以从生成的源代码引用中去除构建路径前缀。这允许 Sentry 相对于您的源根文件夹匹配源文件:
- {
- "compilerOptions": {
- "sourceMap": true,
- "inlineSources": true,
- "sourceRoot": "/"
- }
- }
UglifyJS
我们强烈建议您使用更高级的打包器(或转译器),因为 UglifyJS 配置会变得非常复杂,并且很难达到预期的结果。
UglifyJS 可以配置为输出 source maps,然后您可以使用 sentry-cli 上传:
- uglifyjs app.js \
- -o app.min.js.map \
- --source-map url=app.min.js.map,includeSources
UglifyJS:https://github.com/mishoo/UglifyJS
上传 Source Maps
Webpack
Sentry 使用 releases 来将正确的 source maps 与您的事件相匹配。release API 旨在允许您在 Sentry 中存储源文件(和 source maps)。
您可以在我们的 Webpack 插件的帮助下完成此操作,该插件在内部使用我们的 Sentry CLI。
- 从您的 [Account] > API keys 创建一个新的身份验证令牌
- 确认您在“Scopes”下选择了 project:write
- 使用 npm 安装 @sentry/webpack-plugin
- 使用必要的配置创建 .sentryclirc 文件,如本页所述
- 更新你的 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 环境变量。只要您上传的版本与 SDK 的 init 调用的版本相匹配,您就可以以任何形式提供它们。
- 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 文档。
- sentry-cli:https://docs.sentry.io/product/cli/releases/#managing-release-artifacts
Web 应用程序可在多个来源访问的情况并不少见。请参阅我们关于多源的文档以了解如何处理此问题。
- multiple origins:https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/uploading/multiple-origins/
公开托管
将 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 的更多信息,请参阅:
- IP Ranges:https://docs.sentry.io/product/security/ip-ranges/
安全访问 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 应用程序可在多个来源访问的情况并不少见。例如:
- 网站可通过 https 和 http 运行
- 地理定位网址:例如 https://us.example.com、https://eu.example.com
- 多个静态 CDN:如 https://static1.example.com、https://static2.example.com
- 客户特定的域/子域
在这种情况下,相同的 JavaScript 和 source map 文件可能位于两个或多个不同的来源。在这种情况下,我们建议在路径上使用我们特殊的波浪号 (~) 前缀。
例如,如果您有以下内容:
https://static1.example.com/js/app.js
https://static2.example.com/js/app.js
您可以使用 ~/js/app.js 的 URL 上传。这将告诉 Sentry 忽略域并将 artifact 用于任何来源。
此外,您还可以以多个名称上传同一个文件。在引擎盖(hood)下 Sentry 将对这些进行重复数据删除。
~ 前缀告诉 Sentry 对于给定的 URL,路径为 /js/app.js 的协议和主机名的任何组合都应该使用这个工件。
验证文件
确保 source maps 本身有效并正确上传可能非常具有挑战性。为了解决这个问题,我们维护了一个在线验证工具,可用于针对您的托管源测试您的 source map:sourcemaps.io。
- https://sourcemaps.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 artifacts(bundle 文件和源 source maps)的文件名应与堆栈跟踪中报告的路径匹配。您可以使用上传配置来调整文件的名称。 Webpack 插件和 Sentry CLI 都有相同的选项;下面介绍了与 source maps 相关的内容。还可以使用我们的 RewriteFrames 集成来调整堆栈跟踪内的路径。
根据您的设置,您可能需要在开发和生产环境中为 source maps 进行不同的配置,因为堆栈跟踪中的路径可能不同。
Webpack 和 Sentry CLI 的选项
这些选项和示例将有助于集成您的source maps。
include
此选项接受一个或多个路径来递归扫描源和 *.map 文件。例如:
1.包括您的转译器/捆绑器输出文件的位置:
- include: './app/.next'
- include: './build'
2.包括来自多个文件夹:
- 包括:['./src', './lib']
3.递归搜索整个项目:
- include: '.'
rewrite
允许重写匹配的 source maps,以便在可能的情况下将索引映射扁平化并内联缺失的源。默认为 true。
应该启用此选项以使 stripPrefix 和 stripCommonPrefix 工作。
urlPrefix
此选项在所有文件名的开头添加一个公共前缀。默认为 ~/,这是一个匹配任何 scheme 和 hostname 的通配符(http://my.web.site/path/to/script.js 的 http://my.web.site/ 部分)。
当应用程序的入口点(通常是浏览器端的 index.html 和 Node 的 index.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-cli 的 path/to/sourcemaps 中包含要删除的部分。例如,如果您的文件存储在 ./build/static/js/ 并且您在 Webpack 插件配置中有 include: "build",您的文件将使用类似 ~/static/js/bundle.js 的名称上传。如果您更新您的配置 include: "build/static/js",您的文件将上传为 ~/bundle.js(等等)。
调整帧(Frames)
或者,您可以使用 Sentry 的 RewriteFrames 集成来微调堆栈跟踪内的路径。
- 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。
- Auto-Minify:https://blog.cloudflare.com/an-all-new-and-improved-autominify/
仔细检查您部署的最终 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-cli 或 dist 选项和 @sentry/webpack-plugin。要在 SDK 中设置 dist 值,请使用 Sentry.init() 中的 dist 选项。
要验证 SDK 中的分发设置是否正确,请在 Sentry UI 中打开一个 issue 并检查 dist 标签是否存在。对于工件,转到项目设置中的 Source Maps 页面,选择您刚刚检查的事件中显示的 release,并验证 dist 值(在 upload time 旁边的小椭圆中)与事件上的值匹配。
验证 artifact 名称与 sourceMappingURL 值匹配
bundled 或 minified 的 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 中用于替换 scheme 和 domain。
http://example.com/dist/js/script.js 将匹配 ~/dist/js/script.js 或 http://example.com/dist/js/script.js
但不会匹配 ~/script.js。
在发生错误之前验证 artifact 已上传
Sentry 期望给定版本中的源代码和 source maps 在该 release 中发生错误之前上传到 Sentry。
如果您在 Sentry 捕获错误后上传 artifact,Sentry 将不会返回并追溯将任何源注释(source annotations)应用于这些错误。只有在 artifact 上传后触发的新错误才会受到影响。
验证您的 source maps 是否正确构建
我们维护一个在线验证工具,可用于针对您的托管源测试您的source maps:sourcemaps.io。
- https://sourcemaps.io/
或者,如果您使用 Sentry CLI 将 source maps 上传到 Sentry,您可以使用 --validate 命令行选项来验证您的 source maps 是否正确。
验证您的 source maps 在本地工作
如果您发现 Sentry 没有正确映射文件名、行或列映射,您应该验证您的 source maps 是否在本地运行。为此,您可以将 Node.js 与 Mozilla 的source-map library 结合使用。
- https://github.com/mozilla/source-map
首先,将 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 生成配置。
验证您的源文件不是太大
对于单个 artifact,Sentry 接受的最大文件大小为 40 MB。
用户通常会达到此限制,因为他们在临时构建阶段传输源文件。例如,在 Webpack/Browserify 合并所有源文件之后,但在 minification 之前。如果可能,请发送原始源文件。
验证 artifact 没有被 gzip
Sentry API 目前仅适用于以纯文本(UTF-8 编码)形式上传的 source maps 和源文件。如果文件以压缩格式(例如 gzip)上传,它们将不会被正确解释。
这有时发生在生成 pre-compressed minified 文件的构建脚本和插件中。例如,Webpack 的 compression 插件。您需要禁用此类插件并在生成的 source maps/source files 上传到 Sentry 后执行压缩。
- https://github.com/webpack/compression-webpack-plugin
验证 worker 与 Web 共享相同的卷(如果通过 Docker 运行自托管 Sentry)
Sentry 在其 worker 中进行 source map 计算。这意味着 worker 需要访问通过前端上传的文件。仔细检查 cron worker 和 web worker 是否可以从同一个磁盘读取/写入文件。
故障排除
如果您需要帮助解决 Sentry JavaScript SDK integration 问题,您可以阅读此处记录的边缘案例。
调试附加数据
您可以查看事件的 JSON payload 以了解 Sentry 如何在事件中存储其他数据。数据的形状可能与描述不完全匹配。
有关更多详细信息,请参阅有关事件有效负载的完整文档。
- https://develop.sentry.dev/sdk/event-payloads/
最大 JSON Payload 大小
maxValueLength 的默认值为 250,但如果您的消息较长,您可以根据需要调整此值。请注意,并非每个值都受此选项影响。
CORS 属性和 Header
要了解从不同来源的脚本引发的 JavaScript 异常,请执行以下两项操作:
1.添加 crossorigin="anonymous" 脚本属性
- <script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>
脚本属性告诉浏览器 “anonymously” 获取目标文件。请求此文件时,浏览器不会将潜在的用户识别信息(如 cookie 或 HTTP 凭据)传输到服务器。
2.添加 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 选项。有关更多详细信息,请参阅我们的性能监控文档中的自动检测。
- https://docs.sentry.io/platforms/javascript/performance/instrumentation/automatic-instrumentation/#tracingorigins
instrument.js Console Log 语句的行号
如果调试时在您的控制台中显示了 instrument.js,请将 Sentry 添加到您的框架黑盒设置中,例如:/@sentry/,以便 Chrome 在调试时忽略 SDK 堆栈帧。
- blackboxing:https://docs.sentry.io/platforms/javascript/guides/react/troubleshooting/
处理广告拦截器(Ad-Blockers)
当您使用我们的 CDN 时,广告拦截或脚本拦截扩展可能会阻止我们的 SDK 被正确获取和初始化。因此,对 SDK API 的任何调用都将失败,并可能导致您的应用程序出现意外行为。
此外,即使正确下载并初始化 SDK,也可能会阻止需要接收捕获数据的 Sentry 端点。这将阻止任何错误报告、会话运行状况或性能数据的传递,从而使其在 sentry.io 中实际上不可用。
您可以通过上述多种方式解决第一个 issue 。但是,端点阻塞只能使用隧道解决。
使用 tunnel 选项
隧道是一个 HTTP 端点,充当 Sentry 和您的应用程序之间的代理。由于您控制此服务器,因此不会有任何发送到它的请求被阻止的风险。当端点位于同一个源下时(尽管它不必为了隧道工作),浏览器不会将任何对端点的请求视为第三方请求。因此,这些请求将应用不同的安全措施,默认情况下不会触发广告拦截器。可以在下面找到流程的快速摘要。
从 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();
查看我们的示例存储库以了解更多信息。
- https://github.com/getsentry/examples/tree/master/tunneling
如果您的用例与 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 上缺少一些方法,如 configureScope 和 withScope,因为 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 上设置的,如果您需要处理多个 Client 和 Hub,您还必须确保正确进行集成处理。这是一个如何使用多个 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 handlers 以 capture 未捕获的 exceptions 和未处理的 promise rejections。您可以通过在 GlobalHandlers 集成中将 onunhandledrejection 选项更改为 false 并手动挂接到每个事件处理程序,然后直接调用 Sentry.captureException 或 Sentry.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 object 的 promise。
您可以在 “Additional Data” 部分的 __serialized__ 条目中查看有问题的非错误对象的内容。
为了更好地了解这些错误事件,我们建议根据 __serialized__ 数据的内容找到 plain object 被传递或抛出到 Sentry 的位置,然后将 plain object 转换为 Error 对象。
支持的浏览器
Sentry 的 JavaScript 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)。