Custom Instrumentation for Flutter

Learn how to capture performance data on any action in your app using the Sentry Flutter SDK.

To instrument certain regions of your code, you can create a transaction (or a service span in stream mode) to capture them.

The following example creates a transaction/service span 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();
}

  • Transaction mode: Transactions aren't bound to the scope, so they have to be passed manually as a method parameter to attach nested spans. Keep in mind that each individual span also needs to be manually finished, and spans are sent together with their parent transaction when the transaction is finished.
  • Stream mode: Nested spans attach to the active span automatically — no manual passing needed. When creating a nested span, you can choose its name and sentry.op (see example below). Each span ends automatically when its callback completes, and spans are streamed as they finish.
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();
  }
}

  • Transaction mode: When you want to attach spans to an already ongoing transaction, use Sentry.getSpan(). It returns the running SentryTransaction, or null if there is none.
  • Stream Mode: There's no public API to retrieve the active span. You don't need one: Sentry.startSpan attaches to the currently active span automatically, or starts a new service span if none is active.
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 a transaction or span bound to the scope is running are linked automatically.

  • Transaction mode: Set bindToScope: true.
  • Stream mode: The active span is bound automatically, so no flag is needed.
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 the exception gets reported to Sentry.

  • Transaction mode: Link it by calling the throwable setter.
  • Stream mode: An exception thrown inside the callback is recorded on the span and rethrown automatically.
Copied
import 'package:sentry/sentry.dart';

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

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

You can add data attributes to your transactions and spans. This data is visible in the trace explorer in Sentry.

  • Transaction mode: Use setData with String, int, double or bool values, as well as (non-mixed) arrays of these types.
  • Stream mode: Use setAttribute or setAttributes with a typed SentryAttribute factory.

Copied
final transaction = Sentry.startTransaction('my-transaction', 'http.server');
transaction.setData('data_attribute_1', 'value1');
transaction.setData('data_attribute_2', 42);
transaction.setData('data_attribute_3', true);

transaction.setData('data_attribute_4', ['value1', 'value2']);
transaction.setData('data_attribute_5', [42, 43]);
transaction.setData('data_attribute_6', [true, false]);

Copied
final span = parent.startChild('http.client');
span.setData('data_attribute_1', 'value1');
span.setData('data_attribute_2', 42);
span.setData('data_attribute_3', true);

span.setData('data_attribute_4', ['value1', 'value2']);
span.setData('data_attribute_5', [42, 43]);
span.setData('data_attribute_6', [true, false]);
Was this helpful?
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").