---
title: "Custom Instrumentation"
description: "Learn how to capture performance data on any action in your app."
url: https://docs.sentry.io/platforms/apple/guides/ios/tracing/instrumentation/custom-instrumentation/
---

# Custom Instrumentation | Sentry for iOS

To capture transactions and spans customized to your organization's needs, you must first [set up tracing.](https://docs.sentry.io/platforms/apple/guides/ios/tracing.md)

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

**Swift**

```swift
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
```

**Objective-C**

```objc
@import Sentry;

// Transaction can be started by providing, at minimum, the name and the operation
id<SentrySpan> transaction = [SentrySDK startTransactionWithName:@"transaction-name"
                                                 operation:@"transaction-operation"];

// Transactions can have child spans (and those spans can have child spans as well)
id<SentrySpan> span = [transaction startChildWithOperation:@"child-operation"];

// ...
// (Perform the operation represented by the span/transaction)
// ...

[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:

**Swift**

```swift
// 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.

  validationSpan.finish()

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

  processShoppingCart() //Another time consuming process.

  processSpan.finish();

  transaction.finish();
}
```

**Objective-C**

```objc
// Let's say this method is called in a background thread when a user clicks on the checkout button of your shop
-(void) performCheckout
{
  // This will create a new Transaction for you

  id<SentrySpan> transaction = [SentrySDK startTransactionWithName:@"checkout",
                                                   operation:@"perform-checkout"
  );

  // Validate the cart
  id<SentrySpan> validationSpan = [transaction startChildWithOperation:@"validation",
                                                     description:@"validating shopping cart"];

  [self validateShoppingCart]; //Some long process, maybe a sync http request.

  [validationSpan finish];

  // Process the order
  id<SentrySpan> processSpan = [transaction startChildWithOperation:@"process",
                                                  description:@"processing shopping cart"];

  [self processShoppingCart]; //Another time consuming process.

  [processSpan finish];

  [transaction finish];
}
```

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.

## [Create Transaction Bound to The Current Scope](https://docs.sentry.io/platforms/apple/guides/ios/tracing/instrumentation/custom-instrumentation.md#create-transaction-bound-to-the-current-scope)

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

**Swift**

```swift
import Sentry

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

func processOrderBatch() {
    var span = SentrySDK.span

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

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

**Objective-C**

```objc
@import Sentry;

id<SentrySpan> transaction =
    [SentrySDK startTransactionWithName:@"processOrderBatch"
                              operation:@"task"
                            bindToScope:YES];


[self processOrderBatch];
[transaction finish];

- (void)processOrderBatch {
    id<SentrySpan> span = SentrySDK.span;

    if (span == nil) {
        span = [SentrySDK startTransactionWithName:@"processOrderBatch"
                                         operation:@"task"];
    }

    id<SentrySpan> innerSpan = [span startChildWithOperation:@"subtask"];
    // omitted code
    [innerSpan finish];
}
```

## [Connect Errors with Spans](https://docs.sentry.io/platforms/apple/guides/ios/tracing/instrumentation/custom-instrumentation.md#connect-errors-with-spans)

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:

**Swift**

```swift
import Sentry

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

do {
  try processItem()
  transaction.finish()
} catch {
  SentrySDK.capture(error: error)
  transaction.finish(status: .internalError)
}
```

**Objective-C**

```objc
@import Sentry;

id<SentrySpan> transaction = [SentrySDK startTransactionWithName:@"Transaction Name" operation:@"operation" bindToScope:YES];
NSError * error;

if (processItem(error: &error)) {
  [transaction finish];
} else {
  [SentrySDK captureError:error];
  [transaction finishWithStatus:kSentrySpanStatusInternalError];
}
```

## [Improving Data on Transactions and Spans](https://docs.sentry.io/platforms/apple/guides/ios/tracing/instrumentation/custom-instrumentation.md#improving-data-on-transactions-and-spans)

### [Adding Data Attributes to Transactions](https://docs.sentry.io/platforms/apple/guides/ios/tracing/instrumentation/custom-instrumentation.md#adding-data-attributes-to-transactions)

You can add data attributes to your transactions. This data is visible in the trace explorer in Sentry. Data attributes can be of type `String`, `Number` or `Boolean`, as well as (non-mixed) arrays of these types:

**Swift**

```swift
let transaction = SentrySDK.startTransaction(name: "processOrderBatch", operation: "task")
transaction.setData(value: "value1", key: "my-data-attribute-1")
transaction.setData(value: 42, key: "my-data-attribute-2")
transaction.setData(value: true, key: "my-data-attribute-3")

transaction.setData(value: ["value1", "value2", "value3"], key: "my-data-attribute-4")
transaction.setData(value: [42, 43, 44], key: "my-data-attribute-5")
transaction.setData(value: [true, false, true], key: "my-data-attribute-6")
```

**Objective-C**

```objc
id<SentrySpan> transaction = [SentrySDK startTransactionWithName:@"processOrderBatch" operation:@"task"];
[transaction setDataValue:@"value1" forKey:@"my-data-attribute-1"];
[transaction setDataValue:@(42) forKey:@"my-data-attribute-2"];
[transaction setDataValue:@(YES) forKey:@"my-data-attribute-3"];

[transaction setDataValue:@[@"value1", @"value2", @"value3"] forKey:@"my-data-attribute-4"];
[transaction setDataValue:@[@(42), @(43), @(44)] forKey:@"my-data-attribute-5"];
[transaction setDataValue:@[@(YES), @(NO), @(YES)] forKey:@"my-data-attribute-6"];
```

### [Adding Data Attributes to Spans](https://docs.sentry.io/platforms/apple/guides/ios/tracing/instrumentation/custom-instrumentation.md#adding-data-attributes-to-spans)

You can add data attributes to your spans. This data is visible in the trace explorer in Sentry. Data attributes can be of type `String`, `Number` or `Boolean`, as well as (non-mixed) arrays of these types:

**Swift**

```swift
let span = parent.startChild(operation: "operation")
span.setData(value: "value1", key: "my-data-attribute-1")
span.setData(value: 42, key: "my-data-attribute-2")
span.setData(value: true, key: "my-data-attribute-3")

span.setData(value: ["value1", "value2", "value3"], key: "my-data-attribute-4")
span.setData(value: [42, 43, 44], key: "my-data-attribute-5")
span.setData(value: [true, false, true], key: "my-data-attribute-6")
```

**Objective-C**

```objc
id<SentrySpan> span = [transaction startChildWithOperation:@"task"];
[span setDataValue:@"value1" forKey:@"my-data-attribute-1"];
[span setDataValue:@(42) forKey:@"my-data-attribute-2"];
[span setDataValue:@(YES) forKey:@"my-data-attribute-3"];

[span setDataValue:@[@"value1", @"value2", @"value3"] forKey:@"my-data-attribute-4"];
[span setDataValue:@[@(42), @(43), @(44)] forKey:@"my-data-attribute-5"];
[span setDataValue:@[@(YES), @(NO), @(YES)] forKey:@"my-data-attribute-6"];
```
