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 instrument certain regions of your code, you can create transactions to capture them.

The following example creates a transaction that contains an expensive operation (for example, processOrderBatch), and sends the result to Sentry:

Copied
import 'package:sentry/sentry.dart';

final transaction = Sentry.startTransaction('processOrderBatch()', 'task');

try {
  processOrderBatch();
} catch (exception) {
  transaction.throwable = exception;
  transaction.status = SpanStatus.internalError();
} finally {
  await transaction.finish();
}

By default, transactions are not bound to the scope. Transaction has to be passed manually as a method parameter to enable attaching nested spans. When creating nested span, you can choose the value of operation and description.

Copied
import 'package:sentry/sentry.dart';

final transaction = Sentry.startTransaction('processOrderBatch()', 'task');

try {
  await processOrderBatch(transaction);
} catch (exception) {
  transaction.throwable = exception;
  transaction.status = SpanStatus.internalError();
} finally {
  await transaction.finish();
}

Future<void> processOrderBatch(ISentrySpan span) async {
  // span operation: task, span description: operation
  final innerSpan = span.startChild('task', description: 'operation');

  try {
    // omitted code
  } catch (exception) {
    innerSpan.throwable = exception;
    innerSpan.status = SpanStatus.notFound();
  } finally {
    await innerSpan.finish();
  }
}

Keep in mind that each individual span also needs to be manually finished;

Spans are sent together with their parent transaction when the transaction is finished. Make sure to call finish() on transaction once all the child spans have finished.

In cases where you want to attach Spans to an already ongoing Transaction you can use Sentry#getSpan. This method will return a SentryTransaction in case there is a running Transaction, otherwise it returns null.

Copied
import 'package:sentry/sentry.dart';

final span = Sentry.getSpan()?.startChild('task') ??
    Sentry.startTransaction('processOrderBatch()', 'task');

try {
  processOrderBatch();
} catch (exception) {
  span.throwable = exception;
  span.status = SpanStatus.internalError();
} finally {
  await span.finish();
}

Sentry errors can be linked with transactions and spans.

Errors reported to Sentry while transaction or span bound to the scope is running are linked automatically:

Copied
import 'package:sentry/sentry.dart';

final transaction = Sentry.startTransaction(
  'processOrderBatch()',
  'task',
  bindToScope: true,
);

try {
  processOrderBatch();
} catch (exception) {
  Sentry.captureException(exception);
} finally {
  await transaction.finish();
}

Exceptions may be thrown within spans that can finish before exception gets reported to Sentry. To attach span information to this exception, you must link it by calling the throwable setter method:

Copied
import 'package:sentry/sentry.dart';

final transaction = Sentry.startTransaction('processOrderBatch()', 'task');

try {
  processOrderBatch();
} catch (exception) {
  transaction.throwable = exception;
  rethrow;
} finally {
  await transaction.finish();
}
Help improve this content
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").