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/browser';
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.
You can use the Sentry.startSpan
method to wrap a callback in a span to measure how long it will take. The span will automatically be finished when the callback finishes. This works with both synchronous and async callbacks, but automatically finishes async callbacks based on the promise that is returned from the callback. In cases where you need to manually finish the span (because you don't return anything), you can use Sentry.startSpanManual
, documented below this snippet.
const result = Sentry.startSpan({name: 'Important Function'}, () => {
return expensiveFunction();
});
const result2 = await Sentry.startSpan({name: 'Important Function'}, async () => {
const res = await Sentry.startSpan({name: 'Child Span'}, () => {
return expensiveAsyncFunction();
});
return updateRes(res);
});
const result3 = Sentry.startSpan({name: 'Important Function'}, span => {
// You can access the span to add attributes or set specific status.
// The span may be undefined if the span was not sampled or if performance monitoring is disabled.
span.setAttribute('foo', 'bar');
return expensiveFunction();
});
const result4 = Sentry.startSpan(
{
name: 'Important Function',
// You can also pass attributes directly to `startSpan`:
attributes: {
foo: 'bar',
count: 1,
},
},
() => {
return expensiveFunction();
}
);
In this example, the span named Important Function
will become the active span for the duration of the callback.
If you need to override when the span finishes, you can use Sentry.startSpanManual
. This is useful if you don't want to finish the span when the callback ends, or if you want to finish the span at a specific time.
// Start a span that tracks the duration of middleware
function middleware(_req, res, next) {
return Sentry.startSpanManual({name: 'middleware'}, span => {
res.once('finish', () => {
span.setHttpStatus(res.status);
// manually tell the span when to end
span.end();
});
return next();
});
}
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();
const span2 = Sentry.startInactiveSpan({name: 'span2'});
moreWork();
const span3 = Sentry.startInactiveSpan({name: 'span3'});
evenMoreWork();
span1.end();
span2.end();
span3.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();
})
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").