From 85c1559f240011f7373e5add9e0e5ea2a88656e5 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Tue, 10 Sep 2024 18:20:35 +0200 Subject: [PATCH 01/12] Add React Native V6 Changes --- .../enriching-events/context/index.mdx | 5 +- .../enriching-events/scopes/index.mdx | 155 +++++++++---- .../transaction-name/index.mdx | 8 +- docs/platforms/react-native/index.mdx | 12 +- .../react-native/integrations/custom.mdx | 119 +++++++++- .../react-native/integrations/default.mdx | 120 +++------- .../integrations/error-boundary.mdx | 8 +- .../react-native/integrations/plugin.mdx | 97 +++++++- docs/platforms/react-native/tracing/index.mdx | 4 - .../automatic-instrumentation.mdx | 111 ++++----- .../custom-instrumentation.mdx | 216 +++++++++++++++++- .../instrumentation/react-navigation-v4.mdx | 96 -------- .../configuration/integrations/plugin.mdx | 134 ----------- .../configuration/dedupe/react-native.mdx | 9 - .../react-native.mdx | 10 - .../react-native.mdx | 9 - .../http-client/react-native.mdx | 26 --- .../linked-errors/react-native.mdx | 15 -- .../rewrite-frames/react-native.mdx | 30 --- .../enriching-events/import/react-native.mdx | 3 - .../set-transaction-name/react-native.mdx | 3 - .../filter-span-example/react-native.mdx | 13 -- 22 files changed, 618 insertions(+), 585 deletions(-) delete mode 100644 docs/platforms/react-native/tracing/instrumentation/react-navigation-v4.mdx delete mode 100644 includes/platforms/configuration/integrations/plugin.mdx delete mode 100644 platform-includes/configuration/dedupe/react-native.mdx delete mode 100644 platform-includes/configuration/enable-pluggable-integrations-lazy/react-native.mdx delete mode 100644 platform-includes/configuration/enable-pluggable-integrations/react-native.mdx delete mode 100644 platform-includes/configuration/http-client/react-native.mdx delete mode 100644 platform-includes/configuration/linked-errors/react-native.mdx delete mode 100644 platform-includes/configuration/rewrite-frames/react-native.mdx delete mode 100644 platform-includes/enriching-events/import/react-native.mdx delete mode 100644 platform-includes/enriching-events/set-transaction-name/react-native.mdx delete mode 100644 platform-includes/performance/filter-span-example/react-native.mdx diff --git a/docs/platforms/react-native/enriching-events/context/index.mdx b/docs/platforms/react-native/enriching-events/context/index.mdx index cb533c9061010a..fc9fe8ff41ed74 100644 --- a/docs/platforms/react-native/enriching-events/context/index.mdx +++ b/docs/platforms/react-native/enriching-events/context/index.mdx @@ -89,6 +89,7 @@ Sentry.captureException(new Error("something went wrong"), () => scope); Context is held in the current scope and thus is cleared when the scope is removed ("popped"). You can push and pop your own scopes to apply context data to a specific code block or function. + Sentry supports two different ways for unsetting context: 1. Modifying, overwriting or clearing values on the current scope. @@ -100,10 +101,10 @@ With the following snippet, the `user` context will be updated for all future ev Sentry.setUser(someUser); ``` -If you want to remove data from the current scope, you can call: +If you want to remove all data from the current scope, you can call: ```javascript -Sentry.configureScope((scope) => scope.clear()); +Sentry.getCurrentScope().clear(); ``` The following will only configure the `user` context for the error captured inside the `withScope` callback. The context will be automatically restored to the previous state afterwards: diff --git a/docs/platforms/react-native/enriching-events/scopes/index.mdx b/docs/platforms/react-native/enriching-events/scopes/index.mdx index b7491c6792745f..34ba5fa7a67e6f 100644 --- a/docs/platforms/react-native/enriching-events/scopes/index.mdx +++ b/docs/platforms/react-native/enriching-events/scopes/index.mdx @@ -1,46 +1,112 @@ --- -title: Scopes and Hubs +title: Scopes description: "SDKs will typically automatically manage the scopes for you in the framework integrations. Learn what a scope is and how you can use it to your advantage." --- When an event is captured and sent to Sentry, SDKs will merge that event data with extra information from the current scope. SDKs will typically automatically manage the scopes for you in the framework integrations and you don't need to think about them. However, -you should know what a scope is and how you can use it for your advantage. +you should know what a scope is and how you can use it to your advantage. -## What's a Scope, What's a Hub +## What's a Scope? -You can think of the hub as the central point that our SDKs use to route an -event to Sentry. When you call `init()` a hub is created and a client and a -blank scope are created on it. That hub is then associated with the current -thread and will internally hold a stack of scopes. - -The scope will hold useful information that should be sent along with the -event. For instance [contexts](../context/) or +Scopes hold useful information that gets sent along with the +event. For instance, [contexts](../context/) and [breadcrumbs](../breadcrumbs/) are stored on -the scope. When a scope is pushed, it inherits all data from the parent scope -and when it pops all modifications are reverted. +the scope. When a scope is forked, it inherits all data from its parent scope. + +The default SDK integrations will fork scopes intelligently. For +instance, web framework integrations will fork scopes around your +routes or request handlers. + +## How Scopes Work + +Scopes are basically a stack of data that is attached to events. When an event is captured, the SDK will merge the data from the active scopes into the event. This allows you to attach data to events that is relevant to the context in which the event was captured. + +A scope is generally valid inside of a callback or an execution context. This means that multiple parts of your application may have different scopes active at the same time. For instance, a web server might handle multiple requests at the same time, and each request may have different scope data to apply to its events. + +## Different Kinds of Scopes + +The Sentry SDK has three different kinds of scopes: + +- [Global scope](#global-scope) +- [Isolation scope](#isolation-scope) +- [Current scope](#current-scope) + +### Global Scope + +The global scope is applied to _all_ events, no matter where they originate. You can use it to store data that should apply to all events, such as environmental information. + +You can access the global scope via `Sentry.getGlobalScope()`. + +Note that the global scope can only be used to write data, not to capture events. Events can only be captured on the current scope (e.g. `getCurrentScope().captureException()` and similar APIs). + +### Isolation Scope + +The isolation scope is used to isolate events from each other. For example, each request in a web server might get its own isolation scope, so that events from one request don't interfere with events from another request. In most cases, you'll want to put data that should be applied to your events on the isolation scope - which is also why all `Sentry.setXXX` methods, like `Sentry.setTag()`, will write data onto the currently active isolation scope. A classic example for data that belongs on the isolation scope is a user - each request may have a different user, so you want to make sure that the user is set on the isolation scope. -The default SDK integrations will push and pop scopes intelligently. For -instance web framework integrations will create and destroy scopes around your -routes or controllers. +You can access the isolation scope via `Sentry.getIsolationScope()`, but usually you'll just use the `Sentry.setXXX` methods to set data on the currently active isolation scope: -## How the Scope and Hub Work +```javascript +Sentry.setTag("my-tag", "my value"); +// Is identical to: +Sentry.getIsolationScope().setTag("my-tag", "my value"); +``` -As you start using an SDK, a scope and hub are automatically created for you out -of the box. It's unlikely that you'll interact with the hub directly unless you're -writing an integration or you want to create or destroy scopes. Scopes, on the -other hand are more user facing. You can call at any point in -time to modify data stored on the scope. This is useful for doing things like -[modifying the context](../context/). + + In the browser, the isolation scope is never forked, because it is impossible + to keep track of where an isolation scope would belong to. Because of this, in + the browser the isolation scope is effectively global. + -When you call a global function such as internally Sentry -discovers the current hub and asks it to capture an event. Internally the hub will -then merge the event with the topmost scope's data. +Note that the isolation scope can only be used to write data, not to capture events. Events can only be captured on the current scope (e.g. `getCurrentScope().captureException()` and similar APIs). + +### Current Scope + +The current scope is the local scope that is currently active. Unlike the rarely-forked isolation scope, the current scope may be forked more frequently and under the hood. It can be used to store data that should only be applied to specific events. In most cases, you should not access this scope directly, but use `Sentry.withScope` to create a new scope that is only active for a specific part of your code: + +```javascript +Sentry.withScope((scope) => { + // scope is the current scope inside of this callback! + scope.setTag("my-tag", "my value"); + // this tag will only be applied to events captured inside of this callback + // the following event will have the tag: + Sentry.captureException(new Error("my error")); +}); +// this event will not have the tag: +Sentry.captureException(new Error("my other error")); +``` + +You can access the current scope via `Sentry.getCurrentScope()`, but usually you should use `Sentry.withScope()` to interact with local scopes instead. + +## How Scope Data is Applied to Events + +Before an event (like an error or transaction) is sent to Sentry, the currently active scopes are applied to it. + +The global scope is applied first, followed by the isolation scope, and finally the current scope. This means that any data set on the current scope will take precedence over data set on the isolation and global scopes: + +```javascript +Sentry.getGlobalScope().setExtras({ + shared: "global", + global: "data", +}); +Sentry.getIsolationScope().setExtras({ + shared: "isolation", + isolation: "data", +}); +Sentry.getCurrentScope().setExtras({ + shared: "current", + current: "data", +}); + +Sentry.captureException(new Error("my error")); +// --> Will have the following extra: +// { shared: 'current', global: 'data', isolation: 'data', current: 'data' } +``` ## Configuring the Scope -The most useful operation when working with scopes is the function. It can be used to reconfigure the current scope. +There are two main ways to interact with the scope. You can access the current scope via `Sentry.getCurrentScope()` and use setters on the resulting scope, or you can use global methods like `Sentry.setTag()` directly, which will set on the respective scope under the hood (which will be the isolation scope). You'll first need to import the SDK, as usual: @@ -48,31 +114,32 @@ You'll first need to import the SDK, as usual: You can, for instance, add custom tags or inform Sentry about the currently authenticated user. - - -You can also apply this configuration when unsetting a user at logout: - - +```javascript +/// Usually, you don't want to write on the current scope, so use with care! +const scope = Sentry.getCurrentScope(); +scope.setTag("my-tag", "my value"); +scope.setUser({ + id: 42, + email: "john.doe@example.com", +}); + +// Or use the global methods (which will set data on the isolation scope): +Sentry.setTag("my-tag", "my value"); +Sentry.setUser({ + id: 42, + email: "john.doe@example.com", +}); +``` To learn what useful information can be associated with scopes see -[the context documentation](../context/). +[context](../context/), [tags](../tags), [users](../identify-user) and [breadcrumbs](../breadcrumbs/). -## Local Scopes - -We also support pushing and configuring a scope within a single call. This is typically -called , or implemented as a function parameter on the capture methods, depending on the SDK. It's very helpful if -you only want to send data for one specific event. - -### Using +## Using `withScope` In the following example we use to attach a `level` and a `tag` to only one specific error: -While this example looks similar to , it is actually very different. -Calls to change the current active scope; all successive calls to will maintain previously set changes unless they are explicitly unset. - -On the other hand, creates a clone of the current scope, and the changes -made will stay isolated within the callback function. This allows you to +The scope inside the `withScope()` callback is only valid inside of the callback. Once the callback ends, the scope will be removed and no longer applied. The inner scope is only applied to events that are captured inside of the callback. `withScope()` will clone (or fork) the current scope, so that the current scope is not modified. This allows you to more easily isolate pieces of context information to specific locations in your code or even call to briefly remove all context information. diff --git a/docs/platforms/react-native/enriching-events/transaction-name/index.mdx b/docs/platforms/react-native/enriching-events/transaction-name/index.mdx index 252a245f8d31b0..3d4b603d5b24e5 100644 --- a/docs/platforms/react-native/enriching-events/transaction-name/index.mdx +++ b/docs/platforms/react-native/enriching-events/transaction-name/index.mdx @@ -22,10 +22,14 @@ A lot of our framework integrations already set a transaction name, though you c You'll first need to import the SDK, as usual: - +```javascript +import * as Sentry from "@sentry/react-native"; +``` To override the name of the currently running transaction: - +```javascript +Sentry.getCurrentScope().setTransactionName("UserListView"); +``` Please refer to [the tracing documentation](../../tracing/) for how to start and stop transactions. diff --git a/docs/platforms/react-native/index.mdx b/docs/platforms/react-native/index.mdx index 2d1ef4768d228d..e152b4e6d6a6a8 100644 --- a/docs/platforms/react-native/index.mdx +++ b/docs/platforms/react-native/index.mdx @@ -38,7 +38,7 @@ Sentry captures data by using an SDK within your application's runtime. These ar To install, run `@sentry/wizard`: ```bash {tabTitle:npm} -npx @sentry/wizard@latest -s -i reactNative +npx @sentry/wizard@latest -i reactNative ``` [Sentry Wizard](https://github.com/getsentry/sentry-wizard) will patch your project accordingly, though you can [set up manually](/platforms/react-native/manual-setup/manual-setup/) if you prefer. You only need to patch the project once. Then you can add the patched files to your version control system. @@ -65,7 +65,7 @@ If you're using Expo, [read our docs](/platforms/react-native/manual-setup/expo/ To capture all errors, initialize the Sentry React Native SDK as soon as possible. -```javascript {filename:App.js} {"onboardingOptions": {"performance": "5-7", "profiling": "8-12"}} +```javascript {filename:App.js} {"onboardingOptions": {"performance": "5-7", "profiling": "8-10"}} import * as Sentry from "@sentry/react-native"; Sentry.init({ @@ -73,11 +73,9 @@ Sentry.init({ // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing. // We recommend adjusting this value in production. tracesSampleRate: 1.0, - _experiments: { - // profilesSampleRate is relative to tracesSampleRate. - // Here, we'll capture profiles for 100% of transactions. - profilesSampleRate: 1.0, - }, + // profilesSampleRate is relative to tracesSampleRate. + // Here, we'll capture profiles for 100% of transactions. + profilesSampleRate: 1.0, }); ``` diff --git a/docs/platforms/react-native/integrations/custom.mdx b/docs/platforms/react-native/integrations/custom.mdx index b80f22829f18ef..121aa54046e5d8 100644 --- a/docs/platforms/react-native/integrations/custom.mdx +++ b/docs/platforms/react-native/integrations/custom.mdx @@ -1,7 +1,120 @@ --- title: Custom Integrations -description: "Learn how to enable a custom integration." -sidebar_order: 3 +sidebar_order: 200 +description: "Learn how you can enable a custom integration." --- - +In addition to the integrations that come with the SDK, you can also write custom integrations. + +Custom integration must conform to the [Integration interface](https://github.com/getsentry/sentry-javascript/blob/master/packages/types/src/integration.ts). + +A custom integration can be created and added to the SDK as follows: + +```javascript +function myAwesomeIntegration() { + return { + name: "MyAwesomeIntegration", + setup(client) { + // Do something when the SDK is initialized + // The client that is being setup is passed to the hook + }, + }; +} + +Sentry.init({ + // ... + integrations: [myAwesomeIntegration()], +}); +``` + +All hooks on an integration are optional. The only required field is the `name`. You can use one or multiple of the following hooks in a custom integration: + +### `setup` + +The `setup` hook is called when the SDK is initialized. It receives the client instance as an argument. +You should use this if you want to run some code on initialization. + +```javascript +const integration = { + name: "MyAwesomeIntegration", + setup(client) { + setupCustomSentryListener(client); + }, +}; +``` + +### `processEvent` + +This hook can be used to modify events before they are sent to Sentry. It receives the event as an argument and should return the modified event. The hook also receives a hint object that may hold additional event metadata, and the client that is sending the event. You can also return `null` to drop the event from being sent. + +```javascript +const integration = { + name: "MyAwesomeIntegration", + processEvent(event, hint, client) { + event.extra = { + ...event.extra, + myCustomTag: "value", + }; + // Return the modified event, + // or return `null` to drop the event + return event; + }, +}; +``` + +You can also return a promise that resolves with an event or `null`. However, this is not recommended and should be avoided wherever possible, because it can delay event sending. + +### `preprocessEvent` + +This hook is similar to `processEvent`, but it is called before the event is passed to any other `processEvent` hook. It can be used in places where the order of processing is important. + +In most cases, you should just use `processEvent`. Only use `preprocessEvent` if you need to ensure that your hook is called before any other `processEvent` hook. + +Similar to `processEvent`, this hooks receives the event, hint, and client as arguments. However, this hook does not allow to return a modified event or `null` to drop the event - instead, you can only mutate the passed in event in this hook: + +```javascript +const integration = { + name: "MyAwesomeIntegration", + preprocessEvent(event, hint, client) { + event.extra = { + ...event.extra, + myCustomTag: "value", + }; + // Nothing to return, just mutate the event + }, +}; +``` + +### `setupOnce` + +This hook is similar to `setup`, but it is only run once, even if the SDK is re-initialized. It does not receive any arguments. +You should probably not use this, but use `setup` instead. The only reason to use `setupOnce` is e.g. when you may be calling `Sentry.init()` multiple times and you want to ensure a certain piece of code is only run once. + +```javascript +const integration = { + name: "MyAwesomeIntegration", + setupOnce() { + wrapLibrary(); + }, +}; +``` + +### `afterAllSetup` + +This hook is triggered after `setupOnce()` and `setup()` have been called for all integrations. +You can use it if it is important that all other integrations have been run before. + +In most cases, you should not need to use this hook, and should use `setup` instead. + +The hook receives the `client` that is being set up as the first argument. + +```javascript +const integration = { + name: "MyAwesomeIntegration", + afterAllSetup(client) { + // We can be sure that all other integrations + // have run their `setup` and `setupOnce` hooks now + startSomeThing(client); + }, +}; +``` diff --git a/docs/platforms/react-native/integrations/default.mdx b/docs/platforms/react-native/integrations/default.mdx index 791a6ba0395908..36b3a856597134 100644 --- a/docs/platforms/react-native/integrations/default.mdx +++ b/docs/platforms/react-native/integrations/default.mdx @@ -9,7 +9,7 @@ The below system integrations are part of the standard library or the interprete ### InboundFilters -_Import name: `Sentry.Integrations.InboundFilters`_ +_Import name: `Sentry.inboundFiltersIntegration`_ This integration allows you to ignore specific errors based on the type, message, or URLs in a given exception. @@ -22,23 +22,13 @@ only work for captured exceptions, not raw message events. ### FunctionToString -_Import name: `Sentry.Integrations.FunctionToString`_ +_Import name: `Sentry.functionToStringIntegration`_ This integration allows the SDK to provide original functions and method names, even when those functions or methods are wrapped by our error or breadcrumb handlers. - - -### TryCatch - -_Import name: `Sentry.Integrations.TryCatch`_ - -This integration wraps native time and events APIs (`setTimeout`, `setInterval`, `requestAnimationFrame`, `addEventListener/removeEventListener`) in `try/catch` blocks to handle async exceptions. - - - ### Breadcrumbs -_Import name: `Sentry.Integrations.Breadcrumbs`_ +_Import name: `Sentry.breadcrumbsIntegration`_ This integration wraps native APIs to capture breadcrumbs. By default, the Sentry SDK wraps all APIs. @@ -71,33 +61,12 @@ Available options: } ``` - - -### GlobalHandlers +### NativeLinkedErrors -_Import name: `Sentry.Integrations.GlobalHandlers`_ - -This integration attaches global handlers to capture uncaught exceptions and unhandled rejections. - -Available options: - -```javascript -{ - onerror: boolean; - onunhandledrejection: boolean; -} -``` - - - -### LinkedErrors - -_Import name: `Sentry.Integrations.LinkedErrors`_ +_Import name: `Sentry.nativeLinkedErrorsIntegration`_ This integration allows you to configure linked errors which are recursively read up to a specified limit. A lookup by a specific key on the captured Error object is then performed. By default, the integration records up to five errors and the key used for recursion is `"cause"`. -This integration also enhances captured error events with data from an [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) where applicable, by recursively iterating over the `errors` property on Error objects. - Available options: ```javascript @@ -109,75 +78,42 @@ Available options: Here's a code example of how this could be implemented: - +```javascript +