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.

Copied
// Enable tracing by setting a sample rate above 0
sentry_options_t *options = sentry_options_new();
sentry_options_set_traces_sample_rate(options, 0.2);
// ...
// (Configure tracing unrelated options as you see fit)
// ...
sentry_init(options);

// Transactions can be started by providing the name and the operation
sentry_transaction_context_t *tx_ctx = sentry_transaction_context_new(
    "transaction name",
    "transaction operation"
);
sentry_transaction_t *tx = sentry_transaction_start(tx_ctx, sentry_value_new_null());

// Transactions can have child spans (and those spans can have child spans as well)
sentry_span_t *span = sentry_transaction_start_child(
    tx,
    "child operation",
    "child description"
);

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

sentry_span_finish(span); // Mark the span as finished
sentry_transaction_finish(tx); // Mark the transaction as finished and send it to Sentry

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

Copied
// Let's say this method is called in a background thread when a user clicks on the checkout button.
void perform_checkout() {
    // This will create a new Transaction for you
    sentry_transaction_context_t *tx_ctx = sentry_transaction_context_new(
        "checkout",
        "perform-checkout"
    );
    sentry_transaction_t *tx = sentry_transaction_start(tx_ctx, sentry_value_new_null());

    // Validate the cart
    sentry_span_t *validation_span = sentry_transaction_start_child(
        tx,
        "validation",
        "validating shopping cart"
    );

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

    sentry_span_finish(validation_span);

    // Process the order
    sentry_span_t *process_span = sentry_transaction_start_child(
        tx,
        "process",
        "processing shopping cart"
    );

    process_shopping_cart(); // Another time consuming process.

    sentry_span_finish(process_span);

    sentry_transaction_finish(tx);
}

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

Be careful when spawning operations as independent threads or asynchronous tasks.

APIs provided by the SDK are not inherently thread-safe. Several constructors will contain a warning regarding thread-safety in their docstrings. Functions that operate on the return values of such constructors will also mention any locking requirements.

For example, the documentation of sentry_transaction_context_new(), which constructs a sentry_transaction_context_t, includes a warning in its final paragraph:

Copied
/**
 * Constructs a new Transaction Context. The returned value needs to be passed
 * into `sentry_transaction_start` in order to be recorded and sent to sentry.
 *
 * [...]
 *
 * The returned value is not thread-safe. Users are expected to ensure that
 * appropriate locking mechanisms are implemented over the Transaction Context
 * if it needs to be mutated across threads. Methods operating on the
 * Transaction Context will mention what kind of expectations they carry if they
 * need to mutate or access the object in a thread-safe way.
 */

Following up on that warning, sentry_transaction_context_set_name(), which operates on a sentry_transaction_context_t, notes that it requires a lock:

Copied
/**
 * Sets the `name` on a Transaction Context, which will be used in the
 * Transaction constructed off of the context.
 *
 * The Transaction Context should not be mutated by other functions while
 * setting a name on it.
 */

Sentry errors can be linked with transactions and spans.

Errors reported to Sentry are automatically linked to any running transaction or span that is bound to the scope. There is only one global scope in the native SDK, and only one transaction or span can be bound to the scope at once. In a multi-threaded application, all errors are linked to the single transaction or span bound to the scope.

Copied
sentry_transaction_context_t *tx_ctx = sentry_transaction_context_new(
    "checkout",
    "perform-checkout"
);
sentry_transaction_t *tx = sentry_transaction_start(tx_ctx, sentry_value_new_null());

// Bind the transaction / span to the scope:
sentry_set_span(tx);

// Errors captured after the line above will be linked to the transaction
sentry_value_t exc = sentry_value_new_exception(
    "ParseIntError",
    "invalid digit found in string"
);

sentry_value_t event = sentry_value_new_event();
sentry_event_add_exception(event, exc);
sentry_capture_event(event);

sentry_transaction_finish(tx);

In order to use distributed tracing with the Native SDK, follow the custom instrumentation steps.

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").