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 tracing.

To instrument certain regions of your code, you can create transactions to capture them.

import Sentry;

// Transaction can be started by providing the name and the operation
let transaction = SentrySDK.startTransaction(
  name: "transaction-name",
  operation: "transaction-operation"

// Transactions can have child spans, and those spans can have child spans as well.
let span = transaction.startChild(operation: "child-operation")

// ...
// Perform your operations
// ...

span.finish(); // Remember that only finished spans will be sent with the transaction
transaction.finish(); // Finishing the transaction will send it to Sentry

For example, if you want to create a transaction for a user interaction in your application:

// Let's say this method is called in a background thread when a user clicks on the checkout button of your shop
func performCheckout()
  // This will create a new Transaction for you
  let transaction = SentrySDK.startTransaction(
      name: "checkout",
      operation: "perform-checkout"

  // Validate the cart
  let validationSpan = transaction.startChild(
      operation: "validation",
      description: "validating shopping cart"

  validateShoppingCart() //Some long process, maybe a sync http request.


  // Process the order
  let processSpan = transaction.startChild(
      operation: "process",
      description: "processing shopping cart"

  processShoppingCart() //Another time consuming process.



This example will send a transaction named checkout to Sentry. The transaction will contain a validation span that measures how long validateShoppingCart() took and a process span that measures processShoppingCart(). Finally, the call to transaction.finish() will finish the transaction and send it to Sentry.

Our SDK can bind a transaction to the scope making it accessible to every method running within this scope by calling SentrySDK#startTransaction method with bindToScope parameter to true.

bindToScope additionally ensures that your new transaction replaces any one that may be already started. This is useful if you want custom instrumentation to co-exist with auto-instrumented transactions.

In cases where you want to attach Spans to an already ongoing Transaction you can use SentrySDK.span. This method will return a SpanProtocol in case there is a running Transaction or a Span in case there is already a running Span, otherwise it returns nil.

import Sentry

let transaction = SentrySDK.startTransaction(
    name: "processOrderBatch",
    operation: "task",
    bindToScope: true

func processOrderBatch() {
    var span = SentrySDK.span

    if span == nil {
        span = SentrySDK.startTransaction(name: "processOrderBatch", operation: "task")

    var innerSpan = span.startChild(operation: "subtask")
    // omitted code

Sentry errors can be linked to transactions and spans. Errors that are sent to Sentry while the transaction or span bound to the scope method is running, will be linked automatically:

import Sentry

let transaction = SentrySDK.startTransaction(name: "Transaction Name", operation: "operation", bindToScope: true)

do {
  try processItem()
} catch {
  SentrySDK.capture(error: error)
  transaction.finish(status: .internalError)
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").