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
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
@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:
// 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();
}
// 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();
}
// 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.
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
)
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()
}
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()
}
@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];
}
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()
transaction.finish()
} catch {
SentrySDK.capture(error: error)
transaction.finish(status: .internalError)
}
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)
}
@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];
}
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:
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")
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")
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"];
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:
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")
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")
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"];
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").