Performance Monitoring
Sentry allows you to monitor the performance of your application, showing you how latency in one service may affect another service, and letting you pinpoint exactly which parts of a given operation may be responsible. To do this, it captures distributed traces consisting of transactions and spans, which measure individual services and individual operations within those services, respectively. You can learn more about this model in our Distributed Tracing docs.
Once tracing is enabled, certain types of operations will be measured automatically, and you can also choose to manually measure any operation you like. To learn more, see Capturing Transactions Automatically and Capturing Transactions Manually.
Enabling Tracing
The first step is to install the tracing package, if you haven't done so already:
# Using yarn
yarn add @sentry/tracing
# Using npm
npm install @sentry/tracingOnce you've installed the package, there are two ways to enable tracing in your app:
- Set a uniform sample rate for all transactions, by setting the
tracesSampleRateoption in your SDK config to a number between0and1. (For example, to send 20% of transactions, settracesSampleRateto0.2.) - Control the sample rate dynamically, based on the transaction itself and the context in which it's captured, by providing a function to the
tracesSamplerconfig option.
// 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";
// If taking advantage of automatic instrumentation (highly recommended)
import { Integrations as TracingIntegrations } from "@sentry/tracing";
// Or, if only doing manual tracing
// import * as _ from "@sentry/tracing"
// Note: You MUST import the package in some way for tracing to work
Sentry.init({
dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
// This enables automatic instrumentation (highly recommeneded), but is not
// necessary for purely manual usage
integrations: [new TracingIntegrations.BrowserTracing()],
// To set a uniform sample rate
tracesSampleRate: 0.2
// Alternatively, to control sampling dynamically
tracesSampler: samplingContext => { ... }
});If either of these options is set, tracing will be enabled in your app. (They are meant to be mutually exclusive, but if you do set both, tracesSampler will take precedence.) You can learn more about how they work in Sampling Transactions.
Note
When you first enable tracing, the easiest thing to do to test it is to set tracesSampleRate to 1.0, because that guarantees that every transaction will be sent to Sentry for you to see.
Once you're done with testing, however, we recommend that you either lower your tracesSampleRate value, or switch to using tracesSampler to dynamically sample and filter your transactions.
Without sampling, our atomatic instrumenation will send a transaction any time any user loads any page or navigates anywhere in your app. That's a lot of transactions! Sampling allows you to get representative data without overwhelming either your system or your Sentry transaction quota.
Connecting Backend and Frontend Transactions
To connect backend and frontend transactions into a single choherent trace, Sentry uses a trace_id value which is propagated between frontend and backend. Depending on the circumstance, this id is transmitted either in a request/response header or in an HTML <meta> tag. Linking transactions in this way makes it possible for you to navigate between them in the Sentry UI, so you can better understand how the different parts of your system are affecting each other.
Pageload
When enabling tracing in both frontend and backend and taking advantage of the automatic frontend instrumentation, you can connect the automatically-generated pageload transaction on the frontend with the request transaction that serves the page on the backend. Because JavaScript code running in a browser cannot read the response headers of the current page, the trace_id must be transmitted in the response itself, specifically in a <meta> tag in the <head> of the HTML sent from your backend.
<html>
<head>
<meta name="sentry-trace" content="{{ span.toTraceparent() }}" />
<!-- ... -->
</head>
</html>The name attribute must be the string "sentry-trace" and the content attribute must be generated by your backend's Sentry SDK using span.toTraceparent() (or equivalent, depending on the backend platform). This guarantees that a new and unique value will be generated for each request.
The span reference is either the transaction that serves the HTML, or any of its child spans. It defines the parent of the pageload transaction.
Once the data is included in the <meta> tag, our BrowserTracing integration will pick it up automatically and link it to the transaction generated on pageload. (Note that it will not get linked to automatically-generated navigation transactions, that is, those which don't require a full page reload. Each of those will be the result of a different request transaction on the backend, and therefore should have a unique trace_id.)
Navigation and Other XHR Requests
Once a page is loaded, any requests it makes and any responses your backend generates as a result are linked through a request/response header.
As is the case with the <meta> tag discussed above, the header's name is sentry-trace and its value is obtained by calling span.toTraceparent() (or the equivalent), where span is either the relevant transaction or any of its children.
All of Sentry's tracing-related integrations (BrowserTracing, Http, and Express) either generate or pick up and propagate this header automatically as appropriate, for all transactions and spans which they generate. You can also attach and read the header yourself, in any case in which you've manually created either a transaction or a span and it makes sense to do so.
Retrieving an Active 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 object when 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:
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:
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));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:
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.
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, "<digits>")
.replace(/[a-f0-9]{32}/g, "<hash>"),
};
},
}),
],
});- Package:
- npm:@sentry/react
- Version:
- 5.25.0
- Repository:
- https://github.com/getsentry/sentry-javascript