Custom Instrumentation
Learn how to capture performance data on any action in your app.
To capture transactions and spans customized to your organization's needs, you must first set up performance monitoring.
To add custom performance data to your application, you need to add custom instrumentation in the form of spans. Spans are a way to measure the time it takes for a specific action to occur. For example, you can create a span to measure the time it takes for a function to execute.
To get started, import the SDK.
import * as Sentry from '@sentry/react-native';
The below span APIs (startSpan
, startInactiveSpan
, and startSpanManual
) require SDK version 7.69.0
or higher. If you are using an older version of the SDK, you can use the explicit transaction APIs for custom instrumentation.
By default, spans you create are considered active, which means they are put on the Sentry scope. This allows child spans and Sentry errors to be associated with that span. This is the recommended way to create spans.
Start a span for a synchronuous operation:
const result = Sentry.startSpan({name: 'Important Function'}, () => {
return expensiveFunction();
});
Start a span for an asynchronous operation:
const result = await Sentry.startSpan(
{name: 'Important Function'},
async () => {
const res = await doSomethingAsync();
return updateRes(res);
}
);
You can also nest spans:
const result = await Sentry.startSpan(
{
name: 'Important Function',
},
async () => {
const res = await Sentry.startSpan({name: 'Child Span'}, () => {
return expensiveAsyncFunction();
});
return updateRes(res);
}
);
To add spans that aren't active, you can create independent spans. This is useful for when you have work that is grouped together under a single parent span, but is independent from the current active span. However, in most cases you'll want to create and use the start span API from above.
const span1 = Sentry.startInactiveSpan({name: 'span1'});
someWork();
span1.end();
Spans can have an operation associated with them, which help activate Sentry identify additional context about the span. For example database related spans have the db
span operation associated with them. The Sentry product offers additional controls, visualizations and filters for spans with known operations.
Sentry maintains a list of well known span operations and it is recommended that you use one of those operations if it is applicable to your span.
const result = Sentry.startSpan({ name: 'GET /users', op: 'http.client' }, () => {
return fetchUsers();
})
The root span (the span that is the parent of all other spans) is known as a transaction in Sentry. Transactions can be accessed and created separately if you need more control over your timing data or if you use a version of the SDK that doesn't support the top-level span APIs.
To instrument certain regions of your code, you can create spans to capture them.
This is valid for all JavaScript SDKs (both backend and frontend) and works independently of the Express
, Http
, and BrowserTracing
integrations.
Sentry.startSpan({name: 'test-transaction'}, rootSpan => {
Sentry.startSpan({name: 'child-span', op: 'functionX'}, childSpan => {
// do something in here
// childSpan will be nested inside of rootSpan
});
});
For example, if you want to create a transaction for a user interaction on your page:
// Let's say this function is invoked when a user clicks on the checkout button of your shop
function shopCheckout() {
return Sentry.startSpan({name: 'shopCheckout'}, () => {
// Assume this function makes a fetch call
const result = validateShoppingCartOnServer();
return Sentry.startSpan(
{
name: 'processing shopping cart result',
op: 'task',
attributes: {
// you can add additional attributes, if needed
},
},
() => {
// `startSpan` will automatically mark the span as errored, if an exception is thrown
// it will end the span when the callback is finished.
return processAndValidateShoppingCart(result);
}
);
});
}
This example will send a transaction shopCheckout
to Sentry. The span will contain a task
child span that measures how long processAndValidateShoppingCart
took. When the callback ends, the span will be finished and sent to Sentry.
You can also take advantage of promises when creating spans for async operations. Keep in mind, though, that a span must finish before the parent span finishes in order to be sent to Sentry.
If the callback passed to startSpan()
is an async function or returns a promise, startSpan
will automatically wait for the promise to resolve before finishing the span. If the promise rejects, the span will be marked as errored.
For example, when returning a promise:
function processItem(item) {
return Sentry.startSpan(
{
op: 'http',
name: 'GET /items/:item-id',
},
span => {
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.setAttribute(
'http.foobarsessionid',
getFoobarSessionid(response)
);
resolve(response);
});
});
});
}
);
}
Or when using an async function:
function processItem(item) {
return Sentry.startSpan(
{
op: 'http',
name: 'GET /items/:item-id',
},
async span => {
const response = await fetch('/items/${item.id}');
const json = await response.json();
span.setTag('http.status_code', response.statusCode);
span.setAttribute('http.foobarsessionid', getFoobarSessionid(response));
}
);
}
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").