From 1aefeabd81021a8841ae50f44453e4ade3f6be12 Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Mon, 31 Aug 2020 14:11:58 +0200 Subject: [PATCH 1/2] feat: JS performance docs --- src/platforms/javascript/config/index.mdx | 34 +++ .../javascript/config/performance.mdx | 281 ++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 src/platforms/javascript/config/index.mdx create mode 100644 src/platforms/javascript/config/performance.mdx diff --git a/src/platforms/javascript/config/index.mdx b/src/platforms/javascript/config/index.mdx new file mode 100644 index 00000000000000..5e9062b9168745 --- /dev/null +++ b/src/platforms/javascript/config/index.mdx @@ -0,0 +1,34 @@ +--- +title: Configuration Options +excerpt: "" +--- + +Sentry's JavaScript SDK includes many configuration options that are automatically set. You can configure your SDK using the options outlined in these pages: + +- **[Basic Options](./basics/)** + + Learn more about how to configure the JavaScript SDK. These options are set when the SDK is first initialized, passed to the `init()` as an object. This page also discusses the Hooks `beforeSend` and `beforeBreadcrumb` as well as transport options. + +- **[Performance](./performance/)** + + Learn more about how to configure Performance for the JavaScript SDK. It provides examples of more advanced use cases. + +- **[Filter Events Reported to Sentry](./filter/)** + + Learn more about how to filter events reported to Sentry, using either the SDK, product filtering options, or both. + +- **[Lazy Loading](./lazy-load-sentry/)** + + We recommend using our bundled CDN version for the browser. However, if you useĀ `defer`, learn more about its effect on capturing errors. + +- **[Shutdown and Draining](./shutdown-drain/)** + + Learn more about the default behavior of our JavaScript SDK if the application shuts down unexpectedly. + +- **[Source Maps](./sourcemaps/)** + + Learn more about the Sentry JavaScript SDK's automatic fetching of source code and source maps by scraping the URLs within the stack trace. + +- **[Supported Browsers](./supported-browsers/)** + + We support a variety of browsers; check out our list. diff --git a/src/platforms/javascript/config/performance.mdx b/src/platforms/javascript/config/performance.mdx new file mode 100644 index 00000000000000..68e6a2ffa0948d --- /dev/null +++ b/src/platforms/javascript/config/performance.mdx @@ -0,0 +1,281 @@ +--- +title: Performance +excerpt: "" +--- + +To see how to setup Performance for Javascript visit [Getting Started](/platforms/javascript/#monitor-performance) + +### Automatic Instrumentation + +For `@sentry/browser`, we provide an integration called `BrowserTracing` that does automatic instrumentation in the browser. The `BrowserTracing` integration creates `pageload` and `navigation` transactions containing spans for XHR/fetch requests and Performance API entries such as marks, measures, and resource timings. + +The `BrowserTracing` integration is specific to `@sentry/browser` and does not work with `@sentry/node`. + +The `BrowserTracing` integration resides in the `@sentry/tracing` package. You can add it to your `Sentry.init` call: + +```javascript {tabTitle: ESM} +import * as Sentry from "@sentry/browser"; +import { Integrations } from "@sentry/tracing"; + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + integrations: [new Integrations.BrowserTracing()], + tracesSampleRate: 1.0, // Be sure to lower this in production +}); +``` + +```javascript {tabTitle: CDN} +Sentry.init({ + dsn: "___PUBLIC_DSN___", + integrations: [new Sentry.Integrations.BrowserTracing()], + tracesSampleRate: 1.0, // Be sure to lower this in production +}); +``` + +_NOTE:_ The `BrowserTracing` integration is available under `Sentry.Integrations.BrowserTracing` when using the CDN bundle. + +To send traces, you will need to set the `tracesSampleRate` to a nonzero value. The configuration above will capture 100% of your transactions. + +By default, the `pageload` and `navigation` transactions set a transaction name using `window.location.pathname`. + +You can pass many different options to the `BrowserTracing` integration (as an object of the form `{optionName: value}`), but it comes with reasonable defaults out of the box. + +For all possible options, see [TypeDocs](https://getsentry.github.io/sentry-javascript/interfaces/tracing.browsertracingoptions.html). + +#### tracingOrigins Option + +The default value of `tracingOrigins` is `['localhost', /^\//]`. The JavaScript SDK will attach the `sentry-trace` header to all outgoing XHR/fetch requests whose destination contains a string in the list or matches a regex in the list. If your frontend is making requests to a different domain, you will need to add it there to propagate the `sentry-trace` header to the backend services, which is required to link transactions together as part of a single trace. **One important thing to note is that the `tracingOrigins` option matches against the whole request URL, not just the domain. Using stricter regex to match certain parts of the URL can help make sure that requests do not unnecessarily have the `sentry-trace` header attached.** + +_Example:_ + +- A frontend application is served from `example.com` +- A backend service is served from `api.example.com` +- The frontend application makes API calls to the backend +- Therefore, the option needs to be configured like this: `new Integrations.BrowserTracing({tracingOrigins: ['api.example.com']})` +- Now outgoing XHR/fetch requests to `api.example.com` will get the `sentry-trace` header attached + +_NOTE:_ You need to make sure your web server CORS is configured to allow the `sentry-trace` header. The configuration might look like `"Access-Control-Allow-Headers: sentry-trace"`, but this depends a lot on your setup. If you do not allow the `sentry-trace` header, the request might be blocked. + +#### beforeNavigate Option + +For `pageload` and `navigation` transactions, the `BrowserTracing` integration uses the browser's `window.location` API to generate a transaction name. To customize the name of the `pageload` and `navigation` transactions, you can supply a `beforeNavigate` option to the `BrowserTracing` integration. This option allows you to pass in a function that takes in the location at the time of navigation and returns a new transaction name. + +`beforeNavigate` is useful if you would like to leverage the routes from a custom routing library like `React Router` or if you want to reduce the cardinality of particular transactions. + +```javascript +import * as Sentry from "@sentry/browser"; +import { Integrations } from "@sentry/tracing"; + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + integrations: [ + new Integrations.BrowserTracing({ + beforeNavigate: context => { + return { + ...context, + // You could use your UI's routing library to find the matching + // route template here. We don't have one right now, so do some basic + // parameter replacements. + name: location.pathname + .replace(/\d+/g, "") + .replace(/[a-f0-9]{32}/g, ""), + }; + }, + }), + ], + tracesSampleRate: 1.0, // Be sure to lower this in production +}); +``` + +### Filter out unwanted Spans + +In the `BrowserTracing` options we provide a method called `shouldCreateSpanForRequest`. This function can be used to filter our unwanted Spans like XHR's running health checks or something similar. By default `shouldCreateSpanForRequest` is already filtering out what was defined in `tracingOrigins`. + +```javascript +import * as Sentry from "@sentry/browser"; +import { Integrations } from "@sentry/tracing"; + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + integrations: [ + new Integrations.BrowserTracing({ + shouldCreateSpanForRequest: url => { + // Example of filter out spans that contain `health` + if (url.match(/health/)) { + return false; + } + return true; + }, + }), + ], + tracesSampleRate: 1.0, // Be sure to lower this in production +}); +``` + +### Manual Instrumentation + +To manually instrument certain regions of your code, you can create a transaction to capture them. +This is valid for both JavaScript Browser and Node and works independently of the `BrowserTracing` integration. + +```javascript +const transaction = Sentry.startTransaction({ name: "test-transaction" }); +const span = transaction.startChild({ op: "functionX" }); // This function returns a Span +// functionCallX +span.finish(); // Remember that only finished spans will be sent with the transaction +transaction.finish(); // Finishing the transaction will send it to Sentry +``` + +Here is a different example. If you want to create a transaction for a user interaction on your page, you need to do the following: + +```javascript +// Let's say this function is invoked when a user clicks on the checkout button of your shop +shopCheckout() { + // This will create a new Transaction for you + const transaction = Sentry.startTransaction('shopCheckout'); + // set the transaction on the scope so it picks up any errors + hub.configureScope(scope => scope.setSpan(transaction)); + + // Assume this function makes an xhr/fetch call + const result = validateShoppingCartOnServer(); + + const span = transaction.startChild({ + data: { + result + }, + op: 'task', + description: `processing shopping cart result`, + }); + processAndValidateShoppingCart(result); + span.finish(); + + transaction.finish(); +} +``` + +This example will send a transaction `shopCheckout` to Sentry. The transaction will contain a `task` span that measures how long `processAndValidateShoppingCart` took. Finally, the call to `transaction.finish()` will finish the transaction and send it to Sentry. + +#### Adding Additional Spans to the Transaction + +The next example contains the implementation of the hypothetical `processItem` function called from the code snippet in the previous section. Our SDK can determine if there is currently an open transaction and add to it all newly created spans as child operations. Keep in mind that each individual span needs to be manually finished; otherwise, that span will not show up in the transaction. + +```javascript +function processItem(item, transaction) { + const span = transaction.startChild({ + op: "http", + description: "GET /", + }); + + return new Promise((resolve, reject) => { + http.get(`/items/${item.id}`, response => { + response.on("data", () => {}); + response.on("end", () => { + span.setTag("http.status_code", response.statusCode); + span.setData("http.foobarsessionid", getFoobarSessionid(response)); + span.finish(); + resolve(response); + }); + }); + }); +} +``` + +### Grouping Transactions + +When Sentry captures transactions, they are assigned a transaction name. This name is generally auto-generated by the Sentry SDK based on the framework integrations you are using. If you can't leverage the automatic transaction generation (or want to customize how transaction names are generated) you can use a global event processor that is registered when you initialize the SDK with your configuration. + +An example of doing this in a node.js application: + +```javascript +import { addGlobalEventProcessor } from "@sentry/node"; + +addGlobalEventProcess(event => { + // if event is a transaction event + if (event.type === "transaction") { + event.transaction = sanitizeTransactionName(event.transaction); + } + return event; +}); +``` + +For browser JavaScript applications using the `BrowserTracing` integration, the `beforeNavigate` option can be used to better group navigation/pageload transactions together based on URL. + +```javascript +import * as Sentry from "@sentry/browser"; +import { Integrations } from "@sentry/tracing"; + +Sentry.init({ + // ... + integrations: [ + new Integrations.BrowserTracing({ + beforeNavigate: context => { + return { + ...context, + // You could use your UI's routing library to find the matching + // route template here. We don't have one right now, so do some basic + // parameter replacements. + name: location.pathname + .replace(/\d+/g, "") + .replace(/[a-f0-9]{32}/g, ""), + }; + }, + }), + ], +}); +``` + +### Retrieving a Transaction + +In cases where you want to attach Spans to an already ongoing Transaction you can use `Sentry.getCurrentHub().getScope().getTransaction()`. This function will return a `Transaction` in case there is a running Transaction on the scope, otherwise it returns `undefined`. If you are using our BrowserTracing integration by default we attach the Transaction to the Scope. So you could do something like this: + +```javascript +function myJsFunction() { + const transaction = Sentry.getCurrentHub() + .getScope() + .getTransaction(); + if (transaction) { + let span = transaction.startChild({ + op: "encode", + description: "parseAvatarImages", + }); + // Do something + span.finish(); + } +} +``` + +### Adding Query Information and Parameters to Spans + +Currently, every tag has a maximum character limit of 200 characters. Tags over the 200 character limit will become truncated, losing potentially important information. To retain this data, you can split data over several tags instead. + +For example, a 200+ character tagged request: + +`https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=161803398874989484820458683436563811772030917980576` + +The 200+ character request above will become truncated to: + +`https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=1618033988749894848` + +Instead, using `span.set_tag` and `span.set_data` preserves the details of this query using structured metadata. This could be done over `baseUrl`, `endpoint`, and `parameters`: + +```javascript +const baseUrl = "https://empowerplant.io"; +const endpoint = "/api/0/projects/ep/setup_form"; +const parameters = { + user_id: 314159265358979323846264338327, + tracking_id: "EasyAsABC123OrSimpleAsDoReMi", + product_name: PlantToHumanTranslator, + product_id: 161803398874989484820458683436563811772030917980576, +}; + +const span = transaction.startChild({ + op: "request", + description: "setup form", +}); + +span.setTag("baseUrl", baseUrl); +span.setTag("endpoint", endpoint); +span.setData("parameters", parameters); +// you may also find some parameters to be valuable as tags +span.setData("user_id", parameters.user_id); +http.get(`${base_url}/${endpoint}/`, (data = parameters)); +``` From c31260f5ad3d1b94cc095cdc9783dfee906908bc Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Tue, 1 Sep 2020 18:02:38 +0200 Subject: [PATCH 2/2] ref: Move the performance docs to common for JS --- .../javascript/common/config/index.mdx | 4 +++ .../{ => common}/config/performance.mdx | 1 - src/platforms/javascript/config/index.mdx | 34 ------------------- 3 files changed, 4 insertions(+), 35 deletions(-) rename src/platforms/javascript/{ => common}/config/performance.mdx (99%) delete mode 100644 src/platforms/javascript/config/index.mdx diff --git a/src/platforms/javascript/common/config/index.mdx b/src/platforms/javascript/common/config/index.mdx index 0950ca4a4c9904..5e9062b9168745 100644 --- a/src/platforms/javascript/common/config/index.mdx +++ b/src/platforms/javascript/common/config/index.mdx @@ -8,6 +8,10 @@ Sentry's JavaScript SDK includes many configuration options that are automatical - **[Basic Options](./basics/)** Learn more about how to configure the JavaScript SDK. These options are set when the SDK is first initialized, passed to the `init()` as an object. This page also discusses the Hooks `beforeSend` and `beforeBreadcrumb` as well as transport options. + +- **[Performance](./performance/)** + + Learn more about how to configure Performance for the JavaScript SDK. It provides examples of more advanced use cases. - **[Filter Events Reported to Sentry](./filter/)** diff --git a/src/platforms/javascript/config/performance.mdx b/src/platforms/javascript/common/config/performance.mdx similarity index 99% rename from src/platforms/javascript/config/performance.mdx rename to src/platforms/javascript/common/config/performance.mdx index 68e6a2ffa0948d..08fe710975daf9 100644 --- a/src/platforms/javascript/config/performance.mdx +++ b/src/platforms/javascript/common/config/performance.mdx @@ -1,6 +1,5 @@ --- title: Performance -excerpt: "" --- To see how to setup Performance for Javascript visit [Getting Started](/platforms/javascript/#monitor-performance) diff --git a/src/platforms/javascript/config/index.mdx b/src/platforms/javascript/config/index.mdx deleted file mode 100644 index 5e9062b9168745..00000000000000 --- a/src/platforms/javascript/config/index.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Configuration Options -excerpt: "" ---- - -Sentry's JavaScript SDK includes many configuration options that are automatically set. You can configure your SDK using the options outlined in these pages: - -- **[Basic Options](./basics/)** - - Learn more about how to configure the JavaScript SDK. These options are set when the SDK is first initialized, passed to the `init()` as an object. This page also discusses the Hooks `beforeSend` and `beforeBreadcrumb` as well as transport options. - -- **[Performance](./performance/)** - - Learn more about how to configure Performance for the JavaScript SDK. It provides examples of more advanced use cases. - -- **[Filter Events Reported to Sentry](./filter/)** - - Learn more about how to filter events reported to Sentry, using either the SDK, product filtering options, or both. - -- **[Lazy Loading](./lazy-load-sentry/)** - - We recommend using our bundled CDN version for the browser. However, if you useĀ `defer`, learn more about its effect on capturing errors. - -- **[Shutdown and Draining](./shutdown-drain/)** - - Learn more about the default behavior of our JavaScript SDK if the application shuts down unexpectedly. - -- **[Source Maps](./sourcemaps/)** - - Learn more about the Sentry JavaScript SDK's automatic fetching of source code and source maps by scraping the URLs within the stack trace. - -- **[Supported Browsers](./supported-browsers/)** - - We support a variety of browsers; check out our list.